From dda3d092ba9f071f567d279aed369efe185ffbde Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Thu, 7 Oct 2021 16:08:30 -0400
Subject: fix presentation play bounds to fit trim bounds

---
 src/client/views/collections/TabDocView.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 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 eb95bb913..9f24d60d8 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -220,8 +220,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = "manual";
                 pinDoc.mediaStop = "manual";
-                pinDoc.presStartTime = 0;
-                pinDoc.presEndTime = duration;
+                pinDoc.presStartTime = doc.clipStart;
+                pinDoc.presEndTime = doc.clipEnd;
             }
             //save position
             if (pinProps?.setPosition || pinDoc.isInkMask) {
-- 
cgit v1.2.3-70-g09d2


From 1f94d6b33f81b1b2e6140f58f4de749eb4e74478 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 8 Nov 2021 11:07:05 -0500
Subject: fixed vertical height of timeline for video.  fixed initial display
 of timeline by not  assigning clipEnd in DidMount

---
 .../collections/CollectionStackedTimeline.scss     |  2 +-
 .../collections/CollectionStackedTimeline.tsx      | 33 ++++++++++++----------
 src/client/views/collections/TabDocView.tsx        |  4 +--
 src/client/views/nodes/AudioBox.scss               |  5 ++--
 src/client/views/nodes/AudioBox.tsx                |  7 +++--
 src/client/views/nodes/VideoBox.tsx                |  9 +++---
 6 files changed, 33 insertions(+), 27 deletions(-)

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

diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index fce105a44..36aa36978 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -1,7 +1,7 @@
 @import "../global/globalCssVariables.scss";
 
 .timeline-container {
-    height: calc(100% - 50px);
+    height: 100%;
     overflow-x: auto;
     overflow-y: hidden;
     border: none;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index ced8a68e8..f5c3676e8 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -127,8 +127,11 @@ export class CollectionStackedTimeline extends CollectionSubView<
     }
 
     componentDidMount() {
-        this.layoutDoc.clipStart = 0;
-        this.layoutDoc.clipEnd = this.props.rawDuration;
+        // bcz: setting these shouldn't be necessary since they are the default values of this.clipStart and this.clipEnd.
+        //      also, setting anything on the Document in DidMount or the constructor is not good form since it means that
+        //      someone who has viewing but not edit permissions would not be able to do the assignment.
+        // this.layoutDoc.clipStart = 0;
+        // this.layoutDoc.clipEnd = this.props.rawDuration;
         document.addEventListener("keydown", this.keyEvents, true);
     }
 
@@ -915,19 +918,19 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                 {inner.view}
                 {!inner.anchor.view ||
                     !SelectionManager.IsSelected(inner.anchor.view) ? null : (
-                    <>
-                        <div
-                            key="left"
-                            className="collectionStackedTimeline-left-resizer"
-                            onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
-                        />
-                        <div
-                            key="right"
-                            className="collectionStackedTimeline-resizer"
-                            onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, false)}
-                        />
-                    </>
-                )}
+                        <>
+                            <div
+                                key="left"
+                                className="collectionStackedTimeline-left-resizer"
+                                onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
+                            />
+                            <div
+                                key="right"
+                                className="collectionStackedTimeline-resizer"
+                                onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, false)}
+                            />
+                        </>
+                    )}
             </>
         );
     }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 9f24d60d8..6e1d9b067 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -220,8 +220,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = "manual";
                 pinDoc.mediaStop = "manual";
-                pinDoc.presStartTime = doc.clipStart;
-                pinDoc.presEndTime = doc.clipEnd;
+                pinDoc.presStartTime = NumCast(doc.clipStart);
+                pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
             }
             //save position
             if (pinProps?.setPosition || pinDoc.isInkMask) {
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index 458d607a5..681a6b022 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -180,13 +180,14 @@
 
     .audiobox-playback {
         width: 100%;
-        height: calc(100% - 50px);
+        height: 100%;
         background: $white;
 
         .audiobox-timeline {
-            height: 100%;
+            height: calc(100% - 50px);
             width: 100%;
             background: $white;
+            position: absolute;
         }
 
         .audiobox-timeline > div {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index b51908e20..62958a80b 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -64,8 +64,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @observable _volume: number = 1;
     @observable _muted: boolean = false;
     @observable _paused: boolean = false;
+    @observable rawDuration: number = 0; // computed from the length of the audio element when loaded
     @computed get recordingStart() { return DateCast(this.dataDoc[this.fieldKey + "-recordingStart"])?.date.getTime(); }
-    @computed get rawDuration() { return NumCast(this.dataDoc[`${this.fieldKey}-duration`]); }
+    // @computed get rawDuration() { return NumCast(this.dataDoc[`${this.fieldKey}-duration`]); } // bcz: shouldn't be needed since it's computed from audio element
     @computed get links() { return DocListCast(this.dataDoc.links); }
     @computed get pauseTime() { return this._pauseEnd - this._pauseStart; } // total time paused to update the correct time
     @computed get mediaState() { return this.layoutDoc.mediaState as media_state; }
@@ -490,7 +491,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     />
                 </div>
                 <div className="timecode-duration">
-                    {this.timeline && formatTime(Math.round(NumCast(this.timeline?.clipDuration)))}
+                    {this.timeline && formatTime(Math.round(this.timeline.clipDuration))}
                 </div>
             </div>
 
@@ -537,7 +538,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}
             onLoadedData={action(e =>
                 (this._ele?.duration && this._ele?.duration !== Infinity) &&
-                (this.dataDoc[this.fieldKey + "-duration"] = this._ele?.duration)
+                (this.dataDoc[this.fieldKey + "-duration"] = this.rawDuration = this._ele.duration)
             )}
         >
             <source src={this.path} type="audio/mpeg" />
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index c22dbab5c..ec6519abd 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -79,7 +79,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @computed get links() { return DocListCast(this.dataDoc.links); }
     @computed get heightPercent() { return NumCast(this.layoutDoc._timelineHeightPercent, 100); }
-    @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
+    // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
+    @observable rawDuration: number = 0;
 
     @computed get youtubeVideoId() {
         const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
@@ -234,15 +235,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
     }
 
-    videoLoad = () => {
+    videoLoad = action(() => {
         const aspect = this.player!.videoWidth / this.player!.videoHeight;
         Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
         Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
         this.layoutDoc._height = (this.layoutDoc._width || 0) / aspect;
         if (Number.isFinite(this.player!.duration)) {
-            this.dataDoc[this.fieldKey + "-duration"] = this.player!.duration;
+            this.rawDuration = this.player!.duration;
         }
-    }
+    })
 
     @action
     updateTimecode = () => {
-- 
cgit v1.2.3-70-g09d2


From 3573a81d334cf8cc13d19b04c6f2094c24c43932 Mon Sep 17 00:00:00 2001
From: Aubrey Li <Aubrey-Li>
Date: Thu, 3 Mar 2022 18:12:13 -0500
Subject: edit: lightbox opens tab in the same stack

---
 src/client/views/LightboxView.tsx                      | 8 ++++++--
 src/client/views/collections/CollectionDockingView.tsx | 2 +-
 src/client/views/collections/TabDocView.tsx            | 2 +-
 src/client/views/nodes/DocumentView.tsx                | 2 +-
 src/client/views/nodes/trails/PresBox.tsx              | 9 +++++++--
 5 files changed, 16 insertions(+), 7 deletions(-)

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

diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index ec3bf6c18..e9f63fb5f 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -38,6 +38,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     private static _history: Opt<{ doc: Doc, target?: Doc }[]> = [];
     private static _future: Opt<Doc[]> = [];
     private static _docView: Opt<DocumentView>;
+    private static openInTabFunc: any;
     static path: { doc: Opt<Doc>, target: Opt<Doc>, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt<Doc[]>, saved: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number>, scrollTop: Opt<number> }> }[] = [];
     @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc) {
         if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
@@ -106,7 +107,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             this._docFilters = (f => this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f])(`cookies:${cookie}:provide`);
         }
     }
-    public static AddDocTab = (doc: Doc, location: string, layoutTemplate?: Doc) => {
+    public static AddDocTab = (doc: Doc, location: string, layoutTemplate?: Doc, openInTabFunc?: any) => {
+        LightboxView.openInTabFunc = openInTabFunc;
         SelectionManager.DeselectAll();
         return LightboxView.SetLightboxDoc(doc, undefined,
             [...DocListCast(doc[Doc.LayoutFieldKey(doc)]),
@@ -275,7 +277,9 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 <div className="lightboxView-tabBtn" title={"open in tab"}
                     onClick={e => {
                         e.stopPropagation();
-                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "onRight");
+                        // CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "");
+                        LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace");
+                        console.log("lightbox to tab triggered")
                         SelectionManager.DeselectAll();
                         LightboxView.SetLightboxDoc(undefined);
                     }}>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f543d924d..79e036a0e 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -449,7 +449,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                     alert('cant delete the last stack');
                 }
             }));
-        stack.header?.controlsContainer.find('.lm_popout') //get the close icon
+        stack.header?.controlsContainer.find('.lm_popout') //get the popout icon
             .off('click') //unbind the current click handler
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 7e57d0e89..c11b6c7b3 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -300,7 +300,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
             case "lightbox": {
                 // TabDocView.PinDoc(doc, { hidePresBox: true });
-                return LightboxView.AddDocTab(doc, location);
+                return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab);
             }
             case "inPlace":
             case "add":
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9fc4b7890..d78f55213 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -475,7 +475,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     UndoManager.RunInBatch(() => func().result?.select === true ? this.props.select(false) : "", "on double click");
                 } else if (!Doc.IsSystem(this.rootDoc)) {
                     UndoManager.RunInBatch(() =>
-                        LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.())
+                        LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.(), this.props.addDocTab)
                         , "double tap");
                     SelectionManager.DeselectAll();
                     Doc.UnBrushDoc(this.props.Document);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 14d6e8be6..48223a5f0 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -391,7 +391,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             self._eleArray.splice(0, self._eleArray.length, ...eleViewCache);
         });
         const openInTab = (doc: Doc, finished?: () => void) => {
-            collectionDocView ? collectionDocView.props.addDocTab(doc, "") : this.props.addDocTab(doc, ":left");
+            collectionDocView ? collectionDocView.props.addDocTab(doc, "") : this.props.addDocTab(doc, "");
             this.layoutDoc.presCollection = targetDoc;
             // this still needs some fixing
             setTimeout(resetSelection, 500);
@@ -403,7 +403,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         };
         // If openDocument is selected then it should open the document for the user
         if (activeItem.openDocument) {
-            LightboxView.SetLightboxDoc(targetDoc);
+            // LightboxView.SetLightboxDoc(targetDoc);
+            // activeItem: "slide in the sidebar" --> maps to target document (targetDoc) in the canvas
+            console.log(activeItem);
+            console.log("openDocument triggered");
+
+            openInTab(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
             await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
-- 
cgit v1.2.3-70-g09d2


From d5d472521229f006f4a95d5c019276ec0281ac57 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 31 Mar 2022 14:56:03 -0400
Subject: cleaned up addDocTab for properties views to allow toggling of
 targets

---
 src/client/views/DocumentDecorations.tsx            | 16 +++++++++-------
 src/client/views/MainView.tsx                       | 20 ++++++++++++++++----
 src/client/views/PropertiesDocBacklinksSelector.tsx |  4 +---
 src/client/views/PropertiesDocContextSelector.tsx   | 11 +++++++----
 src/client/views/PropertiesView.tsx                 |  7 ++++---
 src/client/views/collections/TabDocView.tsx         |  6 ++----
 6 files changed, 39 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 328b8ab88..8cd646935 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -83,16 +83,18 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         } else if (this._titleControlString.startsWith("#")) {
             const titleFieldKey = this._titleControlString.substring(1);
             UndoManager.RunInBatch(() => titleFieldKey && SelectionManager.Views().forEach(d => {
-                titleFieldKey === "title" && (d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-"));
+                if (titleFieldKey === "title") {
+                    d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
+                    if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
+                        Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                    }
+                    if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
+                        Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                    }
+                }
                 //@ts-ignore
                 const titleField = (+this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle);
-                if (titleFieldKey === "title" && StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
-                    Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", SelectionManager.Docs().lastElement());
-                }
                 Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
-                if (titleFieldKey === "title" && this._accumulatedTitle.startsWith("@")) {
-                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", SelectionManager.Docs().lastElement());
-                }
 
                 if (d.rootDoc.syncLayoutFieldWithTitle) {
                     const title = titleField.toString();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7cb40f701..5e68832fc 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -329,9 +329,21 @@ export class MainView extends React.Component {
 
     sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
-    addDocTabFunc = (doc: Doc, where: string): boolean => {
-        return where === "close" ? CollectionDockingView.CloseSplit(doc) :
-            doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : CollectionDockingView.AddSplit(doc, "right");
+    addDocTabFunc = (doc: Doc, location: string): boolean => {
+        const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
+        const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        switch (locationFields[0]) {
+            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
+            case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
+            case "lightbox": return LightboxView.AddDocTab(doc, location);
+            case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams);
+            case "inPlace":
+            case "add":
+            default:
+                return CollectionDockingView.AddSplit(doc, locationParams);
+        }
     }
 
 
@@ -440,7 +452,7 @@ export class MainView extends React.Component {
                         <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
                     </div>
                     <div className="properties-container" style={{ width: this.propertiesWidth() }}>
-                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
                     </div>
                 </div>
             </div>
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index 5ffa93961..ea0d90e04 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -5,8 +5,6 @@ import { Doc, DocListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { LinkManager } from "../util/LinkManager";
-import { CollectionDockingView } from "./collections/CollectionDockingView";
-import { CollectionViewType } from "./collections/CollectionView";
 import './PropertiesDocContextSelector.scss';
 
 type PropertiesDocBacklinksSelectorProps = {
@@ -34,7 +32,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
 
     getOnClick = (col: Doc) => {
         col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col;
-        this.props.addDocTab(col, "add:right");
+        this.props.addDocTab(col, "toggle:right");
     }
 
     render() {
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 45a8b6bf6..4d803f992 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -6,10 +6,11 @@ import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { CollectionDockingView } from "./collections/CollectionDockingView";
 import { CollectionViewType } from "./collections/CollectionView";
+import { DocumentView } from "./nodes/DocumentView";
 import './PropertiesDocContextSelector.scss';
 
 type PropertiesDocContextSelectorProps = {
-    Document: Doc,
+    DocView?: DocumentView,
     Stack?: any,
     hideTitle?: boolean,
     addDocTab(doc: Doc, location: string): void
@@ -18,9 +19,11 @@ type PropertiesDocContextSelectorProps = {
 @observer
 export class PropertiesDocContextSelector extends React.Component<PropertiesDocContextSelectorProps> {
     @computed get _docs() {
-        const target = this.props.Document;
+        if (!this.props.DocView) return [];
+        const target = this.props.DocView.props.Document;
+        const targetContext = this.props.DocView.props.ContainingCollectionDoc;
         const aliases = DocListCast(target.aliases);
-        const containerProtos = aliases.filter(alias => alias.context).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
+        const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc && Cast(alias.context, Doc, null) !== targetContext).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases));
         const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
         const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.aliases));
@@ -34,7 +37,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
             col._panX = NumCast(target.x) + NumCast(target._width) / 2;
             col._panY = NumCast(target.y) + NumCast(target._height) / 2;
         }
-        this.props.addDocTab(col, "add:right");
+        this.props.addDocTab(col, "toggle:right");
     }
 
     render() {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8e1e9ea15..91cf83619 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -44,6 +44,7 @@ interface PropertiesViewProps {
     width: number;
     height: number;
     styleProvider?: StyleProviderFunc;
+    addDocTab: (doc: Doc, where: string) => boolean;
 }
 
 @observer
@@ -279,11 +280,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get contexts() {
-        return !this.selectedDoc ? (null) : <PropertiesDocContextSelector Document={this.selectedDoc} hideTitle={true} addDocTab={(doc, where) => CollectionDockingView.AddSplit(doc, "right")} />;
+        return !this.selectedDoc ? (null) : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
     @computed get links() {
-        return !this.selectedDoc ? (null) : <PropertiesDocBacklinksSelector Document={this.selectedDoc} hideTitle={true} addDocTab={(doc, where) => CollectionDockingView.AddSplit(doc, "right")} />;
+        return !this.selectedDoc ? (null) : <PropertiesDocBacklinksSelector Document={this.selectedDoc} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
     @computed get layoutPreview() {
@@ -1077,7 +1078,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             <div className="propertiesView-contexts-title"
                 onPointerDown={action(() => this.openContexts = !this.openContexts)}
                 style={{ backgroundColor: this.openContexts ? "black" : "" }}>
-                Contexts
+                Other Contexts
                 <div className="propertiesView-contexts-title-icon">
                     <FontAwesomeIcon icon={this.openContexts ? "caret-down" : "caret-right"} size="lg" color="white" />
                 </div>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index d52746d11..8b5022593 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -298,10 +298,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
-            case "lightbox": {
-                // TabDocView.PinDoc(doc, { hidePresBox: true });
-                return LightboxView.AddDocTab(doc, location);
-            }
+            case "lightbox": return LightboxView.AddDocTab(doc, location);
+            case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
             case "inPlace":
             case "add":
             default:
-- 
cgit v1.2.3-70-g09d2


From 51d7ce5f71465f2f578a08a998b2df353242ff4d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 5 Apr 2022 13:54:26 -0400
Subject: fixes to allow dragging golden layout windows to be aborted with
 'esc'.  some code cleanup as well to avoid rebuilding golden layout when tabs
 are closed.  Fixes for undo and goldenlayout

---
 src/client/goldenLayout.js                         |  32 ++++--
 src/client/util/DragManager.ts                     |  25 ++---
 src/client/views/DocumentDecorations.tsx           |   7 +-
 src/client/views/GlobalKeyHandler.ts               |   1 +
 .../views/collections/CollectionDockingView.tsx    | 109 +++++++++++++--------
 src/client/views/collections/TabDocView.tsx        |   3 +-
 6 files changed, 108 insertions(+), 69 deletions(-)

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

diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 896237e1d..295875ef6 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -366,6 +366,7 @@
         this._nOriginalY = 0;
 
         this._bDragging = false;
+        this._bAborting = false;
 
         this._fMove = lm.utils.fnBind(this.onMouseMove, this);
         this._fUp = lm.utils.fnBind(this.onMouseUp, this);
@@ -427,6 +428,24 @@
             }
         },
 
+        AbortDrag: function () {
+            if (this._timeout != null) {
+                clearTimeout(this._timeout);
+                this._eBody.removeClass('lm_dragging');
+                this._eElement.removeClass('lm_dragging');
+                this._oDocument.find('iframe').css('pointer-events', '');
+                this._oDocument.unbind('mousemove touchmove', this._fMove);
+                this._oDocument.unbind('mouseup touchend', this._fUp);
+
+                if (this._bDragging === true) {
+                    this._bDragging = false;
+                    this._bAborting = true;
+                    this.emit('dragStop', { pageX: 0, pageY: 0 }, this._nOriginalX + this._nX);
+                    this._bAborting = false;
+                }
+            }
+        },
+
         onMouseUp: function (oEvent) {
             if (this._timeout != null) {
                 clearTimeout(this._timeout);
@@ -2178,18 +2197,18 @@
          */
         _onDrop: function () {
             this._layoutManager.dropTargetIndicator.hide();
-
+            let abortedDrop = this._dragListener._bAborting;
             /*
              * Valid drop area found
              */
-            if (this._area !== null) {
+            if (!abortedDrop && this._area !== null) {
                 this._area.contentItem._$onDrop(this._contentItem, this._area);
 
                 /**
                  * No valid drop area available at present, but one has been found before.
                  * Use it
                  */
-            } else if (this._lastValidArea !== null) {
+            } else if (!abortedDrop && this._lastValidArea !== null) {
                 this._lastValidArea.contentItem._$onDrop(this._contentItem, this._lastValidArea);
 
                 /**
@@ -2197,7 +2216,7 @@
                  * content item to its original position if a original parent is provided.
                  * (Which is not the case if the drag had been initiated by createDragSource)
                  */
-            } else if (this._originalParent) {
+            } else if (!abortedDrop && this._originalParent) {
                 this._originalParent.addChild(this._contentItem);
 
                 /**
@@ -2212,7 +2231,7 @@
             restoreScrollTops(this._contentItem.element)
             this.element.remove();
 
-            this._layoutManager.emit('itemDropped', this._contentItem);
+            !abortedDrop && this._layoutManager.emit('itemDropped', this._contentItem);
         },
 
         /**
@@ -2963,7 +2982,7 @@
             if (this.contentItem.parent.isMaximised === true) {
                 this.contentItem.parent.toggleMaximise();
             }
-            new lm.controls.DragProxy(
+            let proxy = new lm.controls.DragProxy(
                 x,
                 y,
                 this._dragListener,
@@ -2971,6 +2990,7 @@
                 this.contentItem,
                 this.header.parent
             );
+            this._layoutManager.emit('dragStart', proxy);
         },
 
         /**
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0d6a77f71..411fc6d11 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,3 +1,4 @@
+import { extend } from "lodash";
 import { action } from "mobx";
 import { DateField } from "../../fields/DateField";
 import { Doc, Field, Opt } from "../../fields/Doc";
@@ -56,12 +57,7 @@ export function SetupDrag(
             if (e.shiftKey) {
                 e.persist();
                 const dragDoc = await docFunc();
-                dragDoc && DragManager.StartWindowDrag?.({
-                    pageX: e.pageX,
-                    pageY: e.pageY,
-                    preventDefault: emptyFunction,
-                    button: 0
-                }, [dragDoc]);
+                dragDoc && DragManager.StartWindowDrag?.(e, [dragDoc]);
             } else {
                 document.addEventListener("pointermove", onRowMove);
                 document.addEventListener("pointerup", onRowUp);
@@ -74,7 +70,8 @@ export function SetupDrag(
 export namespace DragManager {
     let dragDiv: HTMLDivElement;
     let dragLabel: HTMLDivElement;
-    export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
+    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 Root() {
         const root = document.getElementById("root");
@@ -460,17 +457,13 @@ export namespace DragManager {
             }
             if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
                 dragData.dropAction = dragData.userDropAction || "same";
-                if (dragData.dropAction === "move" || dragData.dropAction === "same") {
-                    dragData.removeDocument?.(dragData.draggedDocuments[0]);
-                }
                 AbortDrag();
                 await finishDrag?.(new DragCompleteEvent(true, dragData));
-                DragManager.StartWindowDrag?.({
-                    pageX: e.pageX,
-                    pageY: e.pageY,
-                    preventDefault: emptyFunction,
-                    button: 0
-                }, dragData.droppedDocuments);
+                DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => {
+                    if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) {
+                        dragData.removeDocument?.(dragData.draggedDocuments[0]);
+                    }
+                });
             }
 
             const target = document.elementFromPoint(e.x, e.y);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 8cd646935..d7ead713a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -151,12 +151,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
     }
     onMaximizeDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, () => {
-            DragManager.StartWindowDrag?.({
-                pageX: e.pageX,
-                pageY: e.pageY,
-                preventDefault: emptyFunction,
-                button: 0
-            }, [SelectionManager.Views().slice(-1)[0].rootDoc]);
+            DragManager.StartWindowDrag?.(e, [SelectionManager.Views().slice(-1)[0].rootDoc]);
             return true;
         }, emptyFunction, this.onMaximizeClick, false, false);
     }
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 1a4080d81..4a327a842 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -114,6 +114,7 @@ export class KeyManager {
                 DocumentLinksButton.StartLinkView = undefined;
                 InkStrokeProperties.Instance._controlButton = false;
                 CurrentUserUtils.SelectedTool = InkTool.None;
+                DragManager.CompleteWindowDrag?.(true);
                 var doDeselect = true;
                 if (SnappingManager.GetIsDragging()) {
                     DragManager.AbortDrag();
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 9e8374605..6931d9896 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -11,6 +11,7 @@ import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { inheritParentAcls } from '../../../fields/util';
+import { emptyFunction } from '../../../Utils';
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -46,7 +47,7 @@ export class CollectionDockingView extends CollectionSubView() {
     private _reactionDisposer?: IReactionDisposer;
     private _lightboxReactionDisposer?: IReactionDisposer;
     private _containerRef = React.createRef<HTMLDivElement>();
-    private _flush: UndoManager.Batch | undefined;
+    public _flush: UndoManager.Batch | undefined;
     private _ignoreStateChange = "";
     public tabMap: Set<any> = new Set();
     public get initialized() { return this._goldenLayout !== null; }
@@ -62,15 +63,37 @@ export class CollectionDockingView extends CollectionSubView() {
         DragManager.StartWindowDrag = this.StartOtherDrag;
     }
 
-    public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
-        !this._flush && (this._flush = UndoManager.StartBatch("golden layout drag"));
+    /**
+     * Switches from dragging a document around a freeform canvas to dragging it as a tab to be docked.
+     * 
+     * @param e fake mouse down event position data containing pageX and pageY coordinates
+     * @param dragDocs the documents to be dragged
+     * @param batch optionally an undo batch that has been started to use instead of starting a new batch
+    */
+    public StartOtherDrag = (e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => {
+        this._flush = this._flush ?? UndoManager.StartBatch("golden layout drag");
         const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) :
-            { type: 'row', content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc)) };
+            { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) };
         const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config);
-        //dragSource._dragListener.on("dragStop", dragSource.destroy);
-        dragSource._dragListener.onMouseDown(e);
+        this.tabDragStart(dragSource, finishDrag);
+        dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 });
     }
 
+    tabItemDropped = () => DragManager.CompleteWindowDrag?.(false);
+    tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => {
+        DragManager.CompleteWindowDrag = (aborted: boolean) => {
+            if (aborted) {
+                proxy._dragListener.AbortDrag();
+                if (this._flush) {
+                    this._flush.cancel();  // cancel the undo change being logged
+                    this._flush = undefined;
+                    this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout)
+                }
+                DragManager.CompleteWindowDrag = undefined;
+            }
+            finishDrag?.(aborted);
+        }
+    }
     @undoBatch
     public CloseFullScreen = () => {
         this._goldenLayout._maximisedItem?.toggleMaximise();
@@ -106,7 +129,6 @@ export class CollectionDockingView extends CollectionSubView() {
         docconfig.callDownwards('_$init');
         instance._goldenLayout._$maximiseItem(docconfig);
         instance._goldenLayout.emit('stateChanged');
-        instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig());
         instance.stateChanged();
         return true;
     }
@@ -255,7 +277,6 @@ export class CollectionDockingView extends CollectionSubView() {
     layoutChanged() {
         this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
         this._goldenLayout.emit('stateChanged');
-        this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
         this.stateChanged();
         return true;
     }
@@ -294,6 +315,9 @@ export class CollectionDockingView extends CollectionSubView() {
                 }
             }
             this._goldenLayout.init();
+            this._goldenLayout.root.layoutManager.on('itemDropped', this.tabItemDropped);
+            this._goldenLayout.root.layoutManager.on('dragStart', this.tabDragStart)
+            this._goldenLayout.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
         }
     }
 
@@ -335,13 +359,12 @@ export class CollectionDockingView extends CollectionSubView() {
     @action
     onPointerUp = (e: MouseEvent): void => {
         window.removeEventListener("pointerup", this.onPointerUp);
-        if (this._flush) {
-            setTimeout(() => {
-                CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
-                this.stateChanged();
-                this._flush?.end();
-                this._flush = undefined;
-            }, 10);
+        const flush = this._flush;
+        this._flush = undefined;
+        if (flush) {
+            DragManager.CompleteWindowDrag = undefined;
+            if (!this.stateChanged()) flush.cancel();
+            else flush.end();
         }
     }
 
@@ -352,9 +375,12 @@ export class CollectionDockingView extends CollectionSubView() {
             hitFlyout = (par.className === "dockingViewButtonSelector");
         }
         if (!hitFlyout) {
+            const htmlTarget = e.target as HTMLElement;
             window.addEventListener("mouseup", this.onPointerUp);
-            if (!(e.target as HTMLElement).closest("*.lm_content") && ((e.target as HTMLElement).closest("*.lm_tab") || (e.target as HTMLElement).closest("*.lm_stack"))) {
-                this._flush = UndoManager.StartBatch("golden layout edit");
+            if (!htmlTarget.closest("*.lm_content") && (htmlTarget.closest("*.lm_tab") || htmlTarget.closest("*.lm_stack"))) {
+                if (htmlTarget.className !== "lm_close_tab") {
+                    this._flush = UndoManager.StartBatch("golden layout edit");
+                }
             }
         }
         if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
@@ -388,38 +414,43 @@ export class CollectionDockingView extends CollectionSubView() {
 
     @action
     stateChanged = () => {
+        this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
         const json = JSON.stringify(this._goldenLayout.toConfig());
         const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
         const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
         const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
-
-        this.props.Document.dockingConfig = json;
-        setTimeout(async () => {
-            const sublists = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
-            const tabs = sublists && Cast(sublists[0], Doc, null);
-            // const other = sublists && Cast(sublists[1], Doc, null);
-            const tabdocs = await DocListCastAsync(tabs?.data);
-            // const otherdocs = await DocListCastAsync(other?.data);
-            if (tabs) {
-                tabs.data = new List<Doc>(docs);
-                // DocListCast(tabs.aliases).forEach(tab => tab !== tabs && (tab.data = new List<Doc>(docs)));
-            }
-            // const otherSet = new Set<Doc>();
-            // otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
-            // tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc));
-            // const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d).filter(d => d.type !== DocumentType.KVP);
-            // this.props.Document[DataSym][this.props.fieldKey + "-all"] = new List<Doc>([...docs, ...vals]);
-            // if (other) {
-            //     other.data = new List<Doc>(vals);
-            //     // DocListCast(other.aliases).forEach(tab => tab !== other && (tab.data = new List<Doc>(vals)));
-            // }
-        }, 0);
+        const changesMade = this.props.Document.dockingConfig !== json;
+        if (changesMade && !this._flush) {
+            this.props.Document.dockingConfig = json;
+            setTimeout(async () => {
+                const sublists = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
+                const tabs = sublists && Cast(sublists[0], Doc, null);
+                // const other = sublists && Cast(sublists[1], Doc, null);
+                const tabdocs = await DocListCastAsync(tabs?.data);
+                // const otherdocs = await DocListCastAsync(other?.data);
+                if (tabs) {
+                    tabs.data = new List<Doc>(docs);
+                    // DocListCast(tabs.aliases).forEach(tab => tab !== tabs && (tab.data = new List<Doc>(docs)));
+                }
+                // const otherSet = new Set<Doc>();
+                // otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
+                // tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc));
+                // const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d).filter(d => d.type !== DocumentType.KVP);
+                // this.props.Document[DataSym][this.props.fieldKey + "-all"] = new List<Doc>([...docs, ...vals]);
+                // if (other) {
+                //     other.data = new List<Doc>(vals);
+                //     // DocListCast(other.aliases).forEach(tab => tab !== other && (tab.data = new List<Doc>(vals)));
+                // }
+            }, 0);
+        }
+        return changesMade;
     }
 
     tabDestroyed = (tab: any) => {
         this.tabMap.delete(tab);
         tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
         tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
+        this.stateChanged();
     }
     tabCreated = (tab: any) => {
         tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab);  // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 8b5022593..ad5c0efb3 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -190,7 +190,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
                     Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true);
                     SelectionManager.DeselectAll();
-                    tab.contentItem.remove();
+                    UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
         }
     }
@@ -277,7 +277,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     private onActiveContentItemChanged(contentItem: any) {
         if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) {
             this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab;
-            (CollectionDockingView.Instance as any)._goldenLayout?.isInitialised && CollectionDockingView.Instance.stateChanged();
             !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 8f6a065c192c091393e654bdac682c285a63ad8f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 5 Apr 2022 14:45:06 -0400
Subject: from last

---
 src/client/views/collections/TabDocView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(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 ad5c0efb3..bd3e810c9 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -179,7 +179,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
             // highlight the tab when the tab document is brushed in any part of the UI
             tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
-                titleEle.value = title;
+                //titleEle.value = title;
                 // titleEle.style.padding = degree ? 0 : 2;
                 // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
             }, { fireImmediately: true });
-- 
cgit v1.2.3-70-g09d2


From 18d21c0616adbdc7e0d0619c3c2d8bdf9b34cc01 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 22 Apr 2022 21:39:08 -0400
Subject: changes to allow PresBox to render using a Tree view

---
 src/client/documents/Documents.ts                 |  7 +--
 src/client/views/collections/TabDocView.tsx       |  9 ++++
 src/client/views/collections/TreeView.tsx         | 54 ++++++++++++-----------
 src/client/views/nodes/DocumentView.tsx           |  3 +-
 src/client/views/nodes/trails/PresBox.tsx         | 13 +++---
 src/client/views/nodes/trails/PresElementBox.scss |  2 +-
 src/client/views/nodes/trails/PresElementBox.tsx  |  4 +-
 7 files changed, 54 insertions(+), 38 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 69ea21541..49e53a214 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -284,6 +284,7 @@ export class DocumentOptions {
     treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
     treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
+    treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
 
     // Action Button
     buttonMenu?: boolean; // whether a action button should be displayed 
@@ -365,7 +366,7 @@ export namespace Docs {
             [DocumentType.RTF, {
                 layout: { view: FormattedTextBox, dataField: "text" },
                 options: {
-                    _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true,
+                    _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true,
                     links: "@links(self)"
                 }
             }],
@@ -460,8 +461,8 @@ export namespace Docs {
                 options: { links: "@links(self)" }
             }],
             [DocumentType.SLIDER, {
-                layout: { view: SliderBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
+                layout: { view: SliderBox, dataField: defaultDataKey, },
+                options: { links: "@links(self)", treeViewGrowsHorizontally: true }
             }],
             [DocumentType.PRES, {
                 layout: { view: PresBox, dataField: defaultDataKey },
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index bd3e810c9..136486f47 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -33,6 +33,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
+import { List } from '../../../fields/List';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -209,9 +210,17 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const pinDoc = Doc.MakeAlias(doc);
             pinDoc.presentationTargetDoc = doc;
             pinDoc.title = doc.title + " - Slide";
+            pinDoc.data = new List<Doc>();  // the children of the alias' layout are the presentation slide children.  the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
             pinDoc.presMovement = PresMovement.Zoom;
             pinDoc.groupWithUp = false;
             pinDoc.context = curPres;
+            // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
+            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
+            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
+            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
+            pinDoc.treeViewGrowsHorizontally = true;// the document can expand horizontally when displayed as a tree view header  
+            pinDoc.treeViewHideHeader = 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 presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index c2d0983da..a35304787 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -112,7 +112,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); }
     @computed get dataDoc() { return this.doc[DataSym]; }
     @computed get layoutDoc() { return Doc.Layout(this.doc); }
-    @computed get fieldKey() { return Doc.LayoutFieldKey(this.doc); }
+    @computed get fieldKey() { return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc)); }
     @computed get childDocs() { return this.childDocList(this.fieldKey); }
     @computed get childLinks() { return this.childDocList("links"); }
     @computed get childAliases() { return this.childDocList("aliases"); }
@@ -156,18 +156,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             docView.select(false);
         }
     }
-    @action
-    openLevel = (docView: DocumentView) => {
-        if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
-            this.treeViewOpen = !this.treeViewOpen;
-        } else {
-            // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
-            // this.props.addDocTab(CurrentUserUtils.ActiveDashboard.isShared ? Doc.MakeAlias(this.props.document) : this.props.document, "add:right");
-            const bestAlias = DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
-            this.props.addDocTab(bestAlias ?? Doc.MakeAlias(this.props.document), "add:right");
 
-        }
-    }
     constructor(props: any) {
         super(props);
         if (!TreeView._openLevelScript) {
@@ -379,8 +368,14 @@ export class TreeView extends React.Component<TreeViewProps> {
         return rows;
     }
 
-    rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), (this.props.panelWidth() - treeBulletWidth())) / (this.props.treeView.props.scaling?.() || 1);
-    rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+    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);
+    }
+    rtfHeight = () => {
+        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+        return this.rtfWidth() <= layout[WidthSym]() ? Math.min(layout[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+    }
     rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth());
     expandPanelHeight = () => {
         if (this.layoutDoc._fitWidth) return this.docHeight();
@@ -580,13 +575,20 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
     onKeyDown = (e: React.KeyboardEvent) => {
         if (this.doc.treeViewHideHeader || this.props.treeView.outlineMode) {
-            e.stopPropagation();
-            e.preventDefault();
             switch (e.key) {
-                case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+                case "Tab":
+                    e.stopPropagation();
+                    e.preventDefault();
+                    setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
                     return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true), "tab");
-                case "Backspace": return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
-                case "Enter": return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
+                case "Backspace":
+                    e.stopPropagation();
+                    e.preventDefault();
+                    return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
+                case "Enter":
+                    e.stopPropagation();
+                    e.preventDefault();
+                    return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
             }
         }
     }
@@ -691,7 +693,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
         return <>
             <div className={`treeView-header` + (editing ? "-editing" : "")} key="titleheader"
-                style={{ width: "max-content" }}
+                style={{ width: StrCast(this.doc.treeViewHeaderWidth, "max-content") }}
                 ref={this._header}
                 onClick={this.ignoreEvent}
                 onPointerDown={this.ignoreEvent}
@@ -705,8 +707,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
 
     renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
-        const layout = StrCast(Doc.LayoutField(this.layoutDoc));
-        const isExpandable = layout.includes(FormattedTextBox.name) || layout.includes(SliderBox.name);
+        const isExpandable = this.doc._treeViewGrowsHorizontally;
         const panelWidth = asText || isExpandable ? this.rtfWidth : this.expandPanelWidth;
         const panelHeight = asText ? this.rtfOutlineHeight : isExpandable ? this.rtfHeight : this.expandPanelHeight;
         return <DocumentView key={this.doc[Id]} ref={action((r: DocumentView | null) => this._dref = r)}
@@ -717,6 +718,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
             NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
             LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined}
+            LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
             isContentActive={isActive}
             isDocumentActive={isActive}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
@@ -759,10 +761,10 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     // renders the document in the header field instead of a text proxy.
-    @computed get renderDocumentAsHeader() {
+    renderDocumentAsHeader = (asText: boolean) => {
         return <>
             {this.renderBullet}
-            {this.renderEmbeddedDocument(true, this.props.isContentActive)}
+            {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
         </>;
     }
 
@@ -794,9 +796,9 @@ export class TreeView extends React.Component<TreeViewProps> {
                 //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
                 onKeyDown={this.onKeyDown}>
                 <li className="collection-child">
-                    {hideTitle && this.doc.type !== DocumentType.RTF ?
+                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader ? // should test for prop 'treeViewRenderDocWithBulletAsHeader"
                         this.renderEmbeddedDocument(false, returnFalse) :
-                        this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader : this.renderTitleAsHeader, this._editTitle)}
+                        this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
                 </li>
             </div>;
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1eaff3c1c..bcf00e88d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1308,6 +1308,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         TraceMobx();
         const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
         const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : 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 (<div className="contentFittingDocumentView">
             {!this.props.Document || !this.props.PanelWidth() ? (null) : (
@@ -1316,7 +1317,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         transition: this.props.dataTransition,
                         position: this.props.Document.isInkMask ? "absolute" : undefined,
                         transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                        width: isButton ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
+                        width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
                         height: (!this.props.ignoreAutoHeight && this.layoutDoc.autoHeight && this.layoutDoc.type === DocumentType.RTF) || isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
                             `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
                     }}>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 2e312ee51..c9c74eca4 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -122,7 +122,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (Doc.UserDoc().activePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
         if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
             Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
-                title: "pres element template", type: DocumentType.PRESELEMENT, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
+                title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
             }));
             // this script will be called by each presElement to get rendering-specific info that the PresBox knows about but which isn't written to the PresElement
             // this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent
@@ -308,7 +308,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             if (!group) this._selectedArray.clear();
             this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //Update selected array
-            if (this.layoutDoc._viewType === "stacking" && !group) this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
+            if ([CollectionViewType.Stacking, CollectionViewType.Tree].includes(this.layoutDoc._viewType as any) && !group) this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
             this.onHideDocument(); //Handles hide after/before
         }
     });
@@ -620,9 +620,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         //@ts-ignore
         const viewType = e.target.selectedOptions[0].value as CollectionViewType;
         // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
-        viewType === CollectionViewType.Stacking && (this.rootDoc._pivotField = undefined);
+        [CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType) && (this.rootDoc._pivotField = undefined);
         this.rootDoc._viewType = viewType;
-        if (viewType === CollectionViewType.Stacking) this.layoutDoc._gridGap = 0;
+        if ([CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType)) this.layoutDoc._gridGap = 0;
     });
 
     /**
@@ -696,7 +696,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     }
-    childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
+    childLayoutTemplate = () => ![CollectionViewType.Stacking, CollectionViewType.Tree].includes(this.rootDoc._viewType as any) ? undefined : this.presElement;
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
@@ -2262,6 +2262,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     onChange={this.viewChanged}
                     value={mode}>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
+                    <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>Tree</option>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>
                 </select>}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
@@ -2459,7 +2460,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 }
 ScriptingGlobals.add(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
     if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data);
-    if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 35 : 31;
+    if (field === 'presCollapsedHeight') return [CollectionViewType.Tree || CollectionViewType.Stacking].includes(container._viewType as any) ? 35 : 31;
     if (field === 'presStatus') return container.presStatus;
     if (field === '_itemIndex') return container._itemIndex;
     if (field === 'presBox') return container;
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 1ad4b820e..a178be910 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -42,7 +42,7 @@ $slide-active: #5B9FDD;
     background-color: #d5dce2;
     border-radius: 5px;
     height: calc(100% - 7px);
-    width: calc(100% - 15px);
+    width: 100%;
     display: grid;
     grid-template-rows: 16px 10px auto;
     grid-template-columns: max-content max-content max-content max-content auto;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index a4ec559f5..5c16d743a 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -23,6 +23,7 @@ import { PresBox } from "./PresBox";
 import "./PresElementBox.scss";
 import { PresMovement } from "./PresEnums";
 import React = require("react");
+import { CollectionViewType } from "../../collections/CollectionView";
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -87,6 +88,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     isContentActive={this.props.isContentActive}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
+                    fitContentsToDoc={returnTrue}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
                     ScreenToLocalTransform={Transform.Identity}
@@ -321,7 +323,7 @@ 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={{ maxWidth: showMore ? (toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 195 : 220)) : toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 105 : 145), cursor: isSelected ? 'text' : 'grab' }}>
                         <EditableView
                             ref={this._titleRef}
                             editing={!isSelected ? false : undefined}
-- 
cgit v1.2.3-70-g09d2


From afeb2ac0d4aa914f94596e08fe4030abba050496 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 23 Apr 2022 16:11:14 -0400
Subject: more fixes for treeView to support  hierarchical presentations.

---
 src/client/views/DocComponent.tsx                |  4 ++-
 src/client/views/DocumentButtonBar.tsx           | 46 ++++++++++++------------
 src/client/views/collections/TabDocView.tsx      |  3 +-
 src/client/views/collections/TreeView.tsx        |  9 +++--
 src/client/views/nodes/trails/PresBox.tsx        |  8 ++---
 src/client/views/nodes/trails/PresElementBox.tsx |  4 +++
 6 files changed, 44 insertions(+), 30 deletions(-)

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

diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 73a261c40..79aaf2158 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -11,6 +11,7 @@ import { DocUtils } from '../documents/Documents';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { InteractionUtils } from '../util/InteractionUtils';
 import { UndoManager } from '../util/UndoManager';
+import { DocumentView } from './nodes/DocumentView';
 import { Touchable } from './Touchable';
 
 
@@ -41,6 +42,7 @@ interface ViewBoxBaseProps {
     Document: Doc;
     DataDoc?: Doc;
     ContainingCollectionDoc: Opt<Doc>;
+    DocumentView?: () => DocumentView;
     fieldKey: string;
     layerProvider?: (doc: Doc, assign?: boolean) => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
@@ -63,7 +65,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
         // key where data is stored
         @computed get fieldKey() { return this.props.fieldKey; }
 
-        lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.ContainingCollectionDoc }).result;
+        lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc }).result;
 
         isContentActive = (outsideReaction?: boolean) => (
             this.props.isContentActive?.() === false ? false :
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index aa9318310..9b95f1525 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -215,28 +215,30 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     pinWithView = (targetDoc: Doc) => {
         if (targetDoc) {
             TabDocView.PinDoc(targetDoc);
-            const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
-            const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
-            const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
-            if (scrollable) {
-                const scroll = targetDoc._scrollTop;
-                activeDoc.presPinView = true;
-                activeDoc.presPinViewScroll = scroll;
-            } else if (targetDoc.type === DocumentType.VID) {
-                activeDoc.presPinTimecode = targetDoc._currentTimecode;
-            } else if (pannable) {
-                const x = targetDoc._panX;
-                const y = targetDoc._panY;
-                const scale = targetDoc._viewScale;
-                activeDoc.presPinView = true;
-                activeDoc.presPinViewX = x;
-                activeDoc.presPinViewY = y;
-                activeDoc.presPinViewScale = scale;
-            } else if (targetDoc.type === DocumentType.COMPARISON) {
-                const width = targetDoc._clipWidth;
-                activeDoc.presPinClipWidth = width;
-                activeDoc.presPinView = true;
-            }
+            setTimeout(() => {
+                const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
+                const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
+                const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
+                if (scrollable) {
+                    const scroll = targetDoc._scrollTop;
+                    activeDoc.presPinView = true;
+                    activeDoc.presPinViewScroll = scroll;
+                } else if (targetDoc.type === DocumentType.VID) {
+                    activeDoc.presPinTimecode = targetDoc._currentTimecode;
+                } else if (pannable) {
+                    const x = targetDoc._panX;
+                    const y = targetDoc._panY;
+                    const scale = targetDoc._viewScale;
+                    activeDoc.presPinView = true;
+                    activeDoc.presPinViewX = x;
+                    activeDoc.presPinViewY = y;
+                    activeDoc.presPinViewScale = scale;
+                } else if (targetDoc.type === DocumentType.COMPARISON) {
+                    const width = targetDoc._clipWidth;
+                    activeDoc.presPinClipWidth = width;
+                    activeDoc.presPinView = true;
+                }
+            });
         }
     }
 
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 136486f47..98d6049f0 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -217,9 +217,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
             pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
             pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
+            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
             pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
             pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
-            pinDoc.treeViewGrowsHorizontally = true;// the document can expand horizontally when displayed as a tree view header  
+            pinDoc.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header  
             pinDoc.treeViewHideHeader = 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;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index a35304787..342424d41 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -110,7 +110,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get treeViewOpen() { return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._transientOpenState; }
     @computed get treeViewExpandedView() { return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView; }
     @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); }
-    @computed get dataDoc() { return this.doc[DataSym]; }
+    @computed get dataDoc() { return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym]; }
     @computed get layoutDoc() { return Doc.Layout(this.doc); }
     @computed get fieldKey() { return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc)); }
     @computed get childDocs() { return this.childDocList(this.fieldKey); }
@@ -280,7 +280,12 @@ export class TreeView extends React.Component<TreeViewProps> {
             (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
         const move = (!dropAction || dropAction === "proto" || dropAction === "move" || dropAction === "same") && moveDocument;
         if (canAdd) {
-            return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === "proto" ? addDoc(d) : false) : addDoc(d)) || added, false));
+            return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) =>
+                (move ?
+                    move(d, undefined, addDoc) || (dropAction === "proto" ? addDoc(d) : false)
+                    :
+                    addDoc(d)) || added,
+                false));
         }
         return false;
     }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index c9c74eca4..30ad43562 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -96,7 +96,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable private openMovementDropdown: boolean = false;
     @observable private openEffectDropdown: boolean = false;
     @observable private presentTools: boolean = false;
-    @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
+    @computed get childDocs() { return DocListCast(this.rootDoc[this.fieldKey]); }
     @computed get tagDocs() {
         const tagDocs: Doc[] = [];
         for (const doc of this.childDocs) {
@@ -697,7 +697,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return true;
     }
     childLayoutTemplate = () => ![CollectionViewType.Stacking, CollectionViewType.Tree].includes(this.rootDoc._viewType as any) ? undefined : this.presElement;
-    removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
+    removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
     isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && this.props.layerProvider?.(this.layoutDoc) !== false) &&
@@ -2256,14 +2256,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const isMini: boolean = this.toolbarWidth <= 100;
         return (
             <div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? "none" : undefined }}>
-                {isMini || Doc.UserDoc().noviceMode ? (null) : <select className="presBox-viewPicker"
+                {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}>List</option>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>Tree</option>
-                    <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>
+                    {Doc.UserDoc().noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
                 </select>}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
                     <span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}>
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5c16d743a..f4dc9b615 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -159,6 +159,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem = this.rootDoc;
         const dragArray = PresBox.Instance._dragArray;
         const dragData = new DragManager.DocumentDragData(PresBox.Instance.sortArray());
+        if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
+        dragData.dropAction = "move";
+        dragData.treeViewDoc = this.props.docViewPath().lastElement()?.props.treeViewDoc;
+        dragData.moveDocument = this.props.docViewPath().lastElement()?.props.moveDocument;
         const dragItem: HTMLElement[] = [];
         if (dragArray.length === 1) {
             const doc = dragArray[0];
-- 
cgit v1.2.3-70-g09d2


From ce94cdd070035ef6374eeb471ecf6e4542b4ba20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 28 Apr 2022 13:53:09 -0400
Subject: fixed rendering of slides in filesystem view to not render the UI,
 just the doc name

---
 src/client/documents/Documents.ts           | 1 +
 src/client/views/collections/TabDocView.tsx | 2 +-
 src/client/views/collections/TreeView.tsx   | 4 ++--
 3 files changed, 4 insertions(+), 3 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 087d32a60..741868a99 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -282,6 +282,7 @@ export class DocumentOptions {
     strokeWidth?: number;
     freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection
     treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
+    treeViewHideHeaderIfTemplate?: boolean; // whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)
     treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 98d6049f0..8e45ec3b3 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -221,7 +221,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
             pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
             pinDoc.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header  
-            pinDoc.treeViewHideHeader = true;  // this will force the document to render itself as the 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 presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 3a49297f8..647476784 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -596,7 +596,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
     }
     onKeyDown = (e: React.KeyboardEvent) => {
-        if (this.doc.treeViewHideHeader || this.props.treeView.outlineMode) {
+        if (this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
             switch (e.key) {
                 case "Tab":
                     e.stopPropagation();
@@ -810,7 +810,7 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     render() {
         TraceMobx();
-        const hideTitle = this.doc.treeViewHideHeader || this.props.treeView.outlineMode;
+        const hideTitle = this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
         return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
             <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`}
                 ref={this.createTreeDropTarget}
-- 
cgit v1.2.3-70-g09d2


From 55a5322fffc09bde04ef8b466e19b701bad2762b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 29 Apr 2022 17:38:06 -0400
Subject: added 'enter' to toggle iconify.  fixed webBox deiconify to render
 webpage.   updated colors in minimap to match icon colors.

---
 src/client/views/DocumentDecorations.tsx    | 11 ++++++-----
 src/client/views/GlobalKeyHandler.ts        |  4 ++++
 src/client/views/collections/TabDocView.tsx | 17 ++++++++++++++---
 src/client/views/nodes/WebBox.tsx           |  8 ++++----
 4 files changed, 28 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1feb643df..fb8cfbc15 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -146,14 +146,15 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
 
     _deleteAfterIconify = false;
     _iconifyBatch: UndoManager.Batch | undefined;
-    onCloseClick = (force = false) => {
+    onCloseClick = (forceDeleteOrIconify: boolean | undefined) => {
         if (this.canDelete) {
             const views = SelectionManager.Views().slice().filter(v => v);
-            this._deleteAfterIconify = force || this._iconifyBatch ? true : false;
+            if (forceDeleteOrIconify === false && this._iconifyBatch) return;
+            this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false;
             if (!this._iconifyBatch) {
                 this._iconifyBatch = UndoManager.StartBatch("iconifying");
             } else {
-                force = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
+                forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
             }
             var iconifyingCount = views.length;
             const finished = action((force?: boolean) => {
@@ -166,7 +167,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     this._iconifyBatch = undefined;
                 }
             });
-            if (force) finished(force);
+            if (forceDeleteOrIconify) finished(forceDeleteOrIconify);
             else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
         }
     }
@@ -531,7 +532,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     left: bounds.x - this._resizeBorderWidth / 2,
                     top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
                 }}>
-                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(this.hasIcons), "Close")}
+                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(this.hasIcons ? true : undefined), "Close")}
                     {titleArea}
                     {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
                     {hideResizers ? (null) :
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 6120c4c9e..914802041 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -139,6 +139,10 @@ export class KeyManager {
                 window.getSelection()?.empty();
                 document.body.focus();
                 break;
+            case "enter": {
+                DocumentDecorations.Instance.onCloseClick(false);
+                break;
+            }
             case "delete":
             case "backspace":
                 if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 8e45ec3b3..78b125e07 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -443,9 +443,20 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                 default: return DefaultStyleProvider(doc, props, property);
                 case StyleProp.PointerEvents: return "none";
                 case StyleProp.DocContents:
-                    const background = doc.type === DocumentType.PDF ? "red" : doc.type === DocumentType.IMG ? "blue" : doc.type === DocumentType.RTF ? "orange" :
-                        doc.type === DocumentType.VID ? "purple" : doc.type === DocumentType.WEB ? "yellow" : doc.type === DocumentType.MAP ? "blue" : "gray";
-                    return doc.type === DocumentType.COL ?
+                    const background = ((type: DocumentType) => {
+                        switch (type) {
+                            case DocumentType.PDF: return "pink";
+                            case DocumentType.AUDIO: return "lightgreen";
+                            case DocumentType.WEB: return "brown";
+                            case DocumentType.IMG: return "blue";
+                            case DocumentType.MAP: return "orange";
+                            case DocumentType.VID: return "purple";
+                            case DocumentType.RTF: return "yellow";
+                            case DocumentType.COL: return undefined;
+                            default: return "gray";
+                        }
+                    })(doc.type as DocumentType);
+                    return !background ?
                         undefined :
                         <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;
             }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 5a39123b9..5c72417cc 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -158,7 +158,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             });
                     }
                 }
-            });
+            }, { fireImmediately: this.props.isSelected() });
 
         this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight,
             autoHeight => {
@@ -202,10 +202,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         );
         quickScroll = false;
     }
-    componentWillUnmount() {
+    @action componentWillUnmount() {
         Object.values(this._disposers).forEach(disposer => disposer?.());
-        this._iframe?.removeEventListener('wheel', this.iframeWheel, true);
-        this._iframe?.contentDocument?.removeEventListener("pointerup", this.iframeUp);
+        // this._iframe?.removeEventListener('wheel', this.iframeWheel, true);
+        // this._iframe?.contentDocument?.removeEventListener("pointerup", this.iframeUp);
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


From f072a9f2c1ad69eb8ae4242fa5d2e18bbd94f6ef Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 6 May 2022 11:45:44 -0400
Subject: changed following links to open up icons all the way down to the
 target document. allow StartLink to work with webBox w/ text selection. 
 fixed web clippings to have a cursor and grab keyDown events.

---
 src/client/util/DocumentManager.ts                 | 42 +++++++++++++++-------
 .../util/Import & Export/DirectoryImportBox.tsx    |  2 +-
 src/client/util/LinkManager.ts                     | 11 +++---
 src/client/util/SharingManager.tsx                 | 30 ++++++++--------
 src/client/views/DocumentDecorations.tsx           | 13 ++++---
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/collections/TabDocView.tsx        |  2 +-
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collectionLinear/CollectionLinearView.tsx      |  2 +-
 .../collectionSchema/CollectionSchemaCells.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 src/client/views/nodes/LinkDescriptionPopup.tsx    |  1 +
 src/client/views/nodes/PDFBox.tsx                  |  1 +
 src/client/views/nodes/WebBox.scss                 |  3 ++
 src/client/views/nodes/WebBox.tsx                  | 11 +++---
 src/client/views/nodes/trails/PresBox.tsx          |  4 +--
 src/client/views/search/SearchBox.tsx              |  2 +-
 18 files changed, 84 insertions(+), 51 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index b6c28d2fe..aeddc3249 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -156,7 +156,7 @@ export class DocumentManager {
         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
-        docContext?: Doc,  // context to load that should contain the target
+        docContext: Doc[],  // context to load that should contain the target
         linkDoc?: Doc,   // link that's being followed
         closeContextIfNotFound: boolean = false, // after opening a context where the document should be, this determines whether the context should be closed if the Doc isn't actually there
         originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc
@@ -190,31 +190,40 @@ export class DocumentManager {
             finished?.();
         };
         const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc);
-        const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
-        const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
+        const contextDocs = docContext.length ? await DocListCastAsync(docContext[0].data) : undefined;
+        const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext.lastElement() : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
         const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||
             (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
-        if ((!docView && targetDoc.type !== DocumentType.MARKER) && annoContainerView) {
+        if (annoContainerView) {
             if (annoContainerView.props.Document.layoutKey === "layout_icon") {
-                annoContainerView.iconify(() => this.jumpToDocument(
-                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
-                    finished, originalTarget, noSelect, presZoom));
+                annoContainerView.iconify(() => annoContainerView.focus(targetDoc, {
+                    originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
+                        new Promise<ViewAdjustment>(res => {
+                            focusAndFinish(true);
+                            res(ViewAdjustment.doNothing);
+                        })
+                }));
                 return;
-            } else {
+            } else if (!docView && targetDoc.type !== DocumentType.MARKER) {
                 annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
             }
         }
         if (focusView) {
             !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
-            focusView.focus(targetDoc, {
+            const doFocus = (forceDidFocus: boolean) => focusView.focus(originalTarget ?? targetDoc, {
                 originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
                     new Promise<ViewAdjustment>(res => {
-                        focusAndFinish(didFocus);
+                        focusAndFinish(forceDidFocus || didFocus);
                         res(ViewAdjustment.doNothing);
                     })
             });
+            if (focusView.props.Document.layoutKey === "layout_icon") {
+                focusView.iconify(() => doFocus(true));
+            } else {
+                doFocus(false);
+            }
         } else {
             if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
                 createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
@@ -228,7 +237,7 @@ export class DocumentManager {
                             targetDocContext._viewTransition = undefined;
                             if (targetDocContext.layoutKey === "layout_icon") {
                                 targetDocContextView.iconify(() => this.jumpToDocument(
-                                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
+                                    resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
                                     finished, originalTarget, noSelect, presZoom));
                             }
                             return ViewAdjustment.doNothing;
@@ -263,7 +272,16 @@ export class DocumentManager {
                         };
                         setTimeout(() => findView(0), 0);
                     }
-                } else {  // there's no context view so we need to create one first and try again when that finishes
+                } else {
+                    if (docContext.length && docContext[0]?.layoutKey === "layout_icon") {
+                        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));
+                        }
+                    }
+                    // there's no context view so we need to create one first and try again when that finishes
                     const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget);
                     createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
                         finishFunc);
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 39e9251a5..37571ae01 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -161,7 +161,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
             await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
             Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
             !this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
-            DocumentManager.Instance.jumpToDocument(importContainer, true);
+            DocumentManager.Instance.jumpToDocument(importContainer, true, undefined, []);
         }
 
         runInAction(() => {
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 9445533dc..b28662a57 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,3 +1,4 @@
+import { validationResult } from "express-validator/check";
 import { action, observable, observe } from "mobx";
 import { computedFn } from "mobx-utils";
 import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc";
@@ -241,10 +242,12 @@ export class LinkManager {
                     } else {
                         const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
                         const containerDoc = containerAnnoDoc || target;
-                        const containerDocContext = Cast(containerDoc?.context, Doc, null);
-                        const targetContext = LightboxView.LightboxDoc ? containerAnnoDoc || containerDocContext : containerDocContext;
-                        const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
-                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, allFinished);
+                        var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : [] as Doc[];
+                        while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
+                            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+                        }
+                        const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]] : containerDocContext;
+                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
                     }
                 } else {
                     allFinished();
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6d7f7e8df..11450309a 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -370,11 +370,11 @@ export class SharingManager extends React.Component<{}> {
         if (!uniform) dropdownValues.unshift("-multiple-");
         if (override) dropdownValues.unshift("None");
         return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
-            (
-                <option key={permission} value={permission}>
-                    {permission}
-                </option>
-            )
+        (
+            <option key={permission} value={permission}>
+                {permission}
+            </option>
+        )
         );
     }
 
@@ -388,7 +388,7 @@ export class SharingManager extends React.Component<{}> {
                 onClick={() => {
                     let context: Opt<CollectionView>;
                     if (this.targetDoc && this.targetDocView && docs.length === 1 && (context = this.targetDocView.props.ContainingCollectionView)) {
-                        DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document);
+                        DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, [context.props.Document]);
                     }
                 }}
                 onPointerEnter={action(() => {
@@ -548,10 +548,10 @@ export class SharingManager extends React.Component<{}> {
                                 {this.sharingOptions(uniform)}
                             </select>
                         ) : (
-                                <div className={"permissions-dropdown"}>
-                                    {permissions}
-                                </div>
-                            )}
+                            <div className={"permissions-dropdown"}>
+                                {permissions}
+                            </div>
+                        )}
                     </div>
                 </div>
             );
@@ -572,7 +572,7 @@ export class SharingManager extends React.Component<{}> {
                         <div className="edit-actions">
                             <div className={"permissions-dropdown"}>
                                 Owner
-                        </div>
+                            </div>
                         </div>
                     </div>
                 ) : null,
@@ -622,10 +622,10 @@ export class SharingManager extends React.Component<{}> {
                                 {this.sharingOptions(uniform, group.title === "Override")}
                             </select>
                         ) : (
-                                <div className={"permissions-dropdown"}>
-                                    {permissions}
-                                </div>
-                            )}
+                            <div className={"permissions-dropdown"}>
+                                {permissions}
+                            </div>
+                        )}
                     </div>
                 </div>
             );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index eb2bcc18c..936946df0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -113,7 +113,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         }
     }
 
-    titleEntered = (e: React.KeyboardEvent) => e.key === "Enter" && (e.target as any).blur();
+    titleEntered = (e: React.KeyboardEvent) => {
+        if (e.key === "Enter") {
+            e.stopPropagation();
+            (e.target as any).blur();
+        }
+    }
 
     @action onTitleDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
@@ -288,7 +293,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
             const project = (p: number[], a: number[], b: number[]) => {
                 const atob = [b[0] - a[0], b[1] - a[1]];
-                const atop = [p[0] - a[0], p[1] - a[1]]; first
+                const atop = [p[0] - a[0], p[1] - a[1]];
                 const len = atob[0] * atob[0] + atob[1] * atob[1];
                 let dot = atop[0] * atob[0] + atop[1] * atob[1];
                 const t = dot / len;
@@ -501,7 +506,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     value={this._accumulatedTitle}
                     onBlur={e => this.titleBlur()}
                     onChange={action(e => this._accumulatedTitle = e.target.value)}
-                    onKeyPress={this.titleEntered} /> :
+                    onKeyDown={this.titleEntered} /> :
                 <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
@@ -521,7 +526,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
         const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : "";
 
-        const rotation = NumCast(seldoc.rootDoc._jitterRotation)
+        const rotation = NumCast(seldoc.rootDoc._jitterRotation);
         const rdeg = rotation / 180 * Math.PI;
 
         return (<div className={`documentDecorations${colorScheme}`}>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c794c73db..db51c54f8 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -133,7 +133,7 @@ export class MainView extends React.Component {
         window.addEventListener("paste", KeyManager.Instance.paste as any);
         document.addEventListener("dash", (e: any) => {  // event used by chrome plugin to tell Dash which document to focus on
             const id = FormattedTextBox.GetDocFromUrl(e.detail);
-            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null));
+            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined, []) : (null));
         });
         document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
         this.initEventListeners();
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 78b125e07..8dfc7edc7 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -256,7 +256,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 }
                 PresBox.Instance?._selectedArray.clear();
                 pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-                DocumentManager.Instance.jumpToDocument(doc, false, undefined);
+                DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
                 batch.end();
             });
         }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 72480b094..164021358 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -72,7 +72,7 @@ export enum TreeSort {
     Down = "down",
     Zindex = "z",
     None = "none"
-};
+}
 /**
  * Renders a treeView of a collection of documents
  * 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5d1e1892c..5b3a7db41 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1423,7 +1423,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     replaceCanvases = (oldDiv: HTMLElement, newDiv: HTMLElement) => {
-        if (oldDiv.childNodes) {
+        if (oldDiv.childNodes && newDiv) {
             for (let i = 0; i < oldDiv.childNodes.length; i++) {
                 this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
             }
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index e3d4f86c1..c615bfb8e 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -231,7 +231,7 @@ export class CollectionLinearView extends CollectionSubView() {
                         <span className="bottomPopup-text">
                             Currently playing:
                             {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
-                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true)}>
+                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
                                     {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")}
                                 </span>)}
                         </span>
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index c2bb3b3ac..adcd9e1e3 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -199,7 +199,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
             const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
             // Jump to the this document
-            DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext,
+            DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext ? [targetContext] : [],
                 undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc));
         }
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8570f6fff..02ea87103 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -663,7 +663,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
                 const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y]);
+                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
             }
         }
     }
@@ -857,6 +857,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     @observable _retryThumb = 1;
     thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+        !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
         !this._componentView?.isAnyChildContentActive?.() ? true : false
     @computed get contents() {
         TraceMobx();
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index a9d33f161..ccac66996 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -54,6 +54,7 @@ export class LinkDescriptionPopup extends React.Component<{}> {
                 top: LinkDescriptionPopup.popupY ? LinkDescriptionPopup.popupY : 350,
             }}>
             <input className="linkDescriptionPopup-input"
+                onKeyDown={e => e.stopPropagation()}
                 onKeyPress={e => e.key === "Enter" && this.onDismiss(true)}
                 placeholder={"(Optional) Enter link description..."}
                 onChange={(e) => this.descriptionChanged(e)}>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 69662d53a..48465976a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -75,6 +75,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     updateIcon = () => {
+        return; // currently we render pdf icons as text labels
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
         newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index b037e7220..d8dd074a5 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -156,6 +156,9 @@
             position: absolute;
             top: 0;
             left: 0;
+            cursor: text;
+            padding: 15px;
+            height: 100%
         }
 
         .webBox-cont {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 80961c92f..f441e5d36 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -207,8 +207,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     @action
-    createTextAnnotation = (sel: Selection, selRange: Range) => {
-        if (this._mainCont.current) {
+    createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
+        if (this._mainCont.current && selRange) {
             const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
@@ -235,7 +235,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
-        this.props.select(true);
+        this.props.select(false); // hack to preven the webBox from being replaced with a thumbnail that currently doesn't show the the anchors
         if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth);
         if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
@@ -252,7 +252,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
         this._initialScroll = NumCast(this.layoutDoc._scrollTop);
-        return 0;
+        return undefined;
     }
 
     getAnchor = () => {
@@ -279,7 +279,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
-                this._textAnnotationCreator = () => this.createTextAnnotation(sel, sel.getRangeAt(0));
+                this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX,
                     e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
             }
@@ -650,6 +650,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get content() {
         const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
         return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
+            onKeyDown={e => e.stopPropagation()}
             style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
             {this.urlContent}
         </div>;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 30ad43562..820d7ef96 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -389,11 +389,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             LightboxView.SetLightboxDoc(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
+            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, [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);
             //awaiting jump so that new scale can be found, since jumping is async     
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
+            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
         }
         // 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.
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 9257cb75e..3d36efbc4 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -365,7 +365,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * or opening it in a new tab.
      */
     selectElement = async (doc: Doc, finishFunc: () => void) => {
-        await DocumentManager.Instance.jumpToDocument(doc, true, undefined, undefined, undefined, undefined, undefined, finishFunc);
+        await DocumentManager.Instance.jumpToDocument(doc, true, undefined, [], undefined, undefined, undefined, finishFunc);
     }
 
     /**
-- 
cgit v1.2.3-70-g09d2


From 57c1b8235b26a1b6a315922b4dc2926e1e597674 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 13 May 2022 18:17:10 -0400
Subject: removed layerProvider functionality.

---
 src/client/documents/Documents.ts                  |  3 +-
 src/client/views/DocComponent.tsx                  |  4 --
 src/client/views/GestureOverlay.tsx                |  1 -
 src/client/views/InkingStroke.tsx                  |  2 +-
 src/client/views/LightboxView.tsx                  |  1 -
 src/client/views/MainView.tsx                      |  6 ---
 src/client/views/OverlayView.tsx                   |  1 -
 src/client/views/Palette.tsx                       |  1 -
 src/client/views/PropertiesView.tsx                |  2 -
 src/client/views/StyleProvider.tsx                 | 52 +++-------------------
 src/client/views/TemplateMenu.tsx                  |  1 -
 src/client/views/collections/CollectionMenu.tsx    |  1 -
 .../collections/CollectionStackedTimeline.tsx      |  2 +-
 .../views/collections/CollectionStackingView.tsx   |  2 -
 .../views/collections/CollectionTreeView.tsx       |  1 -
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        | 19 +-------
 src/client/views/collections/TreeView.tsx          |  2 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 24 +++++-----
 .../collections/collectionFreeForm/MarqueeView.tsx | 10 ++---
 .../collectionLinear/CollectionLinearView.tsx      |  1 -
 .../CollectionMulticolumnView.tsx                  |  1 -
 .../CollectionMultirowView.tsx                     |  1 -
 .../collectionSchema/CollectionSchemaView.tsx      |  1 -
 .../collections/collectionSchema/SchemaTable.tsx   |  1 -
 src/client/views/linking/LinkPopup.tsx             |  3 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 -
 src/client/views/nodes/DocumentView.tsx            |  5 +--
 src/client/views/nodes/FilterBox.tsx               |  1 -
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/KeyValuePair.tsx            |  1 -
 src/client/views/nodes/LinkAnchorBox.tsx           |  1 -
 src/client/views/nodes/LinkDocPreview.tsx          |  1 -
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 src/client/views/nodes/WebBox.tsx                  |  6 +--
 .../views/nodes/formattedText/DashDocView.tsx      |  1 -
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  2 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  1 -
 src/client/views/pdf/Annotation.tsx                |  2 +-
 src/client/views/search/SearchBox.tsx              |  2 +-
 src/fields/Doc.ts                                  |  2 +-
 src/mobile/AudioUpload.tsx                         |  1 -
 src/mobile/MobileInterface.tsx                     |  1 -
 src/server/server_Initialization.ts                |  2 +-
 46 files changed, 41 insertions(+), 143 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d9c2e0d8b..797a129c4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -122,7 +122,6 @@ export class DocumentOptions {
     _scrollTop?: number; // scroll location for pdfs
     _noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged
     _chromeHidden?: boolean; // whether the editing chrome for a document is hidden
-    _layerTags?: List<string>; // layer tags a document has (used for tab filtering "layers" in document tab)
     _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
     _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active)
     _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
@@ -426,7 +425,7 @@ export namespace Docs {
                     childDontRegisterViews: true, _isLinkButton: true, _height: 150, description: "", showCaption: "description",
                     backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area
                     links: "@links(self)",
-                    _removeDropProperties: new List(["_layerTags", "isLinkButton"]),
+                    _removeDropProperties: new List(["isLinkButton"]),
                 }
             }],
             [DocumentType.LINKDB, {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 79aaf2158..9c176a4fd 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -44,7 +44,6 @@ interface ViewBoxBaseProps {
     ContainingCollectionDoc: Opt<Doc>;
     DocumentView?: () => DocumentView;
     fieldKey: string;
-    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
     isContentActive: () => boolean | undefined;
     renderDepth: number;
@@ -85,7 +84,6 @@ export interface ViewBoxAnnotatableProps {
     DataDoc?: Doc;
     fieldKey: string;
     filterAddDocument?: (doc: Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
-    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
     isContentActive: () => boolean | undefined;
     select: (isCtrlPressed: boolean) => void;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
@@ -209,13 +207,11 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
-                            this.props.layerProvider?.(doc, true);
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         });
                     }
                     else {
                         added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => {  // only make a pushpin if we have acl's to edit the document
-                            this.props.layerProvider?.(doc, true);
                             //DocUtils.LeavePushpin(doc);
                             doc._stayInCollection = undefined;
                             doc.context = this.props.Document;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 50dca0a99..482b62479 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -881,7 +881,6 @@ export class GestureOverlay extends Touchable {
                 isContentActive={returnFalse}
                 renderDepth={0}
                 styleProvider={returnEmptyString}
-                layerProvider={undefined}
                 docViewPath={returnEmptyDoclist}
                 focus={DocUtils.DefaultFocus}
                 whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 3d9c048e5..d0f01c363 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -332,7 +332,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             inkStrokeWidth, inkStrokeWidth + (highlightIndex && closed && fillColor && (new Color(fillColor)).alpha() < 1 ? 6 : 15),
             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.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted"), 0.0,
+            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. 
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 59ed0dc92..9882693ee 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -246,7 +246,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         docFilters={this.docFilters}
                         removeDocument={undefined}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={returnTrue}
                         ScreenToLocalTransform={this.lightboxScreenToLocal}
                         PanelWidth={this.lightboxWidth}
                         PanelHeight={this.lightboxHeight}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index db51c54f8..2e1d10955 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -281,7 +281,6 @@ export class MainView extends React.Component {
             addDocTab={this.addDocTabFunc}
             pinToPres={emptyFunction}
             docViewPath={returnEmptyDoclist}
-            layerProvider={undefined}
             styleProvider={undefined}
             rootSelected={returnTrue}
             isContentActive={returnTrue}
@@ -361,7 +360,6 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        layerProvider={undefined}
                         styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
@@ -402,7 +400,6 @@ export class MainView extends React.Component {
                 docViewPath={returnEmptyDoclist}
                 focus={DocUtils.DefaultFocus}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 isContentActive={returnTrue}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
@@ -516,7 +513,6 @@ export class MainView extends React.Component {
                     dropAction={"alias"}
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     rootSelected={returnTrue}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
@@ -591,7 +587,6 @@ export class MainView extends React.Component {
                     ContainingCollectionDoc={undefined}
                     Document={DocumentLinksButton.invisibleWebDoc}
                     dropAction={"move"}
-                    layerProvider={undefined}
                     styleProvider={undefined}
                     isSelected={returnFalse}
                     select={returnFalse}
@@ -673,7 +668,6 @@ export class MainView extends React.Component {
                             rootSelected={returnFalse}
                             renderDepth={0}
                             setHeight={returnFalse}
-                            layerProvider={undefined}
                             styleProvider={undefined}
                             addDocTab={returnFalse}
                             pinToPres={returnFalse}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 0f51cf9b2..ebad2981d 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -195,7 +195,6 @@ export class OverlayView extends React.Component {
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 529697f71..954529bc9 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -54,7 +54,6 @@ export default class Palette extends React.Component<PaletteProps> {
                             focus={emptyFunction}
                             docViewPath={returnEmptyDoclist}
                             styleProvider={returnEmptyString}
-                            layerProvider={undefined}
                             whenChildContentsActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
                             docFilters={returnEmptyFilter}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 21c688421..bba2ac211 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -303,7 +303,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     fitContentsToDoc={returnTrue}
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     freezeDimensions={true}
                     dontCenter={"y"}
@@ -1010,7 +1009,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             createNewFilterDoc={this.createNewFilterDoc}
                             updateFilterDoc={this.updateFilterDoc}
                             docViewPath={returnEmptyDoclist}
-                            layerProvider={undefined}
                             dontCenter="y"
                         />
                     </div>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 5056dedaf..334415b38 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -26,10 +26,6 @@ import React = require("react");
 import { InkingStroke } from './InkingStroke';
 import { TreeSort } from './collections/TreeView';
 
-export enum StyleLayers {
-    Background = "background"
-}
-
 export enum StyleProp {
     TreeViewIcon = "treeViewIcon",
     TreeViewSortings = "treeViewSortings",// options for how to sort tree view items
@@ -56,14 +52,9 @@ export enum StyleProp {
 
 function darkScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark; }
 
-function toggleBackground(doc: Doc) {
+function toggleLockedPosition(doc: Doc) {
     UndoManager.RunInBatch(() => runInAction(() => {
-        const layers = StrListCast(doc._layerTags);
-        if (!layers.includes(StyleLayers.Background)) {
-            if (!layers.length) doc._layerTags = new List<string>([StyleLayers.Background]);
-            else layers.push(StyleLayers.Background);
-        }
-        else layers.splice(layers.indexOf(StyleLayers.Background), 1);
+        doc._lockedPosition = !doc._lockedPosition;
     }), "toggleBackground");
 }
 
@@ -86,7 +77,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const isOpen = property.includes(":open");
     const fieldKey = props?.fieldKey ? props.fieldKey + "-" : isCaption ? "caption-" : "";
     const comicStyle = () => doc && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === "comic";
-    const isBackground = () => StrListCast(doc?._layerTags).includes(StyleLayers.Background);
+    const isBackground = () => doc && BoolCast(doc._lockedPosition);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
     const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
     const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
@@ -177,7 +168,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
             }
-            if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = DashColor(docColor).fade(0.5).toString();
+            if (docColor && !doc) docColor = DashColor(docColor).fade(0.5).toString();
             return docColor;
         }
         case StyleProp.BoxShadow: {
@@ -205,17 +196,15 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.PointerEvents:
             if (doc?.type === DocumentType.MARKER) return "none";
             if (props?.pointerEvents?.() === "none") return "none";
-            const layer = doc && props?.layerProvider?.(doc);
             const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name);
-            if (opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
-            if (layer === false && !selected && !SnappingManager.GetIsDragging()) return "none";
-            if (!isInk && layer === true) return "all";
+            if (doc?._lockedPosition || opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
+            if (!isInk) return "all";
             return undefined;
         case StyleProp.Decorations:
             if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) {
                 return doc && (isBackground() || selected) && (props?.renderDepth || 0) > 0 &&
                     ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
-                    <div className="styleProvider-lock" onClick={() => toggleBackground(doc)}>
+                    <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                         <FontAwesomeIcon icon={isBackground() ? "lock" : "unlock"} style={{ color: isBackground() ? "red" : undefined }} size="lg" />
                     </div>
                     : (null);
@@ -251,30 +240,3 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps
     }
     return DefaultStyleProvider(doc, props, property);
 }
-
-//
-// a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents
-//  currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab.
-//     if it matches, then the document gets pointer events, otherwise it does not.
-//
-export function DefaultLayerProvider(thisDoc: Doc) {
-    return (doc: Doc, assign?: boolean) => {
-        if (doc.z) return true;
-        if (assign) {
-            const activeLayer = StrCast(thisDoc?.activeLayer);
-            if (activeLayer) {
-                const layers = Cast(doc._layerTags, listSpec("string"), []);
-                if (layers.length && !layers.includes(activeLayer)) layers.push(activeLayer);
-                else if (!layers.length) doc._layerTags = new List<string>([activeLayer]);
-                if (activeLayer === "red" || activeLayer === "green" || activeLayer === "blue") doc._backgroundColor = activeLayer;
-            }
-            return true;
-        } else {
-            if (Doc.AreProtosEqual(doc, thisDoc)) return true;
-            const layers = StrListCast(doc._layerTags);
-            if (!layers.length && !thisDoc?.activeLayer) return true;
-            if (layers.includes(StrCast(thisDoc?.activeLayer))) return true;
-            return false;
-        }
-    };
-}
\ No newline at end of file
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index b3a24e031..636f7042f 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -131,7 +131,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 setHeight={returnFalse}
                 docViewPath={returnEmptyDoclist}
                 docFilters={returnEmptyFilter}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 364a2440e..b3fbd4ca4 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -108,7 +108,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
                 dropAction={"alias"}
                 setHeight={returnFalse}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 rootSelected={returnTrue}
                 bringToFront={emptyFunction}
                 select={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index de307416f..683b6d51d 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -392,7 +392,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     // handles dragging and dropping markers in timeline
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) {
-        if (!de.embedKey && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false;
+        if (!de.embedKey && this.props.Document._isGroup) return false;
         if (!super.onInternalDrop(e, de)) return false;
 
         // determine x coordinate of drop and assign it to the documents being dragged  --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 509005b45..4338dedcc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -222,7 +222,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             PanelWidth={width}
             PanelHeight={height}
             styleProvider={this.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             fitWidth={this.props.childFitWidth}
             isContentActive={emptyFunction}
@@ -543,7 +542,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     renderDepth={this.props.renderDepth}
                     focus={emptyFunction}
                     styleProvider={this.props.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 41970eb96..5e89016db 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -296,7 +296,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     renderDepth={this.props.renderDepth + 1}
                     focus={emptyFunction}
                     styleProvider={this.props.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ee2c28b5f..7c5f49b42 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -261,7 +261,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             CollectionView: this,
         };
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: this.props.layerProvider?.(this.rootDoc) === false ? "none" : undefined }}>
+            style={{ pointerEvents: this.rootDoc._lockedPosition ? "none" : undefined }}>
             {this.showIsTagged()}
             {this.renderSubView(this.collectionViewType, props)}
         </div>);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 8dfc7edc7..73e5a9178 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -26,7 +26,7 @@ import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
-import { DefaultLayerProvider, DefaultStyleProvider, StyleLayers, StyleProp } from '../StyleProvider';
+import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@@ -94,20 +94,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
 
             if (tab.element[0].children[1].children.length === 1) {
-                const toggle = document.createElement("div");
-                toggle.style.width = "10px";
-                toggle.style.height = "calc(100% - 2px)";
-                toggle.style.left = "-2px";
-                toggle.style.bottom = "1px";
-                toggle.style.borderTopRightRadius = "7px";
-                toggle.style.position = "relative";
-                toggle.style.display = "inline-block";
-                toggle.style.background = "transparent";
-                toggle.onclick = (e: MouseEvent) => {
-                    if (tab.contentItem === tab.header.parent.getActiveContentItem()) {
-                        tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : StyleLayers.Background;
-                    }
-                };
                 iconWrap.className = "lm_iconWrap";
                 iconWrap.id = "lm_iconWrap";
                 closeWrap.className = "lm_iconWrap";
@@ -357,7 +343,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform);
     hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
 
-    @computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); }
     @computed get docView() {
         return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
             <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)}
@@ -369,7 +354,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 isContentActive={returnTrue}
                 PanelWidth={this.PanelWidth}
                 PanelHeight={this.PanelHeight}
-                layerProvider={this.layerProvider}
                 styleProvider={DefaultStyleProvider}
                 docFilters={CollectionDockingView.Instance.childDocFilters}
                 docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
@@ -520,7 +504,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={TabMinimapView.miniStyleProvider}
-                    layerProvider={undefined}
                     addDocTab={this.props.addDocTab}
                     pinToPres={TabDocView.PinDoc}
                     docFilters={CollectionDockingView.Instance.childDocFilters}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 164021358..d608f800c 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -663,7 +663,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideDecorationTitle={this.props.treeView.outlineMode}
                 hideResizeHandles={this.props.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
-                layerProvider={returnTrue}
                 docViewPath={returnEmptyDoclist}
                 treeViewDoc={this.props.treeView.props.Document}
                 addDocument={undefined}
@@ -760,7 +759,6 @@ export class TreeView extends React.Component<TreeViewProps> {
             renderDepth={this.props.renderDepth + 1}
             treeViewDoc={this.props.treeView?.props.Document}
             rootSelected={returnTrue}
-            layerProvider={returnTrue}
             docViewPath={this.props.treeView.props.docViewPath}
             docFilters={returnEmptyFilter}
             docRangeFilters={returnEmptyFilter}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4f79d19ad..322944d92 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -44,7 +44,7 @@ import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { VideoBox } from "../../nodes/VideoBox";
 import { CreateImage } from "../../nodes/WebBoxRenderer";
-import { StyleLayers, StyleProp } from "../../StyleProvider";
+import { StyleProp } from "../../StyleProvider";
 import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
 import { CollectionViewType } from "../CollectionView";
@@ -122,8 +122,6 @@ 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); }
-    @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); }
-    @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); }
     @computed get fitToContentVals() {
         return {
             bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
@@ -222,8 +220,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
-        if (!de.embedKey && !this.ChildDrag && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false;
-        if (!super.onInternalDrop(e, de)) return false;
+        if (!de.embedKey && !this.ChildDrag && this.rootDoc._isGroup) return false;
+        if (this.rootDoc._lockedPosition || !super.onInternalDrop(e, de)) return false;
         const refDoc = docDragData.droppedDocuments[0];
         const [xpo, ypo] = this.getContainerTransform().transformPoint(de.x, de.y);
         const z = NumCast(refDoc.z);
@@ -251,7 +249,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            !StrListCast(d._layerTags).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -418,8 +416,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 styleProp = colors[cluster % colors.length];
                 const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
                 // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
-                set?.filter(s => !StrListCast(s._layerTags).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
-                set?.filter(s => StrListCast(s._layerTags).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
+                set?.map(s => styleProp = StrCast(s.backgroundColor));
             }
         } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
         return styleProp;
@@ -1017,7 +1014,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     bringToFront = (doc: Doc, sendToBack?: boolean) => {
-        if (sendToBack || StrListCast(doc._layerTags).includes(StyleLayers.Background)) {
+        if (sendToBack) {
             doc.zIndex = 0;
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
@@ -1155,7 +1152,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.backgroundActive || this.props.childPointerEvents ? "all" :
+        const pointerEvents = this.props.isContentActive() === false ? "none" : this.props.childPointerEvents ? "all" :
             (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents?.();
         return pointerEvents;
     }
@@ -1195,7 +1192,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
             docViewPath={this.props.docViewPath}
             styleProvider={this.getClusterColor}
-            layerProvider={this.props.layerProvider}
             dataProvider={this.childDataProvider}
             sizeProvider={this.childSizeProvider}
             freezeDimensions={BoolCast(this.props.Document.childFreezeDimensions, this.props.childFreezeDimensions)}
@@ -1614,7 +1610,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => intersectRect(docDims(doc), rect);
 
         const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
-        let snappableDocs = activeDocs.filter(doc => !StrListCast(doc._layerTags).includes(StyleLayers.Background) && doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
+        let snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs
 
@@ -1739,7 +1735,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             onContextMenu={this.onContextMenu}
             style={{
                 pointerEvents: this.props.Document.type === DocumentType.MARKER ? "none" :  // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
-                    this.backgroundEvents ? "all" : this.props.pointerEvents?.() as any,
+                    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()
@@ -1767,7 +1763,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     </svg>
                 </div>}
 
-            {this.props.Document._isGroup && SnappingManager.GetIsDragging() && (this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) ?
+            {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ?
                 <div className="collectionFreeForm-groupDropper" ref={this.createGroupEventsTarget} style={{
                     width: this.ChildDrag ? "10000" : "100%",
                     height: this.ChildDrag ? "10000" : "100%",
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5f303b428..1c4d47d57 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -28,7 +28,6 @@ import { CollectionView } from "../CollectionView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
-import { StyleLayers } from "../../StyleProvider";
 import { TreeView } from "../TreeView";
 import { VideoBox } from "../../nodes/VideoBox";
 import { ImageField, WebField } from "../../../../fields/URLField";
@@ -348,7 +347,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
-    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[], makeGroup: Opt<boolean>) => {
+    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
         const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
             Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
@@ -357,7 +356,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             return doc;
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
         newCollection.system = undefined;
-        newCollection._layerTags = new List<string>(layers);
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
         newCollection._isGroup = makeGroup;
@@ -437,7 +435,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             }));
             this.props.removeDocument?.(selected);
         }
-        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, [], group);
+        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -531,7 +529,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const newCollection = this.getCollection([], undefined, [StyleLayers.Background], undefined);
+        const newCollection = this.getCollection([], undefined, undefined);
         this.props.addDocument?.(newCollection);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
@@ -617,7 +615,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 (this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
             }
         };
-        this.props.activeDocuments().filter(doc => this.props.layerProvider?.(doc) !== false && !doc.z).map(selectFunc);
+        this.props.activeDocuments().filter(doc => !doc.z && !doc._lockedPosition).map(selectFunc);
         if (!selection.length && selectBackgrounds) this.props.activeDocuments().filter(doc => doc.z === undefined).map(selectFunc);
         if (!selection.length) this.props.activeDocuments().filter(doc => doc.z !== undefined).map(selectFunc);
         return selection;
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c615bfb8e..bec582dcd 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -155,7 +155,6 @@ export class CollectionLinearView extends CollectionSubView() {
                 renderDepth={this.props.renderDepth + 1}
                 focus={emptyFunction}
                 styleProvider={this.props.styleProvider}
-                layerProvider={this.props.layerProvider}
                 docViewPath={returnEmptyDoclist}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 6929a1cd8..92f9b2f49 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -219,7 +219,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             styleProvider={this.props.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 7e2b83230..4c4054b09 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -218,7 +218,6 @@ export class CollectionMultirowView extends CollectionSubView() {
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             styleProvider={this.props.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index a93762ea4..b731479a5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -417,7 +417,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     docRangeFilters={this.childDocRangeFilters}
                     searchFilterDocs={this.searchFilterDocs}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                     ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 605481ddf..bea5b3be6 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -573,7 +573,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         Document={this._showDoc}
                         DataDoc={this._showDataDoc}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={undefined}
                         docViewPath={returnEmptyDoclist}
                         freezeDimensions={true}
                         focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index c8be9069c..4b33ef8ae 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -67,7 +67,7 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                     <input defaultValue={""} autoComplete="off" type="text" placeholder="Search for Document..." id="search-input"
                         className="linkPopup-searchBox searchBox-input" /> */}
 
-                    <SearchBox 
+                    <SearchBox
                         Document={CurrentUserUtils.MySearchPanelDoc}
                         DataDoc={CurrentUserUtils.MySearchPanelDoc}
                         linkFrom={linkDoc}
@@ -83,7 +83,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         pinToPres={emptyFunction}
                         rootSelected={returnTrue}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={undefined}
                         removeDocument={undefined}
                         ScreenToLocalTransform={Transform.Identity}
                         PanelWidth={this.getPWidth}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index e28e5b453..d97cb6f84 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -654,7 +654,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             className="audiobox-container"
             onContextMenu={this.specificContextMenu}
             onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined}
-            style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined }}
+            style={{ pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined }}
         >
             {!this.path ? this.recordingControls : this.playbackControls}
         </div>;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index edadd59c0..5a0ab9110 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -21,7 +21,6 @@ import React = require("react");
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
     sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined;
-    layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined;
     renderCutoffProvider: (doc: Doc) => boolean;
     zIndex?: number;
     highlight?: boolean;
@@ -159,7 +158,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             ...this.props,
             CollectionFreeFormDocumentView: this.returnThis,
             styleProvider: this.styleProvider,
-            layerProvider: this.props.layerProvider,
             ScreenToLocalTransform: this.screenToLocalTransform,
             PanelWidth: this.panelWidth,
             PanelHeight: this.panelHeight,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f30d093a1..c999b3224 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -37,7 +37,7 @@ import { DocComponent } from "../DocComponent";
 import { EditableView } from '../EditableView';
 import { InkingStroke } from "../InkingStroke";
 import { LightboxView } from "../LightboxView";
-import { StyleLayers, StyleProp } from "../StyleProvider";
+import { StyleProp } from "../StyleProvider";
 import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { DocumentLinksButton } from './DocumentLinksButton';
@@ -123,7 +123,6 @@ export interface DocumentViewSharedProps {
     PanelHeight: () => number;
     docViewPath: () => DocumentView[];
     dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
-    layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean);
     styleProvider: Opt<StyleProviderFunc>;
     focus: DocFocusFunc;
     fitWidth?: (doc: Doc) => boolean;
@@ -474,7 +473,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             let stopPropagate = true;
             let preventDefault = true;
-            !StrListCast(this.props.Document._layerTags).includes(StyleLayers.Background) && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+            (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
             if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (this._timeout) {
                     clearTimeout(this._timeout);
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 78adf63cb..a47e17dfc 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -423,7 +423,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     treeViewHideHeaderFields={false}
                     dontRegisterView={true}
                     styleProvider={this.FilterStyleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={this.props.docViewPath}
                     scriptContext={this.props.scriptContext}
                     moveDocument={returnFalse}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index e7c43e5cd..3b04aa807 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -379,7 +379,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             style={{
                 width: this.props.PanelWidth() ? undefined : `100%`,
                 height: this.props.PanelWidth() ? undefined : `100%`,
-                pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
+                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
                 borderRadius
             }} >
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 881cbf2bb..80def3025 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -60,7 +60,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             docRangeFilters: returnEmptyFilter,
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
-            layerProvider: undefined,
             docViewPath: returnEmptyDoclist,
             ContainingCollectionView: undefined,
             ContainingCollectionDoc: undefined,
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 437d29f39..7fd289a97 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -89,7 +89,6 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     openLinkTargetOnRight = (e: React.MouseEvent) => {
         const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
         alias._isLinkButton = undefined;
-        alias._layerTags = undefined;
         alias.layoutKey = "layout";
         this.props.addDocTab(alias, "add:right");
     }
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 375434933..ba515fb89 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -179,7 +179,6 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             moveDocument={returnFalse}
                             rootSelected={returnFalse}
                             styleProvider={this.props.docProps?.styleProvider}
-                            layerProvider={this.props.docProps?.layerProvider}
                             docViewPath={returnEmptyDoclist}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 3b4c94562..c350e3139 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -809,7 +809,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
         return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
             style={{
-                pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
+                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
                 borderRadius,
                 overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
             }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 20e01be05..445df8ddd 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -74,7 +74,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
     @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
-    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url }
+    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url; }
 
     constructor(props: any) {
         super(props);
@@ -134,7 +134,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         returnedfilename => setTimeout(action(() => {
                             this.lockout = false;
                             this.layoutDoc.thumb = new ImageField(returnedfilename);
-                            this.layoutDoc.thumbScrollTop = scrollTop
+                            this.layoutDoc.thumbScrollTop = scrollTop;
                         }), 500));
                 })
                 .catch(function (error: any) {
@@ -717,7 +717,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
     annotationPointerEvents = () => this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none";
     render() {
-        const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents?.() as any;
+        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 renderAnnotations = (docFilters?: () => string[]) =>
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 364be461f..1d8e3a2cf 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -182,7 +182,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     removeDocument={this.removeDoc}
                     isDocumentActive={returnFalse}
                     isContentActive={this._textBox.props.isContentActive}
-                    layerProvider={this._textBox.props.layerProvider}
                     styleProvider={this._textBox.props.styleProvider}
                     docViewPath={this._textBox.props.docViewPath}
                     ScreenToLocalTransform={this.getDocTransform}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 29117794e..f2a222cee 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1630,7 +1630,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const active = this.props.isContentActive();
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
-        const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
+        const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 820d7ef96..64f5a296f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -700,7 +700,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && this.props.layerProvider?.(this.layoutDoc) !== false) &&
+    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
         (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     /**
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f4dc9b615..ef918d991 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -80,7 +80,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     Document={this.targetDoc}
                     DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     styleProvider={this.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     rootSelected={returnTrue}
                     addDocument={returnFalse}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index fb2e33e2a..5bdce273d 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -26,7 +26,7 @@ export
             {DocListCast(this.props.anno.textInlineAnnotations).map(a =>
                 <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
             )}
-        </div>
+        </div>;
     }
 }
 
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 2905e96d9..152b7bbcb 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -127,7 +127,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     static foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) {
         let newarray: Doc[] = [];
         var depth = 0;
-        let visited: Doc[] = [];
+        const visited: Doc[] = [];
         while (docs.length > 0) {
             newarray = [];
             docs.filter(d => d && !visited.includes(d)).forEach(d => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 9de8d0831..194b3ba27 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1026,7 +1026,7 @@ export namespace Doc {
         if (status === DocBrushStatus.unbrushed) {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
             if (lastBrushed) {
-                for (var link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
+                for (const link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
                     const a1 = Cast(link.anchor1, Doc, null);
                     const a2 = Cast(link.anchor2, Doc, null);
                     if (Doc.AreProtosEqual(a1, doc) || Doc.AreProtosEqual(a2, doc) ||
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 88221732e..418464f0e 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -96,7 +96,6 @@ export class AudioUpload extends React.Component {
                         isDocumentActive={returnTrue}
                         isContentActive={emptyFunction}
                         focus={emptyFunction}
-                        layerProvider={undefined}
                         styleProvider={() => "rgba(0,0,0,0)"}
                         docViewPath={returnEmptyDoclist}
                         whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index cfcc48608..78ec706d7 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -214,7 +214,6 @@ export class MobileInterface extends React.Component {
                     isContentActive={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={this.whitebackground}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index bdea57cb2..fd000a83c 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -195,7 +195,7 @@ function proxyServe(req: any, requrl: string, response: any) {
                 console.log("EROR?: ", e);
             }
         } else req.pipe(request(requrl)).pipe(response);
-    }
+    };
     retrieveHTTPBody = () => {
         req.headers.cookie = "";
         req.pipe(request(requrl))
-- 
cgit v1.2.3-70-g09d2


From 444f5b91f28e12ab9d48c5445eb02021a6a563a1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 15 May 2022 11:32:35 -0400
Subject: fixed enterting titles in tab's to not trigger inconify.  added drop
 placement for mulitirow/col.  added time delay for switching to tab dragging.
  all this helps support a worktop-style topBar.

---
 src/client/util/DragManager.ts                     | 27 ++++++++++-----
 src/client/views/collections/TabDocView.tsx        |  3 ++
 .../CollectionMulticolumnView.tsx                  | 39 +++++++++++++++++++---
 .../CollectionMultirowView.tsx                     | 38 ++++++++++++++++++---
 4 files changed, 89 insertions(+), 18 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 12c0fe6c0..037da132b 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -449,20 +449,27 @@ export namespace DragManager {
             SnappingManager.clearSnapLines();
             batch.end();
         });
-        const moveHandler = async (e: PointerEvent) => {
+        var startWindowDragTimer: any;
+        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;
             }
             if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
-                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")) {
-                        dragData.removeDocument?.(dragData.draggedDocuments[0]);
-                    }
-                });
+                if (!startWindowDragTimer) startWindowDragTimer = setTimeout(async () => {
+                    startWindowDragTimer = undefined;
+                    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")) {
+                            dragData.removeDocument?.(dragData.draggedDocuments[0]);
+                        }
+                    });
+                }, 500);
+            } else {
+                clearTimeout(startWindowDragTimer);
+                startWindowDragTimer = undefined;
             }
 
             const target = document.elementFromPoint(e.x, e.y);
@@ -528,6 +535,8 @@ export namespace DragManager {
             );
         };
         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);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 73e5a9178..1310a582c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -83,6 +83,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
             titleEle.size = StrCast(doc.title).length + 3;
             titleEle.value = doc.title;
+            titleEle.onkeydown = (e: KeyboardEvent) => {
+                e.stopPropagation();
+            }
             titleEle.onchange = undoBatch(action((e: any) => {
                 titleEle.size = e.currentTarget.value.length + 3;
                 Doc.GetProto(doc).title = e.currentTarget.value;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 92f9b2f49..3582718a3 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,10 +1,10 @@
 import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
-import { Doc } from '../../../../fields/Doc';
+import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse } from '../../../../Utils';
+import { emptyFunction, returnFalse, returnTrue } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -194,11 +194,38 @@ export class CollectionMulticolumnView extends CollectionSubView() {
     @undoBatch
     @action
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
-        if (super.onInternalDrop(e, de)) {
+        let dropInd = -1;
+        if (de.complete.docDragData && this._mainCont) {
+            let curInd = -1;
             de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                d._dimUnit = "*";
-                d._dimMagnitude = 1;
+                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") {
+                        dropInd = Math.floor(index / 2);
+                    } else {
+                        dropInd = Math.ceil(index / 2 + (de.x - brect.x > brect.width / 2 ? 0 : -1));
+                    }
+                }
+            })
+            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--;
+                        }
+                        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;
     }
@@ -214,6 +241,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -232,6 +260,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
+            isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 4c4054b09..95456cf46 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,7 +1,7 @@
 import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
-import { Doc } from '../../../../fields/Doc';
+import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnFalse } from '../../../../Utils';
@@ -190,14 +190,42 @@ export class CollectionMultirowView extends CollectionSubView() {
         return Transform.Identity(); // type coersion, this case should never be hit
     }
 
+
     @undoBatch
     @action
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
-        if (super.onInternalDrop(e, de)) {
+        let dropInd = -1;
+        if (de.complete.docDragData && this._mainCont) {
+            let curInd = -1;
             de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
-                d._dimUnit = "*";
-                d._dimMagnitude = 1;
+                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") {
+                        dropInd = Math.floor(index / 2);
+                    } else {
+                        dropInd = Math.ceil(index / 2 + (de.y - brect.y > brect.height / 2 ? 0 : -1));
+                    }
+                }
+            })
+            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--;
+                        }
+                        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;
     }
@@ -213,6 +241,7 @@ export class CollectionMultirowView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
         return <DocumentView
             Document={layout}
@@ -230,6 +259,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
+            isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             isContentActive={emptyFunction}
-- 
cgit v1.2.3-70-g09d2


From ee1fa0ad22f1643888e4c467c6f8df837cc69178 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 17 May 2022 10:18:45 -0400
Subject: fixed pointerevents on treview open icon.  added browse mode for
 freeformcollections

---
 src/client/util/DragManager.ts                     | 24 ++++++++++++----------
 src/client/views/collections/TabDocView.tsx        |  2 +-
 src/client/views/collections/TreeView.tsx          | 13 ++++++++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 23 ++++++++++++++++-----
 .../CollectionMulticolumnView.tsx                  |  2 +-
 .../CollectionMultirowView.tsx                     |  2 +-
 6 files changed, 43 insertions(+), 23 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 037da132b..5a00e0c04 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -456,17 +456,19 @@ export namespace DragManager {
                 dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction;
             }
             if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
-                if (!startWindowDragTimer) startWindowDragTimer = setTimeout(async () => {
-                    startWindowDragTimer = undefined;
-                    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")) {
-                            dragData.removeDocument?.(dragData.draggedDocuments[0]);
-                        }
-                    });
-                }, 500);
+                if (!startWindowDragTimer) {
+                    startWindowDragTimer = setTimeout(async () => {
+                        startWindowDragTimer = undefined;
+                        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")) {
+                                dragData.removeDocument?.(dragData.draggedDocuments[0]);
+                            }
+                        });
+                    }, 500);
+                }
             } else {
                 clearTimeout(startWindowDragTimer);
                 startWindowDragTimer = undefined;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 1310a582c..cebc3f779 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -85,7 +85,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             titleEle.value = doc.title;
             titleEle.onkeydown = (e: KeyboardEvent) => {
                 e.stopPropagation();
-            }
+            };
             titleEle.onchange = undoBatch(action((e: any) => {
                 titleEle.size = e.currentTarget.value.length + 3;
                 Doc.GetProto(doc).title = e.currentTarget.value;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index d608f800c..c004b4d49 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -494,10 +494,15 @@ export class TreeView extends React.Component<TreeViewProps> {
         return <div className={`bullet${this.props.treeView.outlineMode ? "-outline" : ""}`} key={"bullet"}
             title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
             onClick={this.bulletClick}
-            style={this.props.treeView.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : {
-                color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
-                opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4
-            }}>
+            style={this.props.treeView.outlineMode ?
+                {
+                    opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity)
+                } :
+                {
+                    pointerEvents: this.props.isContentActive() ? "all" : undefined,
+                    opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4,
+                    color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
+                }}>
             {this.props.treeView.outlineMode ?
                 !(this.doc.text as RichTextField)?.Text ? (null) :
                     <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"]} /> :
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a60253914..68a51ef69 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -53,6 +53,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -154,7 +155,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action setKeyFrameEditing = (set: boolean) => this._keyframeEditing = set;
     getKeyFrameEditing = () => this._keyframeEditing;
-    onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
+    onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick, ScriptCast(this.layoutDoc.onBrowseClick));
     onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
     elementFunc = () => this._layoutElements;
     shrinkWrap = () => {
@@ -621,7 +622,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     onClick = (e: React.MouseEvent) => {
-        if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) {
+
+        if (this.layoutDoc.onBrowseClick) {
+            this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1);
+            e.stopPropagation();
+            e.preventDefault();
+        }
+        else if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) {
             if (e.shiftKey) {
                 if (Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a shift + double click 
                     this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1);
@@ -1127,7 +1134,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
 
         if (scale) {
-            const maxZoom = 2; // 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 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
             return {
                 panX: (bounds.left + bounds.right) / 2,
                 panY: (bounds.top + bounds.bot) / 2,
@@ -1549,7 +1556,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-
+        viewCtrlItems.push({ description: (this.layoutDoc.onChildClick ? "Leave" : "Enter") + " Browse Mode", event: () => this.layoutDoc.onBrowseClick = this.layoutDoc.onBrowseClick ? undefined : ScriptField.MakeScript("CollectionBrowseClick(documentView)", { documentView: "any" }), icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
         !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
@@ -1973,4 +1980,10 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
                 }
             }} />;
     }
-}
\ No newline at end of file
+}
+
+export function CollectionBrowseClick(dv: DocumentView) {
+    dv.props.focus(dv.props.Document, { willZoom: true });
+    Doc.linkFollowHighlight(dv?.props.Document, false);
+}
+ScriptingGlobals.add(CollectionBrowseClick);
\ No newline at end of file
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index c18b4cec7..b6b59a2d6 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -212,7 +212,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                         dropInd = Math.ceil(index / 2 + (de.x - brect.x > brect.width / 2 ? 0 : -1));
                     }
                 }
-            })
+            });
             if (super.onInternalDrop(e, de)) {
                 de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
                     d._dimUnit = "*";
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 95456cf46..4074f8d69 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -212,7 +212,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                         dropInd = Math.ceil(index / 2 + (de.y - brect.y > brect.height / 2 ? 0 : -1));
                     }
                 }
-            })
+            });
             if (super.onInternalDrop(e, de)) {
                 de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
                     d._dimUnit = "*";
-- 
cgit v1.2.3-70-g09d2


From 51199aae80013073d4f1a0749a36051dcee591c1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 20 May 2022 10:48:04 -0400
Subject: moved explore mode to topBar.  fixed highlighting of overlay toggle. 
 fixed pointerEvents on lockedPosition collectoins that are not children of a
 free form view.

---
 src/client/util/CurrentUserUtils.ts                | 10 ++++----
 src/client/views/MainView.tsx                      | 15 ++++++------
 src/client/views/StyleProvider.tsx                 | 20 +++++++---------
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        |  4 +++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +-----
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/button/ButtonScripts.ts     |  4 +++-
 src/client/views/topbar/TopBar.tsx                 | 28 +++++++++++++++-------
 10 files changed, 50 insertions(+), 44 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 78bf531c6..8fed6e940 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1009,7 +1009,7 @@ export class CurrentUserUtils {
                 title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
                 script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()',
             }, // Only when a document is selected
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", backgroundColor: 'toggleOverlay(true)', click: 'toggleOverlay()', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
             { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
             { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available
@@ -1023,7 +1023,7 @@ export class CurrentUserUtils {
         if (doc.contextMenuBtns === undefined) {
             const docList: Doc[] = [];
 
-            (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
+            (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, backgroundColor, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
                 const menuDocList: Doc[] = [];
                 if (subMenu) {
                     // default is textTools
@@ -1046,7 +1046,7 @@ export class CurrentUserUtils {
                             break;
                     }
                     tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle }) => {
-                        const computed = click ? ComputedField.MakeFunction(click) as any : "transparent";
+                        const backgroundColor = click ? ComputedField.MakeFunction(click) as any : "transparent";
                         menuDocList.push(Docs.Create.FontIconDocument({
                             _nativeWidth: width ? width : 25,
                             _nativeHeight: 25,
@@ -1069,7 +1069,7 @@ export class CurrentUserUtils {
                             title,
                             switchToggle,
                             color: Colors.WHITE,
-                            backgroundColor: computed,
+                            backgroundColor,
                             _dropAction: "alias",
                             _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
                             onClick: click ? ScriptField.MakeScript(click) : undefined
@@ -1105,7 +1105,7 @@ export class CurrentUserUtils {
                         dontUndo: true,
                         title,
                         color: Colors.WHITE,
-                        // backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult, {}, {_readOnly_:true}) as any : "transparent",
+                        backgroundColor: backgroundColor ? ComputedField.MakeFunction(backgroundColor) as any : "transparent",
                         _dropAction: "alias",
                         hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
                         _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2e1d10955..9be00fa9e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -273,6 +273,12 @@ export class MainView extends React.Component {
         Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
     }
 
+    @observable _exploreMode = false;
+    @computed get exploreMode() {
+        return () => this._exploreMode ? ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)",
+            { documentView: "any", clientX: "number", clientY: "number" })! : undefined;
+    }
+
     @computed get mainDocView() {
         return <DocumentView key="main"
             Document={this.mainContainer!}
@@ -571,13 +577,6 @@ export class MainView extends React.Component {
         </svg>;
     }
 
-    @computed get topbar() {
-        TraceMobx();
-        return <div className="mainView-topbar">
-            <TopBar />
-        </div>;
-    }
-
     @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
         return !DocumentLinksButton.invisibleWebDoc ? null :
             <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
@@ -625,7 +624,7 @@ export class MainView extends React.Component {
             <GoogleAuthenticationManager />
             <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
             <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
-            {this.topbar}
+            <TopBar />
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 93da2fa19..7ac40fc8e 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -156,15 +156,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
                     docColor = docColor ||
                         (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
-                            isBackground() ? "cyan" : // ?? is there a good default for a background collection
-                                doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
-                                    (doc?._isGroup ? undefined :
-                                        StrCast((props?.renderDepth || 0) > 0 ?
-                                            Doc.UserDoc().activeCollectionNestedBackground :
-                                            Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ?
-                                                Colors.BLACK :
-                                                "linear-gradient(#065fff, #85c1f9)"))
-                                    ));
+                            doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
+                                (doc?._isGroup ? undefined :
+                                    StrCast((props?.renderDepth || 0) > 0 ?
+                                        Doc.UserDoc().activeCollectionNestedBackground :
+                                        Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ?
+                                            Colors.BLACK :
+                                            "linear-gradient(#065fff, #85c1f9)"))
+                                ));
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
@@ -203,7 +202,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             return undefined;
         case StyleProp.Decorations:
             if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) {
-                return doc && (isBackground() || selected) && (props?.renderDepth || 0) > 0 &&
+                return doc && (isBackground() || selected) && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 &&
                     ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
                     <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                         <FontAwesomeIcon icon={isBackground() ? "lock" : "unlock"} style={{ color: isBackground() ? "red" : undefined }} size="lg" />
@@ -236,7 +235,6 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps
                         DocFocusOrOpen(doc, props?.ContainingCollectionDoc);
                     }
                 })}
-                {DashboardToggleButton(doc, "lockedPosition", "lock", "unlock")}
             </>;
     }
     return DefaultStyleProvider(doc, props, property);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7c5f49b42..19485a0eb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -261,7 +261,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             CollectionView: this,
         };
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: this.rootDoc._lockedPosition ? "none" : undefined }}>
+            style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? "none" : undefined }}>
             {this.showIsTagged()}
             {this.renderSubView(this.collectionViewType, props)}
         </div>);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index cebc3f779..43f31813c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -9,6 +9,7 @@ import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
 import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
 import { FieldId } from "../../../fields/RefField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
 import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
@@ -24,6 +25,7 @@ import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
+import { MainView } from '../MainView';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
@@ -33,7 +35,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
-import { List } from '../../../fields/List';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -354,6 +355,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
+                onBrowseClick={MainView.Instance.exploreMode}
                 isContentActive={returnTrue}
                 PanelWidth={this.PanelWidth}
                 PanelHeight={this.PanelHeight}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 916cc9c05..df09569ef 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -624,10 +624,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     onClick = (e: React.MouseEvent) => {
         if (this.onBrowseClickHandler()) {
-            // if (this.Document[this.scaleFieldKey] !== 0.5) {
-            //     this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 0.5);
-            // }
-            // else 
             if (this.props.DocumentView?.()) {
                 this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView(), clientX: e.clientX, clientY: e.clientY });
             }
@@ -961,7 +957,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._lockedTransform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -1564,7 +1560,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-        viewCtrlItems.push({ description: (this.layoutDoc.onBrowseClick ? "Leave" : "Enter") + " Browse Mode", event: () => this.layoutDoc.onBrowseClick = this.layoutDoc.onBrowseClick ? undefined : ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)", { documentView: "any", clientX: "number", clientY: "number" }), icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
         !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1a74a4500..f5ad47ca7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -177,7 +177,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     onDoubleClick?: () => ScriptField;
     onPointerDown?: () => ScriptField;
     onPointerUp?: () => ScriptField;
-    onBrowseClick?: () => ScriptField;
+    onBrowseClick?: () => (ScriptField | undefined);
 }
 
 // these props are only available in DocumentViewIntenral
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 33ed8cd89..94937430e 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -24,7 +24,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     isSelected: (outsideReaction?: boolean) => boolean;
     scaling?: () => number;
     setHeight?: (height: number) => void;
-    onBrowseClick?: () => ScriptField;
+    onBrowseClick?: () => (ScriptField | undefined);
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     pointerEvents?: () => Opt<string>;
diff --git a/src/client/views/nodes/button/ButtonScripts.ts b/src/client/views/nodes/button/ButtonScripts.ts
index f3731b8f9..b4a382faf 100644
--- a/src/client/views/nodes/button/ButtonScripts.ts
+++ b/src/client/views/nodes/button/ButtonScripts.ts
@@ -1,5 +1,6 @@
 import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from "../../../util/SelectionManager";
+import { Colors } from "../../global/globalEnums";
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function changeView(view: string) {
@@ -8,7 +9,8 @@ ScriptingGlobals.add(function changeView(view: string) {
 });
 
 // toggle: Set overlay status of selected document
-ScriptingGlobals.add(function toggleOverlay() {
+ScriptingGlobals.add(function toggleOverlay(readOnly?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+    if (readOnly) return selected?.Document.z ? Colors.MEDIUM_BLUE : "transparent";
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("failed");
 });
\ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0af7de6af..be248ab92 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -1,4 +1,6 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
+import { action } from "mobx";
 import { observer } from "mobx-react";
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
@@ -9,6 +11,7 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { SettingsManager } from "../../util/SettingsManager";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { Borders, Colors } from "../global/globalEnums";
+import { MainView } from "../MainView";
 import "./TopBar.scss";
 
 /**
@@ -40,20 +43,28 @@ export class TopBar extends React.Component {
                             </select>
                         </div>
                         <div className="topbar-dashboards">
-                            <div className="topbar-icon" onClick={async () => {
+                            <Tooltip title={<div className="dash-tooltip">Create a new dashboard </div>} placement="bottom"><div className="topbar-icon" onClick={async () => {
                                 const batch = UndoManager.StartBatch("new dash");
                                 await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
                                 batch.end();
                             }}>
                                 {"New"}<FontAwesomeIcon icon="plus" />
                             </div>
-                            {<div className="topbar-icon" onClick={async () => {
-                                const batch = UndoManager.StartBatch("snapshot");
-                                await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
-                                batch.end();
-                            }}>
-                                {"Snapshot"}<FontAwesomeIcon icon="camera" />
-                            </div>}
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">Work on a copy of the dashboard layout</div>} placement="bottom">
+                                <div className="topbar-icon" onClick={async () => {
+                                    const batch = UndoManager.StartBatch("snapshot");
+                                    await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
+                                    batch.end();
+                                }}>
+                                    {"Snapshot"}<FontAwesomeIcon icon="camera" />
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
+                                <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
+                                    {"Explore"}<FontAwesomeIcon icon="map" />
+                                </div>
+                            </Tooltip>
                         </div>
                     </div>
                     <div className="topbar-right" >
@@ -64,7 +75,6 @@ export class TopBar extends React.Component {
                         <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
                             {"Settings"}<FontAwesomeIcon icon="cog" />
                         </div>
-
                     </div>
                 </div>
             </div >
-- 
cgit v1.2.3-70-g09d2


From 414b56260c55f5ea7cac43dad83b41688fb924b5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 May 2022 20:34:07 -0400
Subject: added headerBar for closed tabs and other docs. extended multirow/col
 to support hiding chile doc decorations and for making child content/document
 active work better.  changed background style to work better for sub
 collections.

---
 src/client/util/CurrentUserUtils.ts                |   3 +
 src/client/views/DocumentDecorations.tsx           |   7 +-
 src/client/views/MainView.tsx                      | 115 +++++++++++++++------
 src/client/views/StyleProvider.tsx                 |   6 +-
 src/client/views/collections/CollectionMenu.tsx    |  17 ++-
 src/client/views/collections/CollectionView.tsx    |   6 +-
 src/client/views/collections/TabDocView.tsx        |   2 +
 .../CollectionMulticolumnView.tsx                  |  11 +-
 .../CollectionMultirowView.tsx                     |  11 +-
 src/client/views/nodes/DocumentView.tsx            |   6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   1 -
 11 files changed, 125 insertions(+), 60 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1ebb5365a..1e42b9073 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -70,6 +70,7 @@ export class CurrentUserUtils {
     @observable public static GuestDashboard: Doc | undefined;
     @observable public static GuestMobile: Doc | undefined;
     @observable public static propertiesWidth: number = 0;
+    @observable public static headerBarHeight: number = 0;
     @observable public static searchPanelWidth: number = 0;
 
     // sets up the default User Templates - slideView,  headerView
@@ -1003,6 +1004,7 @@ export class CurrentUserUtils {
         doc.savedFilters = new List<Doc>();
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
+        doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true });
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupImportSidebar(doc); // sets up the import sidebar
@@ -1210,6 +1212,7 @@ export class CurrentUserUtils {
     public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); }
     public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); }
     public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }
+    public static get MyHeaderBarDoc() { return Cast(Doc.UserDoc().myHeaderBarDoc, Doc, null); }
     public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
     public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
     public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ae3a91c4d..29e088143 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -511,11 +511,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
 
-        let inMainMenuPanel = false;
-        for (let node = seldoc.ContentDiv; node && !inMainMenuPanel; node = node?.parentNode as any) {
-            if (node.className === "mainView-mainContent") inMainMenuPanel = true;
-        }
-        const leftBounds = inMainMenuPanel ? 0 : this.props.boundsLeft;
+
+        const leftBounds = this.props.boundsLeft;
         const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop;
         bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
         bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 09a57843c..69e394790 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -33,7 +33,7 @@ import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOpti
 import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
 import { TreeViewType } from './collections/CollectionTreeView';
-import { CollectionViewType } from './collections/CollectionView';
+import { CollectionView, CollectionViewType } from './collections/CollectionView';
 import "./collections/TreeView.scss";
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
@@ -81,14 +81,19 @@ export class MainView extends React.Component {
 
     @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
     @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
-    @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); }
     @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
     @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
     @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
+    @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
 
+    headerBarDocWidth = () => this._dashUIWidth;
+    headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0;
     topMenuHeight = () => 35;
     topMenuWidth = returnZero; // value is ignored ...
     leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", ""));
@@ -98,7 +103,7 @@ export class MainView extends React.Component {
     propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
     propertiesHeight = () => this._dashUIHeight;
     mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
-    mainDocViewHeight = () => this._dashUIHeight;
+    mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight();
 
     componentDidMount() {
         document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
@@ -177,8 +182,8 @@ export class MainView extends React.Component {
         fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
         fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
         fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
-        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
-        fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
+        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, fa.faAngleDoubleDown, fa.faAngleDoubleLeft, fa.faAngleDoubleUp, faBuffer as any, fa.faExpand, fa.faUndo,
+        fa.faSlidersH, fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
         fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
         fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
         fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
@@ -276,33 +281,67 @@ export class MainView extends React.Component {
         return () => this._exploreMode ? ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)",
             { documentView: "any", clientX: "number", clientY: "number" })! : undefined;
     }
-
+    headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
+
+    headerContentActive = () => SnappingManager.GetIsDragging() ? false : true;
+    @computed get headerBarDocView() {
+        console.log(this.headerBarDoc)
+        return <div style={{ height: this.headerBarDocHeight() }}>
+            <DocumentView key="headerBarDoc"
+                Document={this.headerBarDoc}
+                DataDoc={undefined}
+                addDocument={undefined}
+                addDocTab={this.addDocTabFunc}
+                pinToPres={emptyFunction}
+                docViewPath={returnEmptyDoclist}
+                styleProvider={DefaultStyleProvider}
+                rootSelected={returnTrue}
+                removeDocument={returnFalse}
+                fitContentsToDoc={returnTrue}
+                isContentActive={this.headerContentActive}
+                ScreenToLocalTransform={this.headerBarScreenXf}
+                hideResizeHandles={true}
+                PanelWidth={this.headerBarDocWidth}
+                PanelHeight={this.headerBarDocHeight}
+                renderDepth={0}
+                focus={DocUtils.DefaultFocus}
+                whenChildContentsActiveChanged={emptyFunction}
+                bringToFront={emptyFunction}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={undefined}
+            /></div>;
+    }
     @computed get mainDocView() {
-        return <DocumentView key="main"
-            Document={this.mainContainer!}
-            DataDoc={undefined}
-            addDocument={undefined}
-            addDocTab={this.addDocTabFunc}
-            pinToPres={emptyFunction}
-            docViewPath={returnEmptyDoclist}
-            styleProvider={undefined}
-            rootSelected={returnTrue}
-            isContentActive={returnTrue}
-            removeDocument={undefined}
-            ScreenToLocalTransform={Transform.Identity}
-            PanelWidth={this.mainDocViewWidth}
-            PanelHeight={this.mainDocViewHeight}
-            focus={DocUtils.DefaultFocus}
-            whenChildContentsActiveChanged={emptyFunction}
-            bringToFront={emptyFunction}
-            docFilters={returnEmptyFilter}
-            docRangeFilters={returnEmptyFilter}
-            searchFilterDocs={returnEmptyDoclist}
-            ContainingCollectionView={undefined}
-            ContainingCollectionDoc={undefined}
-            suppressSetHeight={true}
-            renderDepth={-1}
-        />;
+        return <>
+            {this.headerBarDocView}
+            <DocumentView key="main"
+                Document={this.mainContainer!}
+                DataDoc={undefined}
+                addDocument={undefined}
+                addDocTab={this.addDocTabFunc}
+                pinToPres={emptyFunction}
+                docViewPath={returnEmptyDoclist}
+                styleProvider={undefined}
+                rootSelected={returnTrue}
+                isContentActive={returnTrue}
+                removeDocument={undefined}
+                ScreenToLocalTransform={Transform.Identity}
+                PanelWidth={this.mainDocViewWidth}
+                PanelHeight={this.mainDocViewHeight}
+                focus={DocUtils.DefaultFocus}
+                whenChildContentsActiveChanged={emptyFunction}
+                bringToFront={emptyFunction}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={undefined}
+                suppressSetHeight={true}
+                renderDepth={-1}
+            /></>;
     }
 
     @computed get dockingContent() {
@@ -331,7 +370,7 @@ export class MainView extends React.Component {
             this.closeFlyout);
     }
 
-    sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1);
+    sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
     addDocTabFunc = (doc: Doc, location: string): boolean => {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
@@ -461,6 +500,16 @@ export class MainView extends React.Component {
         </>;
     }
 
+    @computed get headerBar() {
+        return !this.userDoc ? (null) :
+            <div className="mainView-dashboardArea" style={{
+                height: this.headerBarDocHeight(),
+                width: "100%",
+            }} >
+                {this.headerBarDocView}
+            </div>;
+    }
+
     @computed get mainDashboardArea() {
         return !this.userDoc ? (null) :
             <div className="mainView-dashboardArea" ref={r => {
@@ -620,7 +669,7 @@ export class MainView extends React.Component {
             <CaptureManager />
             <GroupManager />
             <GoogleAuthenticationManager />
-            <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
+            <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfHeaderBarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
             <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
             <TopBar />
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 7ac40fc8e..c44443264 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -158,12 +158,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                         (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                             doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
                                 (doc?._isGroup ? undefined :
-                                    StrCast((props?.renderDepth || 0) > 0 ?
+                                    Cast((props?.renderDepth || 0) > 0 ?
                                         Doc.UserDoc().activeCollectionNestedBackground :
-                                        Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ?
+                                        Doc.UserDoc().activeCollectionBackground, "string") ?? (darkScheme() ?
                                             Colors.BLACK :
                                             "linear-gradient(#065fff, #85c1f9)"))
-                                ));
+                        );
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index c322d9e16..96029a8e4 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -84,11 +84,11 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
     }
 
     @action
-    toggleProperties = () => {
-        if (CurrentUserUtils.propertiesWidth > 0) {
-            CurrentUserUtils.propertiesWidth = 0;
+    toggleTopBar = () => {
+        if (CurrentUserUtils.headerBarHeight > 0) {
+            CurrentUserUtils.headerBarHeight = 0;
         } else {
-            CurrentUserUtils.propertiesWidth = 250;
+            CurrentUserUtils.headerBarHeight = 60;
         }
     }
 
@@ -137,14 +137,13 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
 
     render() {
 
-        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
-        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel";
+        const propIcon = CurrentUserUtils.headerBarHeight > 0 ? "angle-double-up" : "angle-double-down";
+        const propTitle = CurrentUserUtils.headerBarHeight > 0 ? "Close Header Bar" : "Open Header Bar";
 
-        const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="properties" placement="bottom">
+        const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="topar" placement="bottom">
             <div className="collectionMenu-hardCodedButton"
                 style={{ backgroundColor: CurrentUserUtils.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }}
-                key="properties"
-                onPointerDown={this.toggleProperties}>
+                onPointerDown={this.toggleTopBar}>
                 <FontAwesomeIcon icon={propIcon} size="lg" />
             </div>
         </Tooltip>;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 19485a0eb..4f92e305e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -6,7 +6,7 @@ import { Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { ObjectField } from '../../../fields/ObjectField';
 import { ScriptField } from '../../../fields/ScriptField';
-import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { returnEmptyString } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
@@ -237,6 +237,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
     bodyPanelWidth = () => this.props.PanelWidth();
 
+    childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
+    childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
     @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); }
 
@@ -258,6 +260,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             ScreenToLocalTransform: this.screenToLocalTransform,
             childLayoutTemplate: this.childLayoutTemplate,
             childLayoutString: this.childLayoutString,
+            childHideResizeHandles: this.childHideResizeHandles,
+            childHideDecorationTitle: this.childHideDecorationTitle,
             CollectionView: this,
         };
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 43f31813c..0070d0b9d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -104,6 +104,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 closeWrap.id = "lm_closeWrap";
                 closeWrap.onclick = (e: MouseEvent) => {
                     tab.header.parent.contentItem.remove();
+                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
                     Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
                 };
                 const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
@@ -179,6 +180,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             tab.closeElement.off('click') //unbind the current click handler
                 .click(function () {
                     Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", doc);
                     Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true);
                     SelectionManager.DeselectAll();
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b6b59a2d6..66cc3613d 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -241,7 +241,9 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isContentActive = () => this.props.isSelected() ? true : undefined;
+    isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined;
+    isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive();
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -252,7 +254,11 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             LayoutTemplateString={this.props.childLayoutString}
             freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
-            isContentActive={emptyFunction}
+            isContentActive={this.isChildContentActive}
+            isDocumentActive={this.isChildDocumentActive}
+            hideResizeHandles={this.props.childHideResizeHandles?.()}
+            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+            fitContentsToDoc={this.props.fitContentsToDoc}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
@@ -261,7 +267,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             onDoubleClick={this.onChildDoubleClickHandler}
             suppressSetHeight={true}
             ScreenToLocalTransform={dxf}
-            isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 4074f8d69..8218c49b5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -241,7 +241,9 @@ export class CollectionMultirowView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isContentActive = () => this.props.isSelected() ? true : undefined;
+    isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined;
+    isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive();
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
         return <DocumentView
             Document={layout}
@@ -259,10 +261,13 @@ export class CollectionMultirowView extends CollectionSubView() {
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
-            isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+            isContentActive={this.isChildContentActive}
+            isDocumentActive={this.isChildDocumentActive}
+            hideResizeHandles={this.props.childHideResizeHandles?.()}
+            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+            fitContentsToDoc={this.props.fitContentsToDoc}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
-            isContentActive={emptyFunction}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f7312e59c..54bb66f7f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -747,7 +747,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     const alias = Doc.MakeAlias(this.rootDoc);
                     alias.layout = FormattedTextBox.LayoutString("newfield");
                     alias.title = "newfield";
-                    alias._yMargin = 10;
                     alias._height = 35;
                     alias._width = 100;
                     alias.syncLayoutFieldWithTitle = true;
@@ -881,7 +880,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: this.props.pointerEvents?.() as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
+                pointerEvents: this.props.pointerEvents?.() as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" :
+                    this.props.contentPointerEvents as any ? this.props.contentPointerEvents as any :
+                        this.rootDoc.type !== DocumentType.INK && this.isContentActive() ? "all" :
+                            "none",
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             {!this._retryThumb || !this.thumbShown() ? (null) :
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0e29461a6..822bc996b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -635,7 +635,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }), icon: icon
             });
         });
-        !Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
         const highlighting: ContextMenuProps[] = [];
         const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others"];
         const expertHighlighting = [...noviceHighlighting, "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"];
-- 
cgit v1.2.3-70-g09d2


From d50b851d8e92797d214fc35cf243bfb29e970b36 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 May 2022 21:58:26 -0400
Subject: simplified dashboards by getting rid of confusing
 on-screen/off-screen tabs.  Now off screen tabs appear in the header bar
 which is not currently specific to a dashboard.

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/CurrentUserUtils.ts                | 33 +--------------
 src/client/views/MainView.tsx                      |  4 +-
 .../views/collections/CollectionDockingView.tsx    | 48 +++++++++++++++++-----
 src/client/views/collections/TabDocView.tsx        |  3 +-
 5 files changed, 43 insertions(+), 49 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6fa467368..c3b6953b5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -905,9 +905,7 @@ export namespace Docs {
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            const tabs = TreeDocument(documents, { title: "On-Screen Tabs", childDontRegisterViews: true, freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", _fitWidth: true, system: true, isFolder: true });
-            const all = TreeDocument([], { title: "Off-Screen Tabs", childDontRegisterViews: true, freezeChildren: "add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", system: true, isFolder: true });
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
 
         export function DirectoryImportDocument(options: DocumentOptions = {}) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1e42b9073..9212d8ed9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1172,22 +1172,8 @@ export class CurrentUserUtils {
         freeformDoc.context = dashboardDoc;
 
         // switching the tabs from the datadoc to the regular doc
-        const dashboardTabs = dashboardDoc[DataSym].data;
-        dashboardDoc[DataSym].data = new List<Doc>();
-        dashboardDoc.data = dashboardTabs;
-
-        // collating all docs on the dashboard to make a data-all field
-        const allDocs = new List<Doc>();
-        const allDocs2 = new List<Doc>(); // Array.from, spread, splice all cause so stack or acl issues for some reason
-        DocListCast(dashboardTabs).forEach(doc => {
-            const tabDocs = DocListCast(doc.data);
-            allDocs.push(...tabDocs);
-            allDocs2.push(...tabDocs);
-        });
-        dashboardDoc[DataSym]["data-all"] = allDocs;
-        dashboardDoc["data-all"] = allDocs2;
-        DocListCast(dashboardDoc.data).forEach(doc => doc.dashboard = dashboardDoc);
-        DocListCast(dashboardDoc.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any;
+        const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
+        dashboardDoc.data = new List<Doc>(dashboardTabs);
 
         userDoc.activePresentation = presentation;
 
@@ -1258,21 +1244,6 @@ ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
 },
     "adds Dashboard to set of Dashboards");
 
-/**
- * Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard.
- */
-ScriptingGlobals.add(function dynamicOffScreenDocs(dashboard: Doc) {
-    if (dashboard[DataSym] instanceof Doc) {
-        const allDocs = DocListCast(dashboard["data-all"]);
-        const onScreenTab = DocListCast(dashboard.data)[0];
-        const onScreenDocs = DocListCast(onScreenTab.data);
-        return new List<Doc>(allDocs.reduce((result: Doc[], doc) => {
-            !onScreenDocs.includes(doc) && !onScreenDocs.includes(doc.aliasOf as Doc) && (result.push(doc));
-            return result;
-        }, []));
-    }
-    return [];
-});
 ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
     let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined;
     if (selected && checkParent) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 69e394790..f3dc2cf85 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -92,7 +92,7 @@ export class MainView extends React.Component {
     @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
 
-    headerBarDocWidth = () => this._dashUIWidth;
+    headerBarDocWidth = () => this.mainDocViewWidth();
     headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0;
     topMenuHeight = () => 35;
     topMenuWidth = returnZero; // value is ignored ...
@@ -102,7 +102,7 @@ export class MainView extends React.Component {
     leftMenuFlyoutHeight = () => this._dashUIHeight;
     propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
     propertiesHeight = () => this._dashUIHeight;
-    mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
+    mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth() - this.leftMenuFlyoutWidth();
     mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight();
 
     componentDidMount() {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 30f60bba6..1d65256a6 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -81,6 +81,8 @@ export class CollectionDockingView extends CollectionSubView() {
 
     tabItemDropped = () => DragManager.CompleteWindowDrag?.(false);
     tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => {
+        const dashDoc = proxy?._contentItem?.tab?.DashDoc as Doc;
+        dashDoc && (DragManager.DocDragData = new DragManager.DocumentDragData([proxy._contentItem.tab.DashDoc]));
         DragManager.CompleteWindowDrag = (aborted: boolean) => {
             if (aborted) {
                 proxy._dragListener.AbortDrag();
@@ -90,6 +92,8 @@ export class CollectionDockingView extends CollectionSubView() {
                     this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout)
                 }
                 DragManager.CompleteWindowDrag = undefined;
+            } else {
+                DragManager.DocDragData?.droppedDocuments.forEach(doc => this.addToTabDocList(doc));
             }
             finishDrag?.(aborted);
         };
@@ -100,12 +104,43 @@ export class CollectionDockingView extends CollectionSubView() {
         this.stateChanged();
     }
 
+    addToTabDocList = (document: Doc) => {
+        const instance = CollectionDockingView.Instance;
+        if (instance) {
+            const docList = DocListCast(instance.props.Document[DataSym].data);
+            // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases
+            !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data", document);
+            // adds an alias of the doc to the data-all field of the layoutdocs of the aliases
+            DocListCast(instance.props.Document.aliases).forEach(alias => {
+                const aliasDocList = DocListCast(alias.data);
+                // if aliasDocList contains the alias, don't do anything
+                // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias
+                !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
+            });
+        }
+    }
+    removeFromTabDocList = (document: Doc) => {
+        const instance = CollectionDockingView.Instance;
+        if (instance) {
+            // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases
+            Doc.RemoveDocFromList(instance.props.Document[DataSym], "data", document);
+            // adds an alias of the doc to the data-all field of the layoutdocs of the aliases
+            DocListCast(instance.props.Document.aliases).forEach(alias => {
+                // if aliasDocList contains the alias, don't do anything
+                // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias
+                Doc.RemoveDocFromList(alias, "data", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
+            });
+            document.type !== DocumentType.KVP && Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", document);
+        }
+    }
+
     @undoBatch
     public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean {
         const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document);
         if (tab) {
             const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
             if (j !== -1) {
+                CollectionDockingView.Instance.removeFromTabDocList(tab.dashDoc as Doc);
                 tab.header.parent.contentItems[j].remove();
                 return CollectionDockingView.Instance.layoutChanged();
             }
@@ -185,16 +220,7 @@ export class CollectionDockingView extends CollectionSubView() {
         const instance = CollectionDockingView.Instance;
         if (!instance) return false;
         else {
-            const docList = DocListCast(instance.props.Document[DataSym]["data-all"]);
-            // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases
-            !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data-all", document);
-            // adds an alias of the doc to the data-all field of the layoutdocs of the aliases
-            DocListCast(instance.props.Document[DataSym].aliases).forEach(alias => {
-                const aliasDocList = DocListCast(alias["data-all"]);
-                // if aliasDocList contains the alias, don't do anything
-                // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias
-                !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
-            });
+            instance.addToTabDocList(document);
         }
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
 
@@ -477,7 +503,7 @@ export class CollectionDockingView extends CollectionSubView() {
                 //if (confirm('really close this?')) {
                 if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) {
                     stack.remove();
-                    stack.contentItems.forEach((contentItem: any) => Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", contentItem.tab.DashDoc, undefined, true, true));
+                    stack.contentItems.forEach((contentItem: any) => CollectionDockingView.Instance.removeFromTabDocList(contentItem.tab.DashDoc));
                 } else {
                     alert('cant delete the last stack');
                 }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 0070d0b9d..88d14fdfe 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -180,8 +180,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             tab.closeElement.off('click') //unbind the current click handler
                 .click(function () {
                     Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
-                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", doc);
-                    Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true);
+                    CollectionDockingView.Instance?.removeFromTabDocList(doc);
                     SelectionManager.DeselectAll();
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
-- 
cgit v1.2.3-70-g09d2


From 097998abda6857a498f6cfc8a7214a18b09b8451 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 May 2022 13:10:33 -0400
Subject: fixed TabDocView's screenToLocal xf when headerBar opens/closes. 
 changed webBox to update thumbnails when native dims change.  cleaned up
 content dragging/activation for multiRow/col for headerBar

---
 src/client/views/MainView.tsx                      |  4 ++--
 src/client/views/collections/TabDocView.tsx        |  3 +++
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../CollectionMulticolumnView.tsx                  |  9 ++++----
 .../CollectionMultirowView.tsx                     | 11 +++++-----
 src/client/views/nodes/WebBox.tsx                  | 24 +++++++++++++++++-----
 6 files changed, 34 insertions(+), 19 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4b7b9fc23..183efc944 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -283,7 +283,6 @@ export class MainView extends React.Component {
     }
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
 
-    headerContentActive = () => SnappingManager.GetIsDragging() && DocListCast(CurrentUserUtils.MyHeaderBarDoc.data).length ? false : true;
     @computed get headerBarDocView() {
         return <div style={{ height: this.headerBarDocHeight() }}>
             <DocumentView key="headerBarDoc"
@@ -297,7 +296,8 @@ export class MainView extends React.Component {
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
                 fitContentsToDoc={returnTrue}
-                isContentActive={this.headerContentActive}
+                isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
+                isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                 ScreenToLocalTransform={this.headerBarScreenXf}
                 childHideResizeHandles={returnTrue}
                 hideResizeHandles={true}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 88d14fdfe..a2ba504dc 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -337,7 +337,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         }
     }
     active = () => this._isActive;
+    @observable _forceInvalidateScreenToLocal = 0;
     ScreenToLocalTransform = () => {
+        this._forceInvalidateScreenToLocal;
         const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
         return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY);
     }
@@ -408,6 +410,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 if (this._mainCont = ref) {
                     (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
                     DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
+                    new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
                 }
             }} >
                 {this.docView}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3b32cf57d..dcff32d04 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1217,8 +1217,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
+            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             isContentActive={emptyFunction}
-            isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             focus={this.focusDocument}
             addDocTab={this.addDocTab}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 345ac7854..e2dfb25e2 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -4,7 +4,7 @@ import * as React from "react";
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnTrue } from '../../../../Utils';
+import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -241,9 +241,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    isContentActive = () => this.props.isSelected() ? true : undefined;
-    isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined;
-    isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive();
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -255,7 +254,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             isContentActive={this.isChildContentActive}
-            isDocumentActive={this.isChildDocumentActive}
+            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
             fitContentsToDoc={this.props.fitContentsToDoc}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 8218c49b5..3010e36aa 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -4,7 +4,7 @@ import * as React from "react";
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse } from '../../../../Utils';
+import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -241,10 +241,9 @@ export class CollectionMultirowView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    isContentActive = () => this.props.isSelected() ? true : undefined;
-    isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined;
-    isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive();
-    getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isChildContentActive = () => 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}
@@ -262,7 +261,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             isContentActive={this.isChildContentActive}
-            isDocumentActive={this.isChildDocumentActive}
+            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
             fitContentsToDoc={this.props.fitContentsToDoc}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2112c1d44..dbbcdc9de 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -75,7 +75,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
     @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
-    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url; }
+    @computed get webThumb() {
+        return this.props.thumbShown?.() &&
+            ImageCast(this.layoutDoc["thumb-frozen"],
+                ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) &&
+                    this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight) ? this.layoutDoc.thumb : undefined))?.url;
+    }
 
     constructor(props: any) {
         super(props);
@@ -114,20 +119,21 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     updateThumb = async () => {
         const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
         const scrollTop = NumCast(this.layoutDoc._scrollTop);
-        if (!this.lockout && this._iframe && !imageBitmap && scrollTop !== this.layoutDoc.thumbScrollTop) {
+        const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
+        const nativeHeight = nativeWidth * this.props.PanelHeight() / this.props.PanelWidth();
+        if (!this.lockout && this._iframe && !imageBitmap && (scrollTop !== this.layoutDoc.thumbScrollTop || nativeWidth !== this.layoutDoc.thumbNativeWidth || nativeHeight !== this.layoutDoc.thumbNativeHeight)) {
             var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
             if (!htmlString) {
                 htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
             }
             this.layoutDoc.thumb = undefined;
             this.lockout = true; // lock to prevent multiple thumb updates.
-            const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
             CreateImage(
                 this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl,
                 this._iframe.contentDocument?.styleSheets ?? [],
                 htmlString,
                 nativeWidth,
-                nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
+                nativeHeight,
                 scrollTop
             ).then
                 ((data_url: any) => {
@@ -136,6 +142,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             this.lockout = false;
                             this.layoutDoc.thumb = new ImageField(returnedfilename);
                             this.layoutDoc.thumbScrollTop = scrollTop;
+                            this.layoutDoc.thumbNativeWidth = nativeWidth;
+                            this.layoutDoc.thumbNativeHeight = nativeHeight;
                         }), 500));
                 })
                 .catch(function (error: any) {
@@ -387,6 +395,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
                 this.layoutDoc.thumb = undefined;
                 this.layoutDoc.thumbScrollTop = undefined;
+                this.layoutDoc.thumbNativeWidth = undefined;
+                this.layoutDoc.thumbNativeHeight = undefined;
                 this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
             } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
         }), timeout);
@@ -464,6 +474,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (this._webPageHasBeenRendered) {
                     this.layoutDoc.thumb = undefined;
                     this.layoutDoc.thumbScrollTop = undefined;
+                    this.layoutDoc.thumbNativeWidth = undefined;
+                    this.layoutDoc.thumbNativeHeight = undefined;
                 }
                 future && (future.length = 0);
             }
@@ -596,7 +608,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         setTimeout(action(() => {
             this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0);
-            if (this._initialScroll === undefined && !this._webPageHasBeenRendered) this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+            if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
+                this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+            }
             this._webPageHasBeenRendered = true;
         }));
         return view;
-- 
cgit v1.2.3-70-g09d2


From ddd38f923180efd0dc4f53a1c3caad4a1f9ed26f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 26 May 2022 17:40:54 -0400
Subject: removed more code relating to old version of dashboards with on and
 off screen tab lists.  fixed schema headers to not iconify when typing Enter
 in header field name.

---
 .../views/collections/CollectionDockingView.tsx    | 47 +---------------------
 src/client/views/collections/TabDocView.tsx        |  1 -
 .../collectionSchema/CollectionSchemaHeaders.tsx   |  5 ++-
 3 files changed, 6 insertions(+), 47 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c60434d95..509da18cb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -92,8 +92,6 @@ export class CollectionDockingView extends CollectionSubView() {
                     this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout)
                 }
                 DragManager.CompleteWindowDrag = undefined;
-            } else {
-                DragManager.DocDragData?.droppedDocuments.forEach(doc => this.addToTabDocList(doc));
             }
             finishDrag?.(aborted);
         };
@@ -104,30 +102,12 @@ export class CollectionDockingView extends CollectionSubView() {
         this.stateChanged();
     }
 
-    addToTabDocList = (document: Doc) => {
-        const instance = CollectionDockingView.Instance;
-        if (instance) {
-            const docList = DocListCast(instance.props.Document[DataSym].data);
-            // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases
-            !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document, "data", document);
-        }
-    }
-    removeFromTabDocList = (document: Doc) => {
-        const instance = CollectionDockingView.Instance;
-        if (instance) {
-            // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases
-            Doc.RemoveDocFromList(instance.props.Document, "data", document);
-            document.type !== DocumentType.KVP && Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", document);
-        }
-    }
-
     @undoBatch
     public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean {
         const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document);
         if (tab) {
             const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
             if (j !== -1) {
-                CollectionDockingView.Instance.removeFromTabDocList(tab.dashDoc as Doc);
                 tab.header.parent.contentItems[j].remove();
                 return CollectionDockingView.Instance.layoutChanged();
             }
@@ -206,9 +186,6 @@ export class CollectionDockingView extends CollectionSubView() {
         }
         const instance = CollectionDockingView.Instance;
         if (!instance) return false;
-        else {
-            instance.addToTabDocList(document);
-        }
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
 
         if (!pullSide && stack) {
@@ -434,29 +411,10 @@ export class CollectionDockingView extends CollectionSubView() {
         const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
         const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
         const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
-        const changesMade = this.props.Document.dockingConfig !== json;
+        const changesMade = this.props.Document.dockcingConfig !== json;
         if (changesMade && !this._flush) {
             this.props.Document.dockingConfig = json;
-            setTimeout(async () => {
-                const sublists = await DocListCastAsync(this.props.Document[this.props.fieldKey]);
-                const tabs = sublists && Cast(sublists[0], Doc, null);
-                // const other = sublists && Cast(sublists[1], Doc, null);
-                const tabdocs = await DocListCastAsync(tabs?.data);
-                // const otherdocs = await DocListCastAsync(other?.data);
-                if (tabs) {
-                    tabs.data = new List<Doc>(docs);
-                    // DocListCast(tabs.aliases).forEach(tab => tab !== tabs && (tab.data = new List<Doc>(docs)));
-                }
-                // const otherSet = new Set<Doc>();
-                // otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc));
-                // tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc));
-                // const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d).filter(d => d.type !== DocumentType.KVP);
-                // this.props.Document[DataSym][this.props.fieldKey + "-all"] = new List<Doc>([...docs, ...vals]);
-                // if (other) {
-                //     other.data = new List<Doc>(vals);
-                //     // DocListCast(other.aliases).forEach(tab => tab !== other && (tab.data = new List<Doc>(vals)));
-                // }
-            }, 0);
+            this.props.Document.data = new List<Doc>(docs);
         }
         return changesMade;
     }
@@ -490,7 +448,6 @@ export class CollectionDockingView extends CollectionSubView() {
                 //if (confirm('really close this?')) {
                 if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) {
                     stack.remove();
-                    stack.contentItems.forEach((contentItem: any) => CollectionDockingView.Instance.removeFromTabDocList(contentItem.tab.DashDoc));
                 } else {
                     alert('cant delete the last stack');
                 }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index a2ba504dc..2b78b20ea 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -180,7 +180,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             tab.closeElement.off('click') //unbind the current click handler
                 .click(function () {
                     Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
-                    CollectionDockingView.Instance?.removeFromTabDocList(doc);
                     SelectionManager.DeselectAll();
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
index dc35b5749..0875c80b3 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
@@ -278,6 +278,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
     @undoBatch
     onKeyDown = (e: React.KeyboardEvent): void => {
         if (e.key === "Enter") {
+            e.stopPropagation();
             if (this._searchTerm.includes(":")) {
                 const colpos = this._searchTerm.indexOf(":");
                 const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
@@ -490,7 +491,9 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
 
                 <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}>
                     <input className="keys-search" style={{ width: "100%" }}
-                        ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown}
+                        ref={this._inputRef} type="text"
+                        value={this._searchTerm} placeholder="Column key"
+                        onKeyDown={this.onKeyDown}
                         onChange={e => this.onChange(e.target.value)}
                         onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }}
                         onFocus={this.onFocus} ></input>
-- 
cgit v1.2.3-70-g09d2


From 58114c03252463b7386d16fc2aef61c361038bfa Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 2 Jun 2022 15:44:23 -0400
Subject: fixed previously introduced bug where pin doc added pres to first tab
 collection.  Highlight pres targets when following.  fixed videoBox to play
 regions properly.  adjusted labelBox for stackedTimeline views.  fixed
 stacked timelines to not stop keyboard events which was preventing text notes
 from being created on video boxes.

---
 src/Utils.ts                                         |  2 +-
 .../views/collections/CollectionStackedTimeline.tsx  |  9 +++++++--
 src/client/views/collections/TabDocView.tsx          |  4 +---
 src/client/views/nodes/LabelBox.tsx                  | 20 +++++++++++---------
 src/client/views/nodes/VideoBox.tsx                  |  2 ++
 src/client/views/nodes/trails/PresBox.tsx            |  1 +
 src/fields/Doc.ts                                    |  4 ++--
 src/fields/List.ts                                   |  1 +
 8 files changed, 26 insertions(+), 17 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index dbae0aa8c..e6aa0ad8b 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -209,7 +209,7 @@ export namespace Utils {
         if (scrollTop + contextHgt < Math.min(scrollHeight, targetY + minSpacing + targetHgt)) {
             return Math.ceil(targetY + minSpacing + targetHgt - contextHgt);
         }
-        if (scrollTop > Math.max(0, targetY - minSpacing - targetHgt)) {
+        if (scrollTop >= Math.max(0, targetY - minSpacing - targetHgt)) {
             return Math.max(0, Math.floor(targetY - minSpacing - targetHgt));
         }
     }
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 02b2248fb..5a1cc4ded 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -22,6 +22,7 @@ import {
     returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent
 } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
 import { LinkManager } from "../../util/LinkManager";
@@ -182,7 +183,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         ) {
             // if shift pressed scrub 1 second otherwise 1/10th
             const jump = e.shiftKey ? 1 : 0.1;
-            e.stopPropagation();
             switch (e.key) {
                 case " ":
                     if (!CollectionStackedTimeline.SelectingRegion) {
@@ -202,18 +202,22 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         this._markerEnd = undefined;
                         CollectionStackedTimeline.SelectingRegion = undefined;
                     }
+                    e.stopPropagation();
                     break;
                 case "Escape":
                     // abandons current trim
                     this._trimStart = this.clipStart;
                     this._trimStart = this.clipEnd;
                     this._trimming = TrimScope.None;
+                    e.stopPropagation();
                     break;
                 case "ArrowLeft":
                     this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime - jump), this.clipEnd));
+                    e.stopPropagation();
                     break;
                 case "ArrowRight":
                     this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime + jump), this.clipEnd));
+                    e.stopPropagation();
                     break;
             }
         }
@@ -788,6 +792,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
 
     // updates marker document title to reflect correct timecodes
     computeTitle = () => {
+        if (this.props.mark.type !== DocumentType.LABEL) return undefined;
         const start = Math.max(NumCast(this.props.mark[this.props.startTag]), this.props.trimStart) - this.props.trimStart;
         const end = Math.min(NumCast(this.props.mark[this.props.endTag]), this.props.trimEnd) - this.props.trimStart;
         return `#${formatTime(start)}-${formatTime(end)}`;
@@ -921,7 +926,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     DataDoc={undefined}
                     renderDepth={this.props.renderDepth + 1}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LabelBox.LayoutStringWithTitle(LabelBox, "data", this.computeTitle())}
+                    LayoutTemplateString={LabelBox.LayoutStringWithTitle("data", this.computeTitle())}
                     isDocumentActive={this.props.isDocumentActive}
                     PanelWidth={width}
                     PanelHeight={height}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 2b78b20ea..be68e1001 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -236,9 +236,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
             const dview = CollectionDockingView.Instance.props.Document;
             const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const sublists = DocListCast(dview[fieldKey]);
-            const tabs = Cast(sublists[0], Doc, null);
-            const tabdocs = await DocListCastAsync(tabs?.data);
+            const tabdocs = await DocListCastAsync(dview[fieldKey]);
             runInAction(() => {
                 if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
                     tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index d0d61fd79..b0b050cea 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -23,8 +23,8 @@ export interface LabelBoxProps {
 @observer
 export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps)>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); }
-    public static LayoutStringWithTitle(fieldType: { name: string }, fieldStr: string, label: string) {
-        return `<${fieldType.name} fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`;  //e.g., "<ImageBox {...props} fieldKey={"data} />"
+    public static LayoutStringWithTitle(fieldStr: string, label?: string) {
+        return !label ? LabelBox.LayoutString(fieldStr) : `<LabelBox fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`;  //e.g., "<ImageBox {...props} fieldKey={"data} />"
     }
     private dropDisposer?: DragManager.DragDropDisposer;
     private _timeout: any;
@@ -79,17 +79,18 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
     @computed get hoverColor() { return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : "unset"; }
 
     fitTextToBox = (r: any): any => {
+        const singleLine = BoolCast(this.rootDoc._singleLine, true);
         const params = {
             rotateText: null,
             fontSizeFactor: 1,
-            minimumFontSize: NumCast(this.rootDoc._minFontSize, 2),
+            minimumFontSize: NumCast(this.rootDoc._minFontSize, 8),
             maximumFontSize: NumCast(this.rootDoc._maxFontSize, 1000),
             limitingDimension: "both",
             horizontalAlign: "center",
             verticalAlign: "center",
             textAlign: "center",
-            singleLine: BoolCast(this.rootDoc._singleLine),
-            whiteSpace: this.rootDoc._singleLine ? "nowrap" : "pre-wrap"
+            singleLine,
+            whiteSpace: singleLine ? "nowrap" : "pre-wrap"
         };
         this._timeout = undefined;
         if (!r) return params;
@@ -99,15 +100,16 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
         parentStyle.display = "";
         parentStyle.alignItems = "";
         r.setAttribute("style", "");
-        r.style.width = this.rootDoc._singleLine ? "" : "100%";
+        r.style.width = singleLine ? "" : "100%";
 
         r.style.textOverflow = "ellipsis";
         r.style.overflow = "hidden";
         BigText(r, params);
+        return params;
     }
     // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
     render() {
-        this.fitTextToBox(null);// this causes mobx to trigger re-render when data changes
+        const boxParams = this.fitTextToBox(null);// this causes mobx to trigger re-render when data changes
         const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
         const missingParams = params?.filter(p => !this.paramsDoc[p]);
         params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ...
@@ -130,9 +132,9 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
                     paddingBottom: NumCast(this.rootDoc._yPadding),
                     width: this.props.PanelWidth(),
                     height: this.props.PanelHeight(),
-                    whiteSpace: this.rootDoc._singleLine ? "pre" : "pre-wrap"
+                    whiteSpace: boxParams.singleLine ? "pre" : "pre-wrap"
                 }} >
-                    <span style={{ width: this.layoutDoc._singleLine ? "" : "100%" }} ref={action((r: any) => this.fitTextToBox(r))}>
+                    <span style={{ width: boxParams.singleLine ? "" : "100%" }} ref={action((r: any) => this.fitTextToBox(r))}>
                         {label.startsWith("#") ? (null) : label.replace(/([^a-zA-Z])/g, "$1\u200b")}
                     </span>
                 </div>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index c350e3139..ac848fc2a 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -147,6 +147,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // plays video
     @action public Play = (update: boolean = true) => {
+        if (this._playRegionTimer) return;
         this._playing = true;
         const eleTime = this.player?.currentTime || 0;
         if (this.timeline) {
@@ -558,6 +559,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         // removes from currently playing if playback has reached end of range marker
                         else this.removeCurrentlyPlaying();
                         this.Pause();
+                        this._playRegionTimer = undefined;
                     }, playRegionDuration * 1000);
             } else {
                 this.Pause();
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 9641b403f..077db9077 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -297,6 +297,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 this.startTempMedia(targetDoc, activeItem);
             }
             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;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index b0a45091e..6abc27b23 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1073,9 +1073,9 @@ export namespace Doc {
     }
 
     let _lastDate = 0;
-    export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) {
+    export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true) {
         linkFollowUnhighlight();
-        Doc.HighlightDoc(destDoc, dataAndDisplayDocs);
+        (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs));
         document.removeEventListener("pointerdown", linkFollowUnhighlight);
         document.addEventListener("pointerdown", linkFollowUnhighlight);
         const lastDate = _lastDate = Date.now();
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 60bf442d4..b15548327 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -32,6 +32,7 @@ const listHandlers: any = {
     },
     push: action(function (this: any, ...items: any[]) {
         items = items.map(toObjectField);
+
         const list = this[Self];
         const length = list.__fields.length;
         for (let i = 0; i < items.length; i++) {
-- 
cgit v1.2.3-70-g09d2


From ba7824a91da113cc813c58b678719092c96d79a2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 3 Jun 2022 16:02:24 -0400
Subject: fixed opening closed tabs from Files sidebar to re-use best Alias
 before making a new alias.

---
 src/.DS_Store                                      | Bin 8196 -> 8196 bytes
 src/client/util/CurrentUserUtils.ts                |   2 +-
 src/client/util/DocumentManager.ts                 |  28 ++++++++++-----------
 src/client/views/LightboxView.tsx                  |   5 ++--
 src/client/views/collections/TabDocView.tsx        |  15 ++++++++++-
 src/client/views/collections/TreeView.tsx          |   4 +--
 src/client/views/nodes/DocumentIcon.tsx            |   4 +--
 src/client/views/nodes/DocumentView.tsx            |   4 ---
 src/client/views/nodes/button/FontIconBox.tsx      |  11 ++++----
 .../views/nodes/formattedText/DashFieldView.tsx    |   6 ++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  14 ++---------
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 13 files changed, 48 insertions(+), 49 deletions(-)

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

diff --git a/src/.DS_Store b/src/.DS_Store
index b0987293b..4bf9cdac7 100644
Binary files a/src/.DS_Store and b/src/.DS_Store differ
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2ed6c3cbe..85ad58726 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -869,7 +869,7 @@ export class CurrentUserUtils {
                             Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub);
                         }
                         prevSub = params.title;
-                    })
+                    });
                 }
                 prev = params.title;
             });
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index aeddc3249..a82f99d5a 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -16,7 +16,7 @@ import { SelectionManager } from './SelectionManager';
 export class DocumentManager {
 
     //global holds all of the nodes (regardless of which collection they're in)
-    @observable public DocumentViews: DocumentView[] = [];
+    @observable public DocumentViews = new Set<DocumentView>();
     @observable public LinkAnchorBoxViews: DocumentView[] = [];
     @observable public RecordingEvent = 0;
     @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = [];
@@ -46,7 +46,7 @@ export class DocumentManager {
             // this.LinkedDocumentViews.forEach(view => console.log("  LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " +
             //     view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString));
         } else {
-            this.DocumentViews.push(view);
+            this.DocumentViews.add(view);
         }
     }
     public RemoveView = action((view: DocumentView) => {
@@ -61,8 +61,7 @@ export class DocumentManager {
             const index = this.LinkAnchorBoxViews.indexOf(view);
             this.LinkAnchorBoxViews.splice(index, 1);
         } else {
-            const index = this.DocumentViews.indexOf(view);
-            index !== -1 && this.DocumentViews.splice(index, 1);
+            this.DocumentViews.delete(view);
         }
         SelectionManager.DeselectView(view);
     });
@@ -70,13 +69,13 @@ export class DocumentManager {
     //gets all views
     public getDocumentViewsById(id: string) {
         const toReturn: DocumentView[] = [];
-        DocumentManager.Instance.DocumentViews.map(view => {
+        Array.from(DocumentManager.Instance.DocumentViews).map(view => {
             if (view.rootDoc[Id] === id) {
                 toReturn.push(view);
             }
         });
         if (toReturn.length === 0) {
-            DocumentManager.Instance.DocumentViews.map(view => {
+            Array.from(DocumentManager.Instance.DocumentViews).map(view => {
                 const doc = view.rootDoc.proto;
                 if (doc && doc[Id] && doc[Id] === id) {
                     toReturn.push(view);
@@ -96,14 +95,14 @@ export class DocumentManager {
         const passes = preferredCollection ? [preferredCollection, undefined] : [undefined];
 
         for (const pass of passes) {
-            DocumentManager.Instance.DocumentViews.map(view => {
+            Array.from(DocumentManager.Instance.DocumentViews).map(view => {
                 if (view.rootDoc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                     toReturn = view;
                     return;
                 }
             });
             if (!toReturn) {
-                DocumentManager.Instance.DocumentViews.map(view => {
+                Array.from(DocumentManager.Instance.DocumentViews).map(view => {
                     const doc = view.rootDoc.proto;
                     if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) {
                         toReturn = view;
@@ -122,9 +121,8 @@ export class DocumentManager {
     }
 
     public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
-        const docViews = DocumentManager.Instance.DocumentViews;
         const views: DocumentView[] = [];
-        docViews.map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
+        Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
     }
     public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
@@ -133,8 +131,8 @@ export class DocumentManager {
     }
     public getDocumentViews(toFind: Doc): DocumentView[] {
         const toReturn: DocumentView[] = [];
-        const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
-        const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.IsLightboxDocView(view.docViewPath));
+        const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
+        const lightViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => LightboxView.IsLightboxDocView(view.docViewPath));
 
         // heuristic to return the "best" documents first:
         //   choose a document in the lightbox first
@@ -290,7 +288,7 @@ export class DocumentManager {
         }
     }
 }
-export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) {
+export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
     const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc);
     const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView);
     if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) {
@@ -300,7 +298,9 @@ export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) {
     else {
         const context = doc.context !== Doc.UserDoc().myFilesystem && Cast(doc.context, Doc, null);
         const showDoc = context || doc;
-        CollectionDockingView.AddSplit(showDoc === Doc.GetProto(showDoc) ? Doc.MakeAlias(showDoc) : showDoc, "right") && context &&
+        const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc;
+
+        CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), "right") && context &&
             setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));
     }
 }
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index ddfc8b249..3d2769f66 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -275,9 +275,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 <div className="lightboxView-tabBtn" title={"open in tab"}
                     onClick={e => {
                         e.stopPropagation();
-                        // CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "");
-                        LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace");
-                        console.log("lightbox to tab triggered")
+                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "");
+                        //LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace");
                         SelectionManager.DeselectAll();
                         LightboxView.SetLightboxDoc(undefined);
                     }}>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 8d84e9a56..d64cb2fd7 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -66,6 +66,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     get stack() { return (this.props as any).glContainer.parent.parent; }
     get tab() { return (this.props as any).glContainer.tab; }
     get view() { return this._view; }
+    _lastTab: any;
+    _lastView: DocumentView | undefined;
 
     @action
     init = (tab: any, doc: Opt<Doc>) => {
@@ -266,6 +268,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
     componentWillUnmount() {
         this._tabReaction?.();
+        this._view && DocumentManager.Instance.RemoveView(this._view);
         this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
 
         this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
@@ -353,7 +356,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
     @computed get docView() {
         return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
-            <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)}
+            <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => {
+                this._lastView && DocumentManager.Instance.RemoveView(this._lastView);
+                this._view = r;
+                this._lastView = this._view;
+            })}
                 renderDepth={0}
                 Document={this._document}
                 DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
@@ -409,6 +416,12 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 height: "100%", width: "100%"
             }} ref={ref => {
                 if (this._mainCont = ref) {
+                    if (this._lastTab) {
+                        console.log("DUP tab")
+                        this._view && DocumentManager.Instance.RemoveView(this._view);
+                        CollectionDockingView.Instance.tabMap.delete(this._lastTab);
+                    }
+                    this._lastTab = this.tab;
                     (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
                     DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
                     new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 661db7997..9de44fb00 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -183,7 +183,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             this.treeViewOpen = !this.treeViewOpen;
         } else {
             // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
-            const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+            const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
             const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail);
             this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), "lightbox");
         }
@@ -609,7 +609,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     // TODO: currently doc focus works, but can't seem to edit title
     // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
     onChildClick = () => {
-        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.())
+        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.());
     }
 
     onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index a9c998757..56de2d1fc 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -52,7 +52,7 @@ export class DocumentIconContainer extends React.Component {
                 };
             },
             getVars() {
-                const docs = DocumentManager.Instance.DocumentViews;
+                const docs = Array.from(DocumentManager.Instance.DocumentViews);
                 const capturedVariables: { [name: string]: Field } = {};
                 usedDocuments.forEach(index => capturedVariables[`d${index}`] = docs[index].props.Document);
                 return { capturedVariables };
@@ -60,6 +60,6 @@ export class DocumentIconContainer extends React.Component {
         };
     }
     render() {
-        return DocumentManager.Instance.DocumentViews.map((dv, i) => <DocumentIcon key={i} index={i} view={dv} />);
+        return Array.from(DocumentManager.Instance.DocumentViews).map((dv, i) => <DocumentIcon key={i} index={i} view={dv} />);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 14ededaf8..6e4d6a52e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -242,7 +242,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
     @computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
 
-
     componentWillUnmount() { this.cleanupHandlers(true); }
     componentDidMount() { this.setupHandlers(); }
     //componentDidUpdate() { this.setupHandlers(); }
@@ -1126,9 +1125,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     render() {
         TraceMobx();
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : Doc.DocBrushStatus.unbrushed) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        if (highlightIndex === 6) {
-            console.log("")
-        }
         const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange", "lightBlue"][highlightIndex];
         const highlightStyle = ["solid", "dashed", "solid", "solid", "solid"][highlightIndex];
         const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d29160458..b21a28d8e 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -231,7 +231,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
         const script = ScriptCast(this.rootDoc.script);
-        if (!script) { return null };
+        if (!script) { return null; }
 
         let noviceList: string[] = [];
         let text: string | undefined;
@@ -459,7 +459,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const buttonText = StrCast(this.rootDoc.buttonText);
 
         // TODO:glr Add label of button type
-        let button = this.defaultButton;
+        let button: JSX.Element | null = this.defaultButton;
 
         switch (this.type) {
             case ButtonType.TextButton:
@@ -527,9 +527,10 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         }
 
         return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button :
-            <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
-                {button}
-            </Tooltip>;
+            button !== null ?
+                <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
+                    {button}
+                </Tooltip > : null
     }
 }
 
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 6a3f9ed00..bb3791f1e 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -86,7 +86,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
         const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === "PARAMS" ? textBoxDoc[fieldKey] : "");
         const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
-        return { boolVal: Cast(fval, "boolean", null), strVal: Field.toString(fval as Field) || "" }
+        return { boolVal: Cast(fval, "boolean", null), strVal: Field.toString(fval as Field) || "" };
     }
 
     // set the display of the field's value (checkbox for booleans, span of text for strings)
@@ -241,8 +241,8 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
         const hideMenu = () => {
             this.fadeOut(true);
             document.removeEventListener("pointerdown", hideMenu);
-        }
-        document.addEventListener("pointerdown", hideMenu)
+        };
+        document.addEventListener("pointerdown", hideMenu);
     }
     render() {
         const buttons = [
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ce82821b6..434fccf8d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1202,7 +1202,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks);
                 this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
             } else if (this._editorView && curText && !FormattedTextBox.DontSelectInitialText) {
-                selectAll(this._editorView.state, this._editorView?.dispatch)
+                selectAll(this._editorView.state, this._editorView?.dispatch);
                 this.startUndoTypingBatch();
             } else if (this._editorView) {
                 this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index f4e792acd..0af7715fe 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -2435,9 +2435,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             indexNum += (index[i] * (10 ** (-i)));
         }
         this._treeViewMap.set(indexNum, treeViewDoc);
-        console.log(String(index), treeViewDoc)
         this.props.Document.presentationLinearizedDocuments = new List<Doc>(this.sort(this._treeViewMap)); // this is a flat array of Docs
-        console.log(this.props.Document.presentationLinearizedDocuments)
         return this.childDocs;
     }
 
@@ -2447,21 +2445,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             indexNum += (index[i] * (10 ** (-i)));
         }
         console.log(String(index), treeViewDoc)
-        this._treeViewMap.delete(indexNum)
+        this._treeViewMap.delete(indexNum);
         this.props.Document.presentationLinearizedDocuments = new List<Doc>(this.sort(this._treeViewMap));
         return this.childDocs;
     }
 
     // TODO: [AL] implement sort function for an array of numbers (e.g. arr[1,2,4] v arr[1,2,1])
-    sort = (treeViewMap: Map<number, Doc>): Doc[] => {
-        // TODO
-        const sortedMap = [...treeViewMap.entries()].sort();
-        var sortedDocs = [];
-        for (var kv of sortedMap) {
-            sortedDocs.push(kv[1]);
-        }
-        return sortedDocs;
-    }
+    sort = (treeViewMap: Map<number, Doc>) => [...treeViewMap.entries()].sort().map(kv => kv[1]);
 
     render() {
         // calling this method for keyEvents
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 6db36f36a..d5fd425ad 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -154,7 +154,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
             const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight, NumCast(this.props.Document.scrollHeight));
-            if (scrollTo !== undefined && scrollTo != this.props.layoutDoc._scrollTop) {
+            if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
                 focusSpeed = 500;
 
                 if (!this._pdfViewer) this._initialScroll = scrollTo;
-- 
cgit v1.2.3-70-g09d2


From 84e843ccb21dcb95dff11e34b749f4587fc0fd86 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 4 Jun 2022 23:21:17 -0400
Subject: fixed mini-pres view css, fixed interaction with mini-pres by fixing
 overlayView panelWidth/height, fixed tabMap to get updated properly to allow
 CloseSplit to work, etc.

---
 src/client/views/OverlayView.tsx            | 12 +++---
 src/client/views/StyleProvider.tsx          |  2 +-
 src/client/views/collections/TabDocView.tsx |  2 +-
 src/client/views/nodes/trails/PresBox.scss  |  7 +--
 src/client/views/nodes/trails/PresBox.tsx   | 67 ++++++++++++++++++++---------
 5 files changed, 58 insertions(+), 32 deletions(-)

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

diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index ebad2981d..18b87e3d5 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -2,10 +2,10 @@ import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
 import ReactLoading from 'react-loading';
-import { Doc } from "../../fields/Doc";
+import { Doc, HeightSym, WidthSym } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast } from "../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../Utils";
 import { DocUtils } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { DragManager } from "../util/DragManager";
@@ -186,12 +186,12 @@ export class OverlayView extends React.Component {
                     bringToFront={emptyFunction}
                     addDocument={undefined}
                     removeDocument={undefined}
-                    PanelWidth={returnOne}
-                    PanelHeight={returnOne}
+                    PanelWidth={d[WidthSym]}
+                    PanelHeight={d[HeightSym]}
                     ScreenToLocalTransform={Transform.Identity}
                     renderDepth={1}
-                    isDocumentActive={returnTrue}
-                    isContentActive={emptyFunction}
+                    isDocumentActive={returnFalse}
+                    isContentActive={returnTrue}
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={DefaultStyleProvider}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 553f84a67..35415ae4e 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -136,7 +136,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                         StrCast(props?.Document.backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : "")));
             switch (doc?.type) {
                 case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break;
-                case DocumentType.PRES: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
+                case DocumentType.PRES: docColor = docColor || (darkScheme() ? "transparent" : "transparent"); break;
                 case DocumentType.FONTICON: docColor = docColor || Colors.DARK_GRAY; break;
                 case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
                 case DocumentType.FILTER: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : "rgba(105, 105, 105, 0.432)"); break;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index d64cb2fd7..ad01be3a3 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -76,7 +76,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             tab._disposers = {} as { [name: string]: IReactionDisposer };
             tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
             tab.DashDoc = doc;
-            CollectionDockingView.Instance.tabMap.add(tab);
             const iconType: IconProp = Doc.toIcon(doc);
             // setup the title element and set its size according to the # of chars in the title.  Show the full title when clicked.
             const titleEle = tab.titleElement[0];
@@ -186,6 +185,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
         }
+        CollectionDockingView.Instance.tabMap.add(tab);
     }
 
     /**
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index 06932d145..a0a2dd4f8 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -1078,11 +1078,13 @@
 .miniPres {
     cursor: grab;
     position: absolute;
-    right: 10;
-    top: 10;
+    top: 0;
+    left: 0;
     opacity: 0.1;
     transition: all 0.4s;
     color: $white;
+    width: 100%;
+    height: 100%;
 }
 
 .miniPres:hover {
@@ -1101,7 +1103,6 @@
     align-items: center;
     display: flex;
     position: absolute;
-    right: 10px;
     transition: all 0.2s;
 
     .presPanel-button-text {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 0af7715fe..914d6ab15 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -12,7 +12,7 @@ import { PrefetchProxy } from "../../../../fields/Proxy";
 import { listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnFalse, returnOne, returnTrue } from '../../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
@@ -604,27 +604,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * The method called to open the presentation as a minimized view
      */
     @action
-    updateMinimize = () => {
-        const docView = DocumentManager.Instance.getDocumentView(this.layoutDoc);
+    updateMinimize = async () => {
         if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
-            console.log("case 1");
             this.layoutDoc.presStatus = PresStatus.Edit;
             Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, "right");
-        } else if ((true || this.layoutDoc.context) && docView) {
-            console.log("case 2");
+        } else {
             this.layoutDoc.presStatus = PresStatus.Edit;
             clearTimeout(this._presTimer);
             const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
             this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
             this.rootDoc.y = pt[1] + 10;
-            this.rootDoc._height = 35;
-            this.rootDoc._width = 250;
-            docView.props.removeDocument?.(this.layoutDoc);
+            this.rootDoc._height = 30;
+            this.rootDoc._width = 248;
+            this.props.removeDocument?.(this.layoutDoc);
             Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
-        } else {
-            console.log("case 3");
-            // TODO glr: fix this case
         }
     }
 
@@ -2424,6 +2418,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         else this.pauseAutoPres();
     }
 
+    @action
+    prevClicked = (e: PointerEvent) => {
+        this.back();
+        if (this._presTimer) {
+            clearTimeout(this._presTimer);
+            this.layoutDoc.presStatus = PresStatus.Manual;
+        }
+    }
+
+    @action
+    nextClicked = (e: PointerEvent) => {
+        this.next();
+        if (this._presTimer) {
+            clearTimeout(this._presTimer);
+            this.layoutDoc.presStatus = PresStatus.Manual;
+        }
+    }
+    @undoBatch
+    @action
+    exitClicked = (e: PointerEvent) => {
+        this.updateMinimize();
+        this.layoutDoc.presStatus = PresStatus.Edit;
+        clearTimeout(this._presTimer);
+    }
+
     @action
     startMarqueeCreateSlide = () => {
         PresBox.startMarquee = true;
@@ -2465,28 +2484,34 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ?
             <div className="miniPres">
                 <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></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+                    <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
+                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.layoutDoc.presLoop = !this.layoutDoc.presLoop, false, false)}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
                     <div className="presPanel-divider"></div>
-                    <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></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 === "auto" ? "pause" : "play"} /></div></Tooltip>
-                    <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onClick={() => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-right"} /></div>
+                    <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></>}><div className="presPanel-button"
+                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
+                    <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }}
+                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.nextClicked, false, false)}><FontAwesomeIcon icon={"arrow-right"} /></div>
                     <div className="presPanel-divider"></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></Tooltip>
+                    <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></Tooltip>
                     <div className="presPanel-button-text">
                         Slide {this.itemIndex + 1} / {this.childDocs.length}
                         {this.playButtonFrames}
                     </div>
-                    <div className="presPanel-divider"></div>
-                    <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.updateMinimize(); this.layoutDoc.presStatus = PresStatus.Edit; clearTimeout(this._presTimer); }))}>EXIT</div>
+                    <div className="presPanel-divider" />
+                    <div className="presPanel-button-text" onPointerDown={e =>
+                        setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>EXIT</div>
                 </div>
-            </div>
+            </div >
             :
             <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.OverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
                 <div className="presBox-listCont">
-                    <div className="Slide" style={{ height: `calc(100% - 100px)` }}>
+                    <div className="Slide" style={{ height: `calc(100% - 30px)` }}>
                         {mode !== CollectionViewType.Invalid ?
                             <CollectionView {...this.props}
                                 ContainingCollectionDoc={this.props.Document}
-- 
cgit v1.2.3-70-g09d2


From acf19e54a43d5b05c58ef3c80d27086dd64f8e8e Mon Sep 17 00:00:00 2001
From: geireann <60007097+geireann@users.noreply.github.com>
Date: Sun, 5 Jun 2022 16:38:46 +0100
Subject: updated and refactored pinWithView

Changed MarqueeView pinWithView so that it just calls TabDocView.PinDoc
---
 .vscode/settings.json                              |   2 +-
 src/client/views/collections/TabDocView.tsx        |  11 +-
 .../collections/collectionFreeForm/MarqueeView.tsx | 122 +++++----------------
 src/client/views/nodes/trails/PresBox.tsx          |  13 ++-
 4 files changed, 50 insertions(+), 98 deletions(-)

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

diff --git a/.vscode/settings.json b/.vscode/settings.json
index 27acfd1a2..a6e6ad360 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,7 +6,7 @@
         "**/CVS": true,
         "**/.DS_Store": true,
     },
-    "editor.formatOnSave": true,
+    "editor.formatOnSave": false,
     "editor.detectIndentation": false,
     "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
     "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index ad01be3a3..0cd3fb5a7 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -193,10 +193,12 @@ export class TabDocView extends React.Component<TabDocViewProps> {
      **/
     @action
     public static async PinDoc(doc: Doc, pinProps?: PinProps) {
-        if (pinProps?.unpin) console.log('TODO: Remove UNPIN from this location');
         //add this new doc to props.Document
+
+        // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
         const curPres = CurrentUserUtils.ActivePresentation;
         if (curPres) {
+            // Edge Case 1: Cannot pin document to itself
             if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
             const batch = UndoManager.StartBatch("pinning doc");
             const pinDoc = Doc.MakeAlias(doc);
@@ -239,6 +241,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const dview = CollectionDockingView.Instance.props.Document;
             const fieldKey = CollectionDockingView.Instance.props.fieldKey;
             const tabdocs = await DocListCastAsync(dview[fieldKey]);
+			if (pinProps?.pinWithView) {
+				const viewProps = pinProps.pinWithView;
+				pinDoc.presPinView = true;
+            	pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
+            	pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
+            	pinDoc.presPinViewScale = viewProps.scale;
+			}
             runInAction(() => {
                 if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
                     tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 8ca7e160e..362117b19 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -20,7 +20,7 @@ import { Transform } from "../../../util/Transform";
 import { undoBatch, UndoManager } from "../../../util/UndoManager";
 import { ContextMenu } from "../../ContextMenu";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PresBox } from "../../nodes/trails/PresBox";
+import { PinViewProps, PresBox } from "../../nodes/trails/PresBox";
 import { PresMovement } from "../../nodes/trails/PresEnums";
 import { VideoBox } from "../../nodes/VideoBox";
 import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
@@ -31,6 +31,7 @@ import { TreeView } from "../TreeView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
+import { TabDocView } from "../TabDocView";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -44,6 +45,14 @@ interface MarqueeViewProps {
     ungroup?: () => void;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
 }
+
+export interface MarqueeViewBounds {
+	left: number;
+	top: number;
+	width: number;
+	height: number;
+}
+
 @observer
 export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
 {
@@ -60,7 +69,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     @computed get Bounds() {
         const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
         const size = this.Transform.transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-        return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
+		const bounds:MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+        return bounds;
     }
     get inkDoc() { return this.props.Document; }
     get ink() { return Cast(this.props.Document.ink, InkField); }
@@ -263,7 +273,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             document.removeEventListener("wheel", hideMarquee);
         };
         if (PresBox.startMarquee) {
-            this.pinMarqueeView();
+            this.pinWithView();
             PresBox.startMarquee = false;
         }
         if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100) && !PresBox.startMarquee) {
@@ -385,98 +395,24 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
+	/**
+	 * This triggers the TabDocView.PinDoc method which is the universal method
+	 * used to pin documents to the currently active presentation trail. 
+	 * 
+	 * This one is unique in that it includes the bounds associated with marquee view. 
+	 */
     @undoBatch
     @action
-    pinMarqueeView = async () => {
-        const doc = this.props.Document;
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
-        if (curPres) {
-            console.log(curPres);
-            if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const pinDoc = Doc.MakeAlias(doc);
-            pinDoc.presentationTargetDoc = doc;
-            pinDoc.presMovement = PresMovement.Zoom;
-            pinDoc.groupWithUp = false;
-            pinDoc.context = curPres;
-            pinDoc.title = doc.title + " - Slide";// these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
-            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
-            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
-            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
-            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
-            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
-            pinDoc.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 = PresBox.Instance?.sortArray();
-            const size = PresBox.Instance?._selectedArray.size;
-            const presSelected = presArray && size ? presArray[size - 1] : undefined;
-            Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
-            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            const dview = CollectionDockingView.Instance.props.Document;
-            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const tabdocs = await DocListCastAsync(dview[fieldKey]);
-            if (!tabdocs?.includes(curPres)) {
-                tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
-                CollectionDockingView.AddSplit(curPres, "right");
-            }
-            PresBox.Instance?._selectedArray.clear();
-            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Updates selected array
-            const index = PresBox.Instance?.childDocs.indexOf(pinDoc);
-            index && (curPres._itemIndex = index);
-            const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
-            pinDoc.presPinView = true;
-            pinDoc.presPinViewX = this.Bounds.left + this.Bounds.width / 2;
-            pinDoc.presPinViewY = this.Bounds.top + this.Bounds.height / 2;
-            pinDoc.presPinViewScale = scale;
-        }
-        this.hideMarquee();
-    }
-
-    @undoBatch
-    @action
-    pinWithView = async (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const doc = this.props.Document;
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
-        if (curPres) {
-            if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const pinDoc = Doc.MakeAlias(doc);
-            pinDoc.presentationTargetDoc = doc;
-            pinDoc.presMovement = PresMovement.Zoom;
-            pinDoc.groupWithUp = false;
-            pinDoc.context = curPres;
-            pinDoc.title = doc.title + " - Slide";
-            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
-            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
-            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
-            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
-            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
-            pinDoc.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 = PresBox.Instance?.sortArray();
-            const size = PresBox.Instance?._selectedArray.size;
-            const presSelected = presArray && size ? presArray[size - 1] : undefined;
-            Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
-            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; const dview = CollectionDockingView.Instance.props.Document;
-            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const tabdocs = await DocListCastAsync(dview[fieldKey]);
-            if (!tabdocs?.includes(curPres)) {
-                tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
-                CollectionDockingView.AddSplit(curPres, "right");
-            }
-            PresBox.Instance?._selectedArray.clear();
-            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Updates selected array
-            const index = PresBox.Instance?.childDocs.indexOf(pinDoc);
-            index && (curPres._itemIndex = index);
-            if (e instanceof KeyboardEvent ? e.key === "c" : true) {
-                const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
-                pinDoc.presPinView = true;
-                pinDoc.presPinViewX = this.Bounds.left + this.Bounds.width / 2;
-                pinDoc.presPinViewY = this.Bounds.top + this.Bounds.height / 2;
-                pinDoc.presPinViewScale = scale;
-            }
-        }
-        MarqueeOptionsMenu.Instance.fadeOut(true);
+    pinWithView = async () => {
+		console.log("pinWithView");
+        const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
+		const doc = this.props.Document;
+		const viewOptions:PinViewProps = {
+			bounds: this.Bounds,
+			scale: scale
+		};
+		TabDocView.PinDoc(doc, {pinWithView: viewOptions});
+		MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
     }
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index ab5382581..3a29d9adf 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -30,14 +30,21 @@ import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import "./PresBox.scss";
 import { PresEffect, PresMovement, PresStatus } from "./PresEnums";
+import { MarqueeViewBounds } from "../../collections/collectionFreeForm";
 
-export class PinProps {
+export interface PinProps {
     audioRange?: boolean;
-    unpin?: boolean;
     setPosition?: boolean;
     hidePresBox?: boolean;
+    pinWithView?: PinViewProps;
 }
 
+export interface PinViewProps {
+  bounds: MarqueeViewBounds;
+  scale: number;
+}
+
+
 @observer
 export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
@@ -2543,7 +2550,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 // this func communicates with PresBoxElement to send information of the doc
 ScriptingGlobals.add(function lookupPresBoxField(presBoxDoc: Doc, field: string, presEleDoc: Doc) {
     if (field === 'indexInPres') return DocListCast(presBoxDoc._viewType === CollectionViewType.Tree ? presBoxDoc.presentationLinearizedDocuments : presBoxDoc[StrCast(presBoxDoc.presentationFieldKey)]).indexOf(presEleDoc);
-    if (field === 'presCollapsedHeight') return [CollectionViewType.Tree || CollectionViewType.Stacking].includes(presBoxDoc._viewType as any) ? 35 : 31;
+    if (field === 'presCollapsedHeight') return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(presBoxDoc._viewType as any) ? 35 : 31;
     if (field === 'presStatus') return presBoxDoc.presStatus;
     if (field === '_itemIndex') return presBoxDoc._itemIndex;
     if (field === 'presBox') return presBoxDoc;
-- 
cgit v1.2.3-70-g09d2


From d939e83f1279cb010b3573f0ac02ba4dc7c3cb37 Mon Sep 17 00:00:00 2001
From: geireann <60007097+geireann@users.noreply.github.com>
Date: Sun, 5 Jun 2022 17:11:22 +0100
Subject: updates and attempts at debugging

---
 src/client/views/collections/TabDocView.tsx | 15 ++++++++-------
 src/client/views/nodes/trails/PresBox.tsx   |  5 +++++
 2 files changed, 13 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 0cd3fb5a7..004032282 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -220,6 +220,14 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             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);
+			// If pinWithView option set then update scale and x / y props of slide
+			if (pinProps?.pinWithView) {
+				const viewProps = pinProps.pinWithView;
+				pinDoc.presPinView = true;
+            	pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
+            	pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
+            	pinDoc.presPinViewScale = viewProps.scale;
+			}
             Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = "manual";
@@ -241,13 +249,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const dview = CollectionDockingView.Instance.props.Document;
             const fieldKey = CollectionDockingView.Instance.props.fieldKey;
             const tabdocs = await DocListCastAsync(dview[fieldKey]);
-			if (pinProps?.pinWithView) {
-				const viewProps = pinProps.pinWithView;
-				pinDoc.presPinView = true;
-            	pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
-            	pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
-            	pinDoc.presPinViewScale = viewProps.scale;
-			}
             runInAction(() => {
                 if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
                     tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 3a29d9adf..ca71c76a9 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -335,7 +335,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     navigateToView = (targetDoc: Doc, activeItem: Doc) => {
         clearTimeout(this._navTimer);
         const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
+        if (bestTarget) console.log(bestTarget.title, bestTarget.type);
+        else console.log("no best target");
         bestTarget && runInAction(() => {
+            console.log(bestTarget.title, bestTarget.type);
             if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
                 bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
                 bestTarget._scrollTop = activeItem.presPinViewScroll;
@@ -417,6 +420,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // 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) {
+            console.log(targetDoc.title);
+            console.log("presPinView in PresBox.tsx:420");
             // 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);
         }
-- 
cgit v1.2.3-70-g09d2


From 0371242941dfdd1d689d0097140b203bb0b24dea Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 5 Jun 2022 20:16:02 -0400
Subject: fixed presentations to allow pinning a clip of audio.  fixed
 TabDocViews to be added properly to DocumentManager.  fixed presentations to
 allow groupWithUp to work in tree/stacking view modes.  f

---
 src/client/views/collections/TabDocView.tsx | 3 +++
 src/client/views/nodes/AudioBox.tsx         | 1 -
 src/client/views/nodes/trails/PresBox.tsx   | 4 ++--
 3 files changed, 5 insertions(+), 3 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 ad01be3a3..dab3d490d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -265,6 +265,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         //     ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""),
         //     { fireImmediately: true });
     }
+    componentDidUpdate() {
+        this._view && DocumentManager.Instance.AddView(this._view);
+    }
 
     componentWillUnmount() {
         this._tabReaction?.();
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index d97cb6f84..85dfd1e9a 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -611,7 +611,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 renderDepth={this.props.renderDepth + 1}
                 startTag={"_timecodeToShow" /* audioStart */}
                 endTag={"_timecodeToHide" /* audioEnd */}
-                focus={DocUtils.DefaultFocus}
                 bringToFront={emptyFunction}
                 CollectionView={undefined}
                 playFrom={this.playFrom}
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 18344a11d..e32f2826f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -319,7 +319,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             if (!group) this._selectedArray.clear();
             this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //Update selected array
-            if ([CollectionViewType.Stacking, CollectionViewType.Tree].includes(this.layoutDoc._viewType as any) && !group) this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
+            this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
             this.onHideDocument(); //Handles hide after/before
         }
     });
@@ -357,7 +357,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     navigateToElement = async (curDoc: Doc) => {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const srcContext = Cast(targetDoc.context, Doc, null);
+        const srcContext = Cast(targetDoc.context, Doc, null) ?? Cast(Cast(targetDoc.annotationOn, Doc, null)?.context, Doc, null);
         const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
         const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined;
         const includesDoc: boolean = DocListCast(presCollection?.data).includes(targetDoc);
-- 
cgit v1.2.3-70-g09d2


From f8840672a7bcb3f337d8e50098f374a1b2d441ce Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Jun 2022 01:17:24 -0400
Subject: added ability to make thumbnails of dashboards. started to cleanup
 dockingView/goldenlayout undo event handling.  cleaned up tab doc list in
 docking view.  made titles editable again in treeview.  fixed overlay view to
 work with image docs etc by setting top/left in css

---
 src/client/documents/Documents.ts                  |  6 +--
 src/client/goldenLayout.js                         |  2 +-
 src/client/util/CurrentUserUtils.ts                | 26 ++++++++---
 src/client/views/DocumentButtonBar.tsx             | 53 ++++++++++------------
 src/client/views/MainView.tsx                      | 12 +----
 src/client/views/OverlayView.scss                  |  2 +
 src/client/views/OverlayView.tsx                   |  2 +-
 .../views/collections/CollectionDockingView.tsx    | 21 ++++++---
 src/client/views/collections/TabDocView.tsx        | 28 ++++--------
 src/client/views/collections/TreeView.tsx          |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 45 ++++++++++--------
 src/client/views/nodes/WebBoxRenderer.js           |  4 +-
 src/client/views/topbar/TopBar.tsx                 |  6 +--
 13 files changed, 108 insertions(+), 103 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 817478b2f..acc1e341a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -671,9 +671,9 @@ export namespace Docs {
             return viewDoc;
         }
 
-        export function ImageDocument(url: string, options: DocumentOptions = {}) {
-            const imgField = new ImageField(url);
-            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options });
+        export function ImageDocument(url: string|ImageField, options: DocumentOptions = {}) {
+            const imgField =  url instanceof ImageField ? url : new ImageField(url);
+            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options });
         }
 
         export function PresDocument(options: DocumentOptions = {}) {
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 82b10608d..4735f216f 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -1431,7 +1431,7 @@
             this.root.callDownwards('_$init');
 
             if (config.maximisedItemId === '__glMaximised') {
-                this.root.getItemsById(config.maximisedItemId)[0].toggleMaximise();
+                this.root.getItemsById(config.maximisedItemId)[0]?.toggleMaximise();
             }
         },
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 51bfdbbd2..885679b64 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -8,7 +8,7 @@ import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
 import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
-import { nullAudio } from "../../fields/URLField";
+import { ImageField, nullAudio } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
@@ -22,11 +22,10 @@ import { TreeView } from "../views/collections/TreeView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
-import { LabelBox } from "../views/nodes/LabelBox";
 import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DocumentManager } from "./DocumentManager";
-import { DragManager, dropActionType } from "./DragManager";
+import { DragManager } from "./DragManager";
 import { makeTemplate, MakeTemplate } from "./DropConverter";
 import { HistoryUtil } from "./History";
 import { LinkManager } from "./LinkManager";
@@ -1185,9 +1184,24 @@ export class CurrentUserUtils {
     }
 
     public static async snapshotDashboard(userDoc: Doc) {
-        const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
-        Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
-        CurrentUserUtils.openDashboard(userDoc, copy);
+        const docView = CollectionDockingView.Instance.props.DocumentView?.();
+        const content = docView?.ContentDiv;
+        if (docView && content) {
+            const _width = Number(getComputedStyle(content).width.replace("px",""));
+            const _height = Number(getComputedStyle(content).height.replace("px",""));
+            CollectionFreeFormView.UpdateIcon(
+                docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
+                content,
+                _width, _height,
+                _width, _height, 0,
+                (iconFile, _nativeWidth, _nativeHeight) => {
+                    const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight});
+                    const proto = Cast(img.proto, Doc, null)!;
+                    proto["data-nativeWidth"] = _width;
+                    proto["data-nativeHeight"] = _height;
+                    docView.dataDoc.thumb = img;
+                });
+        }
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index c0645f0ab..103734b9b 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -213,34 +213,31 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     @undoBatch
     @action
     pinWithView = (targetDoc: Doc) => {
-        if (targetDoc) {
-            TabDocView.PinDoc(targetDoc);
-            setTimeout(() => {
-                const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
-                const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
-                const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
-                if (scrollable) {
-                    const scroll = targetDoc._scrollTop;
-                    activeDoc.presPinView = true;
-                    activeDoc.presPinViewScroll = scroll;
-                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                    activeDoc.presPinView = true;
-                    activeDoc.presStartTime = targetDoc._currentTimecode;
-                    activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                } else if (pannable) {
-                    const x = targetDoc._panX;
-                    const y = targetDoc._panY;
-                    const scale = targetDoc._viewScale;
-                    activeDoc.presPinView = true;
-                    activeDoc.presPinViewX = x;
-                    activeDoc.presPinViewY = y;
-                    activeDoc.presPinViewScale = scale;
-                } else if (targetDoc.type === DocumentType.COMPARISON) {
-                    const width = targetDoc._clipWidth;
-                    activeDoc.presPinClipWidth = width;
-                    activeDoc.presPinView = true;
-                }
-            });
+        const activeDoc = targetDoc && TabDocView.PinDoc(targetDoc);
+        if (activeDoc) {
+            const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
+            const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
+            if (scrollable) {
+                const scroll = targetDoc._scrollTop;
+                activeDoc.presPinView = true;
+                activeDoc.presPinViewScroll = scroll;
+            } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
+                activeDoc.presPinView = true;
+                activeDoc.presStartTime = targetDoc._currentTimecode;
+                activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
+            } else if (pannable) {
+                const x = targetDoc._panX;
+                const y = targetDoc._panY;
+                const scale = targetDoc._viewScale;
+                activeDoc.presPinView = true;
+                activeDoc.presPinViewX = x;
+                activeDoc.presPinViewY = y;
+                activeDoc.presPinViewScale = scale;
+            } else if (targetDoc.type === DocumentType.COMPARISON) {
+                const width = targetDoc._clipWidth;
+                activeDoc.presPinClipWidth = width;
+                activeDoc.presPinView = true;
+            }
         }
     }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cdf85c905..a30db66e4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -226,17 +226,7 @@ export class MainView extends React.Component {
         if (received && !this.userDoc) {
             reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
         } else {
-            if (received && CurrentUserUtils._urlState.sharing) {
-                reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
-                    initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
-                        if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
-                            CollectionDockingView.AddSplit(docField, "right");
-                        }
-                    }),
-                );
-            }
-            const activeDash = PromiseValue(this.userDoc.activeDashboard);
-            activeDash.then(dash => {
+            PromiseValue(this.userDoc.activeDashboard).then(dash => {
                 if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
                 else CurrentUserUtils.createNewDashboard(this.userDoc);
             });
diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss
index 555f4298d..f22e68ff5 100644
--- a/src/client/views/OverlayView.scss
+++ b/src/client/views/OverlayView.scss
@@ -46,4 +46,6 @@
 .overlayView-doc {
     z-index: 9002; //so that it appears above chroma 
     position: absolute;
+    top: 0;
+    left: 0;
 }
\ No newline at end of file
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index c3c103b50..f5e686473 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -200,7 +200,7 @@ export class OverlayView extends React.Component {
                     ScreenToLocalTransform={this.docScreenToLocalXf(d)}
                     renderDepth={1}
                     isDocumentActive={returnTrue}
-                    isContentActive={retu}
+                    isContentActive={returnTrue}
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={DefaultStyleProvider}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index de2106e4a..5af72b7d1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -26,6 +26,7 @@ import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
 import { CollectionViewType } from './CollectionView';
 import { TabDocView } from './TabDocView';
 import React = require("react");
+import { SelectionManager } from '../../util/SelectionManager';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -50,9 +51,8 @@ export class CollectionDockingView extends CollectionSubView() {
     public _flush: UndoManager.Batch | undefined;
     private _ignoreStateChange = "";
     public tabMap: Set<any> = new Set();
-    public get initialized() { return this._goldenLayout !== null; }
     public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; }
-    @observable private _goldenLayout: any = null;
+    private _goldenLayout: any = null;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -118,6 +118,7 @@ export class CollectionDockingView extends CollectionSubView() {
 
     @undoBatch
     public static OpenFullScreen(doc: Doc) {
+        SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
             return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
@@ -271,7 +272,7 @@ export class CollectionDockingView extends CollectionSubView() {
         return true;
     }
 
-    async setupGoldenLayout() {
+    setupGoldenLayout = async () => {
         const config = StrCast(this.props.Document.dockingConfig);
         if (config) {
             const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -291,7 +292,7 @@ export class CollectionDockingView extends CollectionSubView() {
             }
             this.tabMap.clear();
             this._goldenLayout?.destroy();
-            runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
+            this._goldenLayout = new GoldenLayout(JSON.parse(config));
             this._goldenLayout.on('tabCreated', this.tabCreated);
             this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
             this._goldenLayout.on('stackCreated', this.stackCreated);
@@ -322,7 +323,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     }
                     this._ignoreStateChange = "";
                 });
-            setTimeout(() => this.setupGoldenLayout(), 0);
+            setTimeout(this.setupGoldenLayout);
             //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
         }
     }
@@ -413,19 +414,25 @@ export class CollectionDockingView extends CollectionSubView() {
         const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
         const changesMade = this.props.Document.dockcingConfig !== json;
         if (changesMade && !this._flush) {
-            this.props.Document.dockingConfig = json;
-            this.props.Document.data = new List<Doc>(docs);
+            UndoManager.RunInBatch(() => {
+                this.props.Document.dockingConfig = json;
+                this.props.Document.data = new List<Doc>(docs);
+            }, "state changed");
         }
         return changesMade;
     }
 
     tabDestroyed = (tab: any) => {
+        const dview = CollectionDockingView.Instance.props.Document;
+        const fieldKey = CollectionDockingView.Instance.props.fieldKey;
+        Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
         this.tabMap.delete(tab);
         tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
         tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
         this.stateChanged();
     }
     tabCreated = (tab: any) => {
+        this.tabMap.add(tab);
         tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab);  // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
     }
 
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 0141724ea..75a8207dd 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -82,7 +82,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const iconWrap = document.createElement("div");
             const closeWrap = document.createElement("div");
 
-
             titleEle.size = StrCast(doc.title).length + 3;
             titleEle.value = doc.title;
             titleEle.onkeydown = (e: KeyboardEvent) => {
@@ -185,14 +184,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
         }
-        CollectionDockingView.Instance.tabMap.add(tab);
     }
 
     /**
      * Adds a document to the presentation view
      **/
     @action
-    public static async PinDoc(doc: Doc, pinProps?: PinProps) {
+    public static PinDoc(doc: Doc, pinProps?: PinProps) {
         //add this new doc to props.Document
 
         // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
@@ -246,19 +244,14 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presMovement = PresMovement.None;
             }
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            const dview = CollectionDockingView.Instance.props.Document;
-            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const tabdocs = await DocListCastAsync(dview[fieldKey]);
-            runInAction(() => {
-                if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
-                    tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
-                    CollectionDockingView.AddSplit(curPres, "right");
-                }
-                PresBox.Instance?._selectedArray.clear();
-                pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-                DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
-                batch.end();
-            });
+            if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
+                CollectionDockingView.AddSplit(curPres, "right");
+                setTimeout(() => DocumentManager.Instance.jumpToDocument(doc, false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
+            }
+            PresBox.Instance?._selectedArray.clear();
+            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
+            setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
+            return pinDoc;
         }
     }
 
@@ -282,7 +275,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     componentWillUnmount() {
         this._tabReaction?.();
         this._view && DocumentManager.Instance.RemoveView(this._view);
-        this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
 
         this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
     }
@@ -430,9 +422,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             }} ref={ref => {
                 if (this._mainCont = ref) {
                     if (this._lastTab) {
-                        console.log("DUP tab")
                         this._view && DocumentManager.Instance.RemoveView(this._view);
-                        CollectionDockingView.Instance.tabMap.delete(this._lastTab);
                     }
                     this._lastTab = this.tab;
                     (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index befd2020c..09f05f69a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -609,10 +609,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     }
 
-    // TODO: currently doc focus works, but can't seem to edit title
-    // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
     onChildClick = () => {
-        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.());
+        return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
     }
 
     onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 77cf0fc84..5f927a2a9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1489,7 +1489,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }));
     }
 
-    replaceCanvases = (oldDiv: HTMLElement, newDiv: HTMLElement) => {
+    static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
         if (oldDiv.childNodes && newDiv) {
             for (let i = 0; i < oldDiv.childNodes.length; i++) {
                 this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
@@ -1508,32 +1508,39 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     }
 
-    updateIcon = () => {
-        const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
+    updateIcon = () => CollectionFreeFormView.UpdateIcon(
+        this.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
+        this.props.docViewPath().lastElement().ContentDiv!,
+        this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), 
+        this.props.PanelWidth(), this.props.PanelHeight(), 0, 
+        (iconFile, nativeWidth, nativeHeight) => {
+                    this.dataDoc.icon = new ImageField(iconFile);
+                    this.dataDoc["icon-nativeWidth"] = nativeWidth;
+                    this.dataDoc["icon-nativeHeight"] = nativeHeight;
+        });
+
+    public static UpdateIcon(filename:string, docViewContent:HTMLElement, width: number, height: number, 
+        panelWidth:number, panelHeight: number, scrollTop:number, cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) {
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
-        newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
-        newDiv.style.height = (this.layoutDoc[HeightSym]()).toString();
+        newDiv.style.width = width.toString();
+        newDiv.style.height = height.toString();
         this.replaceCanvases(docViewContent, newDiv);
-        const htmlString = this._mainCont && new XMLSerializer().serializeToString(newDiv);
-        const nativeWidth = this.layoutDoc[WidthSym]();
-        const nativeHeight = this.layoutDoc[HeightSym]();
-
+        const htmlString = new XMLSerializer().serializeToString(newDiv);
+        const nativeWidth = width;
+        const nativeHeight = height;
         CreateImage(
-            "",
+            Utils.prepend(""),
             document.styleSheets,
             htmlString,
             nativeWidth,
-            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
-            NumCast(this.layoutDoc._scrollTop)
+            nativeWidth * panelHeight / panelWidth,
+            scrollTop
         ).then
             ((data_url: any) => {
-                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
-                    returnedfilename => setTimeout(action(() => {
-
-                        this.dataDoc.icon = new ImageField(returnedfilename);
-                        this.dataDoc["icon-nativeWidth"] = nativeWidth;
-                        this.dataDoc["icon-nativeHeight"] = nativeHeight;
-                    }), 500));
+                VideoBox.convertDataUri(data_url, filename).then(
+                    returnedfilename => setTimeout(() => {
+                        cb(returnedfilename as string, nativeWidth, nativeHeight);
+                    }, 500));
             })
             .catch(function (error: any) {
                 console.error('oops, something went wrong!', error);
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index d9524dd6e..f3f1bcf5c 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -214,8 +214,8 @@ var ForeignHtmlRenderer = function (styleSheets) {
                 .replace(/noscript/g, "div").replace(/<div class="mediaset"><\/div>/g, "")  // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s.  But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
                 .replace(/<link[^>]*>/g, "")  // don't need to keep any linked style sheets because we've already processed all style sheets above
                 .replace(/srcset="([^ "]*)[^"]*"/g, "src=\"$1\""); // instead of converting each item in the srcset to a data url, just convert the first one and use that
-            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml);
-            const fetchedResources = await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml);
+            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith("data:"));
+            const fetchedResources = webUrl ? await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml) : [];
             for (let i = 0; i < fetchedResources.length; i++) {
                 const r = fetchedResources[i];
                 if (r.resourceUrl) {
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index be248ab92..e486bcb52 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -48,7 +48,7 @@ export class TopBar extends React.Component {
                                 await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
                                 batch.end();
                             }}>
-                                {"New"}<FontAwesomeIcon icon="plus" />
+                                New<FontAwesomeIcon icon="plus" />
                             </div>
                             </Tooltip>
                             <Tooltip title={<div className="dash-tooltip">Work on a copy of the dashboard layout</div>} placement="bottom">
@@ -57,12 +57,12 @@ export class TopBar extends React.Component {
                                     await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
                                     batch.end();
                                 }}>
-                                    {"Snapshot"}<FontAwesomeIcon icon="camera" />
+                                    Snapshot<FontAwesomeIcon icon="camera" />
                                 </div>
                             </Tooltip>
                             <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
                                 <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
-                                    {"Explore"}<FontAwesomeIcon icon="map" />
+                                    Explore<FontAwesomeIcon icon="map" />
                                 </div>
                             </Tooltip>
                         </div>
-- 
cgit v1.2.3-70-g09d2


From 58c2085ceb20cd2527ac69088efb7a46b20f8434 Mon Sep 17 00:00:00 2001
From: Jenny Yu <jennyyu212@outlook.com>
Date: Wed, 8 Jun 2022 00:49:12 -0700
Subject: styling: pres element box

---
 src/client/views/DocumentDecorations.tsx          |  44 +-
 src/client/views/collections/TabDocView.tsx       | 904 +++++++++++-----------
 src/client/views/nodes/trails/PresElementBox.scss | 338 ++++----
 src/client/views/nodes/trails/PresElementBox.tsx  |  20 +-
 4 files changed, 656 insertions(+), 650 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0e9bf50fc..b666b5977 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -153,29 +153,29 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
       _deleteAfterIconify = false;
       _iconifyBatch: UndoManager.Batch | undefined;
       onCloseClick = (forceDeleteOrIconify: boolean | undefined) => {
-            if (this.canDelete) {
-                  const views = SelectionManager.Views().slice().filter(v => v);
-                  if (forceDeleteOrIconify === false && this._iconifyBatch) return;
-                  this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false;
-                  if (!this._iconifyBatch) {
-                        this._iconifyBatch = UndoManager.StartBatch("iconifying");
-                  } else {
-                        forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
-                  }
-                  var iconifyingCount = views.length;
-                  const finished = action((force?: boolean) => {
-                        if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
-                              if (this._deleteAfterIconify) {
-                                    views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
-                                    SelectionManager.DeselectAll();
-                              }
-                              this._iconifyBatch?.end();
-                              this._iconifyBatch = undefined;
-                        }
-                  });
-                  if (forceDeleteOrIconify) finished(forceDeleteOrIconify);
-                  else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
+
+            const views = SelectionManager.Views().slice().filter(v => v);
+            if (forceDeleteOrIconify === false && this._iconifyBatch) return;
+            this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false;
+            if (!this._iconifyBatch) {
+                  this._iconifyBatch = UndoManager.StartBatch("iconifying");
+            } else {
+                  forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
             }
+            var iconifyingCount = views.length;
+            const finished = action((force?: boolean) => {
+                  if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
+                        if (this._deleteAfterIconify) {
+                              views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
+                              SelectionManager.DeselectAll();
+                        }
+                        this._iconifyBatch?.end();
+                        this._iconifyBatch = undefined;
+                  }
+            });
+            if (forceDeleteOrIconify) finished(forceDeleteOrIconify);
+            else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
+
       }
       onMaximizeDown = (e: React.PointerEvent) => {
             setupMoveUpEvents(this, e, () => {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 2b78b20ea..39add0534 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -38,490 +38,490 @@ import React = require("react");
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
-    documentId: FieldId;
-    glContainer: any;
+      documentId: FieldId;
+      glContainer: any;
 }
 @observer
 export class TabDocView extends React.Component<TabDocViewProps> {
-    _mainCont: HTMLDivElement | null = null;
-    _tabReaction: IReactionDisposer | undefined;
-    @observable _activated: boolean = false;
-    @observable _panelWidth = 0;
-    @observable _panelHeight = 0;
-    @observable _isActive: boolean = false;
-    @observable _document: Doc | undefined;
-    @observable _view: DocumentView | undefined;
+      _mainCont: HTMLDivElement | null = null;
+      _tabReaction: IReactionDisposer | undefined;
+      @observable _activated: boolean = false;
+      @observable _panelWidth = 0;
+      @observable _panelHeight = 0;
+      @observable _isActive: boolean = false;
+      @observable _document: Doc | undefined;
+      @observable _view: DocumentView | undefined;
 
-    @computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
-    @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); }
-    @computed get tabTextColor() { return this._document?.type === DocumentType.PRES ? "black" : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); }
-    // @computed get renderBounds() {
-    //     const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
-    //     const xbounds = bounds[2] - bounds[0];
-    //     const ybounds = bounds[3] - bounds[1];
-    //     const dim = Math.max(xbounds, ybounds);
-    //     return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim };
-    // }
+      @computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
+      @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); }
+      @computed get tabTextColor() { return this._document?.type === DocumentType.PRES ? "black" : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); }
+      // @computed get renderBounds() {
+      //     const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
+      //     const xbounds = bounds[2] - bounds[0];
+      //     const ybounds = bounds[3] - bounds[1];
+      //     const dim = Math.max(xbounds, ybounds);
+      //     return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim };
+      // }
 
-    get stack() { return (this.props as any).glContainer.parent.parent; }
-    get tab() { return (this.props as any).glContainer.tab; }
-    get view() { return this._view; }
+      get stack() { return (this.props as any).glContainer.parent.parent; }
+      get tab() { return (this.props as any).glContainer.tab; }
+      get view() { return this._view; }
 
-    @action
-    init = (tab: any, doc: Opt<Doc>) => {
-        if (tab.contentItem === tab.header.parent.getActiveContentItem()) this._activated = true;
-        if (tab.DashDoc !== doc && doc && tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
-            tab._disposers = {} as { [name: string]: IReactionDisposer };
-            tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
-            tab.DashDoc = doc;
-            CollectionDockingView.Instance.tabMap.add(tab);
-            const iconType: IconProp = Doc.toIcon(doc);
-            // setup the title element and set its size according to the # of chars in the title.  Show the full title when clicked.
-            const titleEle = tab.titleElement[0];
-            const iconWrap = document.createElement("div");
-            const closeWrap = document.createElement("div");
+      @action
+      init = (tab: any, doc: Opt<Doc>) => {
+            if (tab.contentItem === tab.header.parent.getActiveContentItem()) this._activated = true;
+            if (tab.DashDoc !== doc && doc && tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
+                  tab._disposers = {} as { [name: string]: IReactionDisposer };
+                  tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
+                  tab.DashDoc = doc;
+                  CollectionDockingView.Instance.tabMap.add(tab);
+                  const iconType: IconProp = Doc.toIcon(doc);
+                  // setup the title element and set its size according to the # of chars in the title.  Show the full title when clicked.
+                  const titleEle = tab.titleElement[0];
+                  const iconWrap = document.createElement("div");
+                  const closeWrap = document.createElement("div");
 
 
-            titleEle.size = StrCast(doc.title).length + 3;
-            titleEle.value = doc.title;
-            titleEle.onkeydown = (e: KeyboardEvent) => {
-                e.stopPropagation();
-            };
-            titleEle.onchange = undoBatch(action((e: any) => {
-                titleEle.size = e.currentTarget.value.length + 3;
-                Doc.GetProto(doc).title = e.currentTarget.value;
-            }));
+                  titleEle.size = StrCast(doc.title).length + 3;
+                  titleEle.value = doc.title;
+                  titleEle.onkeydown = (e: KeyboardEvent) => {
+                        e.stopPropagation();
+                  };
+                  titleEle.onchange = undoBatch(action((e: any) => {
+                        titleEle.size = e.currentTarget.value.length + 3;
+                        Doc.GetProto(doc).title = e.currentTarget.value;
+                  }));
 
-            const dragBtnDown = (e: React.PointerEvent) => {
-                setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction);
-            };
+                  const dragBtnDown = (e: React.PointerEvent) => {
+                        setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction);
+                  };
 
 
-            if (tab.element[0].children[1].children.length === 1) {
-                iconWrap.className = "lm_iconWrap";
-                iconWrap.id = "lm_iconWrap";
-                closeWrap.className = "lm_iconWrap";
-                closeWrap.id = "lm_closeWrap";
-                closeWrap.onclick = (e: MouseEvent) => {
-                    tab.header.parent.contentItem.remove();
-                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
-                    Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
-                };
-                const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
-                const closeIcon = <FontAwesomeIcon icon={"times"} />;
-                ReactDOM.render(docIcon, iconWrap);
-                ReactDOM.render(closeIcon, closeWrap);
-                // tab.element[0].append(closeWrap);
-                tab.element[0].prepend(iconWrap);
-                tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
-                    ({ layer, color }) => {
-                        // console.log("TabDocView: " + this.tabColor);
-                        // console.log("lightOrDark: " + lightOrDark(this.tabColor));
-                        const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color
-                        titleEle.style.color = textColor;
-                        titleEle.style.backgroundColor = "transparent";
-                        iconWrap.style.color = textColor;
-                        closeWrap.style.color = textColor;
-                        moreInfoDrag.style.backgroundColor = textColor;
-                        tab.element[0].style.background = !layer ? color : "dimgrey";
-                    }, { fireImmediately: true });
-            }
-            // shifts the focus to this tab when another tab is dragged over it
-            tab.element[0].onmouseenter = (e: MouseEvent) => {
-                if (SnappingManager.GetIsDragging() && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
-                    tab.header.parent.setActiveContentItem(tab.contentItem);
-                    tab.setActive(true);
-                }
-            };
-
+                  if (tab.element[0].children[1].children.length === 1) {
+                        iconWrap.className = "lm_iconWrap";
+                        iconWrap.id = "lm_iconWrap";
+                        closeWrap.className = "lm_iconWrap";
+                        closeWrap.id = "lm_closeWrap";
+                        closeWrap.onclick = (e: MouseEvent) => {
+                              tab.header.parent.contentItem.remove();
+                              Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
+                              Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
+                        };
+                        const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
+                        const closeIcon = <FontAwesomeIcon icon={"times"} />;
+                        ReactDOM.render(docIcon, iconWrap);
+                        ReactDOM.render(closeIcon, closeWrap);
+                        // tab.element[0].append(closeWrap);
+                        tab.element[0].prepend(iconWrap);
+                        tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
+                              ({ layer, color }) => {
+                                    // console.log("TabDocView: " + this.tabColor);
+                                    // console.log("lightOrDark: " + lightOrDark(this.tabColor));
+                                    const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color
+                                    titleEle.style.color = textColor;
+                                    titleEle.style.backgroundColor = "transparent";
+                                    iconWrap.style.color = textColor;
+                                    closeWrap.style.color = textColor;
+                                    moreInfoDrag.style.backgroundColor = textColor;
+                                    tab.element[0].style.background = !layer ? color : "dimgrey";
+                              }, { fireImmediately: true });
+                  }
+                  // shifts the focus to this tab when another tab is dragged over it
+                  tab.element[0].onmouseenter = (e: MouseEvent) => {
+                        if (SnappingManager.GetIsDragging() && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
+                              tab.header.parent.setActiveContentItem(tab.contentItem);
+                              tab.setActive(true);
+                        }
+                  };
 
-            // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected
-            titleEle.onpointerdown = action((e: any) => {
-                if (e.target.className !== "lm_iconWrap") {
-                    if (this.view) SelectionManager.SelectView(this.view, false);
-                    else this._activated = true;
-                    if (Date.now() - titleEle.lastClick < 1000) titleEle.select();
-                    titleEle.lastClick = Date.now();
-                    (document.activeElement !== titleEle) && titleEle.focus();
-                }
-            });
-            tab._disposers.selectionDisposer = reaction(() => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc),
-                action((selected) => {
-                    if (selected) this._activated = true;
-                    const toggle = tab.element[0].children[1].children[0] as HTMLInputElement;
-                    selected && tab.contentItem !== tab.header.parent.getActiveContentItem() &&
-                        UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch");
-                    // toggle.style.fontWeight = selected ? "bold" : "";
-                    // toggle.style.textTransform = selected ? "uppercase" : "";
-                }));
 
-            //attach the selection doc buttons menu to the drag handle
-            const stack: HTMLDivElement = tab.contentItem.parent;
-            const header: HTMLDivElement = tab;
-            stack.onscroll = action((e: any) => {
-                console.log('scrolling...');
-            });
-            const moreInfoDrag = document.createElement("div");
-            moreInfoDrag.className = "lm_iconWrap";
-            tab._disposers.buttonDisposer = reaction(() => this.view, view =>
-                view && [ReactDOM.render(<span><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, moreInfoDrag), tab._disposers.buttonDisposer?.()],
-                { fireImmediately: true });
-            // tab.reactComponents = [moreInfoDrag];
-            // tab.element[0].children[3].before(moreInfoDrag);
+                  // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected
+                  titleEle.onpointerdown = action((e: any) => {
+                        if (e.target.className !== "lm_iconWrap") {
+                              if (this.view) SelectionManager.SelectView(this.view, false);
+                              else this._activated = true;
+                              if (Date.now() - titleEle.lastClick < 1000) titleEle.select();
+                              titleEle.lastClick = Date.now();
+                              (document.activeElement !== titleEle) && titleEle.focus();
+                        }
+                  });
+                  tab._disposers.selectionDisposer = reaction(() => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc),
+                        action((selected) => {
+                              if (selected) this._activated = true;
+                              const toggle = tab.element[0].children[1].children[0] as HTMLInputElement;
+                              selected && tab.contentItem !== tab.header.parent.getActiveContentItem() &&
+                                    UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch");
+                              // toggle.style.fontWeight = selected ? "bold" : "";
+                              // toggle.style.textTransform = selected ? "uppercase" : "";
+                        }));
 
-            // highlight the tab when the tab document is brushed in any part of the UI
-            tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
-                //titleEle.value = title;
-                // titleEle.style.padding = degree ? 0 : 2;
-                // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
-            }, { fireImmediately: true });
+                  //attach the selection doc buttons menu to the drag handle
+                  const stack: HTMLDivElement = tab.contentItem.parent;
+                  const header: HTMLDivElement = tab;
+                  stack.onscroll = action((e: any) => {
+                        console.log('scrolling...');
+                  });
+                  const moreInfoDrag = document.createElement("div");
+                  moreInfoDrag.className = "lm_iconWrap";
+                  tab._disposers.buttonDisposer = reaction(() => this.view, view =>
+                        view && [ReactDOM.render(<span><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, moreInfoDrag), tab._disposers.buttonDisposer?.()],
+                        { fireImmediately: true });
+                  // tab.reactComponents = [moreInfoDrag];
+                  // tab.element[0].children[3].before(moreInfoDrag);
 
-            // clean up the tab when it is closed
-            tab.closeElement.off('click') //unbind the current click handler
-                .click(function () {
-                    Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
-                    SelectionManager.DeselectAll();
-                    UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
-                });
-        }
-    }
+                  // highlight the tab when the tab document is brushed in any part of the UI
+                  tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
+                        //titleEle.value = title;
+                        // titleEle.style.padding = degree ? 0 : 2;
+                        // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
+                  }, { fireImmediately: true });
 
-    /**
-     * Adds a document to the presentation view
-     **/
-    @action
-    public static async PinDoc(doc: Doc, pinProps?: PinProps) {
-        if (pinProps?.unpin) console.log('TODO: Remove UNPIN from this location');
-        //add this new doc to props.Document
-        const curPres = CurrentUserUtils.ActivePresentation;
-        if (curPres) {
-            if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const batch = UndoManager.StartBatch("pinning doc");
-            const pinDoc = Doc.MakeAlias(doc);
-            pinDoc.presentationTargetDoc = doc;
-            pinDoc.title = doc.title + " - Slide";
-            pinDoc.data = new List<Doc>();  // the children of the alias' layout are the presentation slide children.  the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
-            pinDoc.presMovement = PresMovement.Zoom;
-            pinDoc.groupWithUp = false;
-            pinDoc.context = curPres;
-            // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
-            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
-            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
-            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
-            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
-            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
-            pinDoc.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 presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
-            const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
-            Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
-            if (!pinProps?.audioRange && duration !== undefined) {
-                pinDoc.mediaStart = "manual";
-                pinDoc.mediaStop = "manual";
-                pinDoc.presStartTime = NumCast(doc.clipStart);
-                pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
+                  // clean up the tab when it is closed
+                  tab.closeElement.off('click') //unbind the current click handler
+                        .click(function () {
+                              Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+                              SelectionManager.DeselectAll();
+                              UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
+                        });
             }
-            //save position
-            if (pinProps?.setPosition || pinDoc.isInkMask) {
-                pinDoc.setPosition = true;
-                pinDoc.y = doc.y;
-                pinDoc.x = doc.x;
-                pinDoc.presHideAfter = true;
-                pinDoc.presHideBefore = true;
-                pinDoc.title = doc.title + " (move)";
-                pinDoc.presMovement = PresMovement.None;
-            }
-            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            const dview = CollectionDockingView.Instance.props.Document;
-            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const sublists = DocListCast(dview[fieldKey]);
-            const tabs = Cast(sublists[0], Doc, null);
-            const tabdocs = await DocListCastAsync(tabs?.data);
-            runInAction(() => {
-                if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
-                    tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
-                    CollectionDockingView.AddSplit(curPres, "right");
-                }
-                PresBox.Instance?._selectedArray.clear();
-                pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-                DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
-                batch.end();
-            });
-        }
-    }
+      }
 
-    componentDidMount() {
-        new _global.ResizeObserver(action((entries: any) => {
-            for (const entry of entries) {
-                this._panelWidth = entry.contentRect.width;
-                this._panelHeight = entry.contentRect.height;
+      /**
+       * Adds a document to the presentation view
+       **/
+      @action
+      public static async PinDoc(doc: Doc, pinProps?: PinProps) {
+            if (pinProps?.unpin) console.log('TODO: Remove UNPIN from this location');
+            //add this new doc to props.Document
+            const curPres = CurrentUserUtils.ActivePresentation;
+            if (curPres) {
+                  if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
+                  const batch = UndoManager.StartBatch("pinning doc");
+                  const pinDoc = Doc.MakeAlias(doc);
+                  pinDoc.presentationTargetDoc = doc;
+                  pinDoc.title = doc.title;
+                  pinDoc.data = new List<Doc>();  // the children of the alias' layout are the presentation slide children.  the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
+                  pinDoc.presMovement = PresMovement.Zoom;
+                  pinDoc.groupWithUp = false;
+                  pinDoc.context = curPres;
+                  // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
+                  pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+                  pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
+                  pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
+                  pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
+                  pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
+                  pinDoc.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 presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
+                  const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
+                  Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
+                  if (!pinProps?.audioRange && duration !== undefined) {
+                        pinDoc.mediaStart = "manual";
+                        pinDoc.mediaStop = "manual";
+                        pinDoc.presStartTime = NumCast(doc.clipStart);
+                        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;
+                        pinDoc.title = doc.title + " (move)";
+                        pinDoc.presMovement = PresMovement.None;
+                  }
+                  if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+                  const dview = CollectionDockingView.Instance.props.Document;
+                  const fieldKey = CollectionDockingView.Instance.props.fieldKey;
+                  const sublists = DocListCast(dview[fieldKey]);
+                  const tabs = Cast(sublists[0], Doc, null);
+                  const tabdocs = await DocListCastAsync(tabs?.data);
+                  runInAction(() => {
+                        if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
+                              tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
+                              CollectionDockingView.AddSplit(curPres, "right");
+                        }
+                        PresBox.Instance?._selectedArray.clear();
+                        pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
+                        DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
+                        batch.end();
+                  });
             }
-        })).observe(this.props.glContainer._element[0]);
-        this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
-        this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined);
-        // this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }),
-        //     ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""),
-        //     { fireImmediately: true });
-    }
+      }
 
-    componentWillUnmount() {
-        this._tabReaction?.();
-        this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
+      componentDidMount() {
+            new _global.ResizeObserver(action((entries: any) => {
+                  for (const entry of entries) {
+                        this._panelWidth = entry.contentRect.width;
+                        this._panelHeight = entry.contentRect.height;
+                  }
+            })).observe(this.props.glContainer._element[0]);
+            this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
+            this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined);
+            // this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }),
+            //     ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""),
+            //     { fireImmediately: true });
+      }
 
-        this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
-    }
+      componentWillUnmount() {
+            this._tabReaction?.();
+            this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
 
-    @action.bound
-    private onActiveContentItemChanged(contentItem: any) {
-        if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) {
-            this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab;
-            !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
-        }
-    }
+            this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
+      }
+
+      @action.bound
+      private onActiveContentItemChanged(contentItem: any) {
+            if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) {
+                  this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab;
+                  !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
+            }
+      }
 
-    // adds a tab to the layout based on the locaiton parameter which can be:
-    //  close[:{left,right,top,bottom}]  - e.g., "close" will close the tab, "close:left" will close the left tab,
-    //  add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right
-    //  replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents,
-    //                                  "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name,
-    //                                   "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right
-    //  inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack
-    addDocTab = (doc: Doc, location: string) => {
-        SelectionManager.DeselectAll();
-        const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
-        const locationParams = locationFields.length > 1 ? locationFields[1] : "";
-        switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
-            case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
-            case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
-            case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
-            case "lightbox": return LightboxView.AddDocTab(doc, location);
-            case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
-            case "inPlace":
-            case "add":
-            default:
-                return CollectionDockingView.AddSplit(doc, locationParams, this.stack);
-        }
-    }
-    remDocTab = (doc: Doc | Doc[]) => {
-        if (doc === this._document) {
+      // adds a tab to the layout based on the locaiton parameter which can be:
+      //  close[:{left,right,top,bottom}]  - e.g., "close" will close the tab, "close:left" will close the left tab,
+      //  add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right
+      //  replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents,
+      //                                  "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name,
+      //                                   "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right
+      //  inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack
+      addDocTab = (doc: Doc, location: string) => {
             SelectionManager.DeselectAll();
-            CollectionDockingView.CloseSplit(this._document);
-            return true;
-        }
-        return false;
-    }
+            const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
+            const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+            switch (locationFields[0]) {
+                  case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+                  case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
+                  case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
+                  case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
+                  case "lightbox": return LightboxView.AddDocTab(doc, location);
+                  case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
+                  case "inPlace":
+                  case "add":
+                  default:
+                        return CollectionDockingView.AddSplit(doc, locationParams, this.stack);
+            }
+      }
+      remDocTab = (doc: Doc | Doc[]) => {
+            if (doc === this._document) {
+                  SelectionManager.DeselectAll();
+                  CollectionDockingView.CloseSplit(this._document);
+                  return true;
+            }
+            return false;
+      }
 
-    getCurrentFrame = () => {
-        return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame);
-    }
-    @action
-    focusFunc = (doc: Doc, options?: DocFocusOptions) => {
-        const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap;
-        if (shrinkwrap && this._document) {
-            const focusSpeed = 1000;
-            shrinkwrap();
-            this._document._viewTransition = `transform ${focusSpeed}ms`;
-            setTimeout(action(() => {
-                this._document!._viewTransition = undefined;
-                options?.afterFocus?.(false);
-            }), focusSpeed);
-        } else {
-            options?.afterFocus?.(false);
-        }
-        if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
-            this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
-        }
-    }
-    active = () => this._isActive;
-    @observable _forceInvalidateScreenToLocal = 0;
-    ScreenToLocalTransform = () => {
-        this._forceInvalidateScreenToLocal;
-        const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
-        return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY);
-    }
-    PanelWidth = () => this._panelWidth;
-    PanelHeight = () => this._panelHeight;
-    miniMapColor = () => this.tabColor;
-    tabView = () => this._view;
-    disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform);
-    hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
+      getCurrentFrame = () => {
+            return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame);
+      }
+      @action
+      focusFunc = (doc: Doc, options?: DocFocusOptions) => {
+            const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap;
+            if (shrinkwrap && this._document) {
+                  const focusSpeed = 1000;
+                  shrinkwrap();
+                  this._document._viewTransition = `transform ${focusSpeed}ms`;
+                  setTimeout(action(() => {
+                        this._document!._viewTransition = undefined;
+                        options?.afterFocus?.(false);
+                  }), focusSpeed);
+            } else {
+                  options?.afterFocus?.(false);
+            }
+            if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
+                  this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
+            }
+      }
+      active = () => this._isActive;
+      @observable _forceInvalidateScreenToLocal = 0;
+      ScreenToLocalTransform = () => {
+            this._forceInvalidateScreenToLocal;
+            const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
+            return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY);
+      }
+      PanelWidth = () => this._panelWidth;
+      PanelHeight = () => this._panelHeight;
+      miniMapColor = () => this.tabColor;
+      tabView = () => this._view;
+      disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform);
+      hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
 
-    @computed get docView() {
-        return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
-            <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)}
-                renderDepth={0}
-                Document={this._document}
-                DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                onBrowseClick={MainView.Instance.exploreMode}
-                isContentActive={returnTrue}
-                PanelWidth={this.PanelWidth}
-                PanelHeight={this.PanelHeight}
-                styleProvider={DefaultStyleProvider}
-                docFilters={CollectionDockingView.Instance.childDocFilters}
-                docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
-                searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
-                addDocument={undefined}
-                removeDocument={this.remDocTab}
-                addDocTab={this.addDocTab}
-                ScreenToLocalTransform={this.ScreenToLocalTransform}
-                dontCenter={"y"}
-                rootSelected={returnTrue}
-                whenChildContentsActiveChanged={emptyFunction}
-                focus={this.focusFunc}
-                docViewPath={returnEmptyDoclist}
-                bringToFront={emptyFunction}
-                pinToPres={TabDocView.PinDoc} />
-                <TabMinimapView key="minimap"
-                    hideMinimap={this.hideMinimap}
-                    addDocTab={this.addDocTab}
-                    PanelHeight={this.PanelHeight}
-                    PanelWidth={this.PanelWidth}
-                    background={this.miniMapColor}
-                    document={this._document}
-                    tabView={this.tabView} />
-                <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? "Open minimap" : "Close minimap"}</div>}>
-                    <div className="miniMap-hidden"
-                        style={{
-                            display: this.disableMinimap() || this._document._viewType !== "freeform" ? "none" : undefined,
-                            color: this._document.hideMinimap ? Colors.BLACK : Colors.WHITE,
-                            backgroundColor: this._document.hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
-                            boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined
-                        }}
-                        onPointerDown={e => e.stopPropagation()}
-                        onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
-                        <FontAwesomeIcon icon={"globe-asia"} size="lg" />
-                    </div>
-                </Tooltip>
-            </>;
-    }
+      @computed get docView() {
+            return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
+                  <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)}
+                        renderDepth={0}
+                        Document={this._document}
+                        DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                        onBrowseClick={MainView.Instance.exploreMode}
+                        isContentActive={returnTrue}
+                        PanelWidth={this.PanelWidth}
+                        PanelHeight={this.PanelHeight}
+                        styleProvider={DefaultStyleProvider}
+                        docFilters={CollectionDockingView.Instance.childDocFilters}
+                        docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
+                        searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
+                        addDocument={undefined}
+                        removeDocument={this.remDocTab}
+                        addDocTab={this.addDocTab}
+                        ScreenToLocalTransform={this.ScreenToLocalTransform}
+                        dontCenter={"y"}
+                        rootSelected={returnTrue}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        focus={this.focusFunc}
+                        docViewPath={returnEmptyDoclist}
+                        bringToFront={emptyFunction}
+                        pinToPres={TabDocView.PinDoc} />
+                        <TabMinimapView key="minimap"
+                              hideMinimap={this.hideMinimap}
+                              addDocTab={this.addDocTab}
+                              PanelHeight={this.PanelHeight}
+                              PanelWidth={this.PanelWidth}
+                              background={this.miniMapColor}
+                              document={this._document}
+                              tabView={this.tabView} />
+                        <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? "Open minimap" : "Close minimap"}</div>}>
+                              <div className="miniMap-hidden"
+                                    style={{
+                                          display: this.disableMinimap() || this._document._viewType !== "freeform" ? "none" : undefined,
+                                          color: this._document.hideMinimap ? Colors.BLACK : Colors.WHITE,
+                                          backgroundColor: this._document.hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
+                                          boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined
+                                    }}
+                                    onPointerDown={e => e.stopPropagation()}
+                                    onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
+                                    <FontAwesomeIcon icon={"globe-asia"} size="lg" />
+                              </div>
+                        </Tooltip>
+                  </>;
+      }
 
-    render() {
-        return (
-            <div className="collectionDockingView-content" style={{
-                fontFamily: Doc.UserDoc().renderStyle === "comic" ? "Comic Sans MS" : undefined,
-                height: "100%", width: "100%"
-            }} ref={ref => {
-                if (this._mainCont = ref) {
-                    (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
-                    DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
-                    new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
-                }
-            }} >
-                {this.docView}
-            </div >
-        );
-    }
+      render() {
+            return (
+                  <div className="collectionDockingView-content" style={{
+                        fontFamily: Doc.UserDoc().renderStyle === "comic" ? "Comic Sans MS" : undefined,
+                        height: "100%", width: "100%"
+                  }} ref={ref => {
+                        if (this._mainCont = ref) {
+                              (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
+                              DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
+                              new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
+                        }
+                  }} >
+                        {this.docView}
+                  </div >
+            );
+      }
 }
 
 interface TabMinimapViewProps {
-    document: Doc;
-    hideMinimap: () => boolean;
-    tabView: () => DocumentView | undefined;
-    addDocTab: (doc: Doc, where: string) => boolean;
-    PanelWidth: () => number;
-    PanelHeight: () => number;
-    background: () => string;
+      document: Doc;
+      hideMinimap: () => boolean;
+      tabView: () => DocumentView | undefined;
+      addDocTab: (doc: Doc, where: string) => boolean;
+      PanelWidth: () => number;
+      PanelHeight: () => number;
+      background: () => string;
 }
 @observer
 export class TabMinimapView extends React.Component<TabMinimapViewProps> {
-    static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
-        if (doc) {
-            switch (property.split(":")[0]) {
-                default: return DefaultStyleProvider(doc, props, property);
-                case StyleProp.PointerEvents: return "none";
-                case StyleProp.DocContents:
-                    const background = ((type: DocumentType) => {
-                        switch (type) {
-                            case DocumentType.PDF: return "pink";
-                            case DocumentType.AUDIO: return "lightgreen";
-                            case DocumentType.WEB: return "brown";
-                            case DocumentType.IMG: return "blue";
-                            case DocumentType.MAP: return "orange";
-                            case DocumentType.VID: return "purple";
-                            case DocumentType.RTF: return "yellow";
-                            case DocumentType.COL: return undefined;
-                            default: return "gray";
-                        }
-                    })(doc.type as DocumentType);
-                    return !background ?
-                        undefined :
-                        <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;
+      static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
+            if (doc) {
+                  switch (property.split(":")[0]) {
+                        default: return DefaultStyleProvider(doc, props, property);
+                        case StyleProp.PointerEvents: return "none";
+                        case StyleProp.DocContents:
+                              const background = ((type: DocumentType) => {
+                                    switch (type) {
+                                          case DocumentType.PDF: return "pink";
+                                          case DocumentType.AUDIO: return "lightgreen";
+                                          case DocumentType.WEB: return "brown";
+                                          case DocumentType.IMG: return "blue";
+                                          case DocumentType.MAP: return "orange";
+                                          case DocumentType.VID: return "purple";
+                                          case DocumentType.RTF: return "yellow";
+                                          case DocumentType.COL: return undefined;
+                                          default: return "gray";
+                                    }
+                              })(doc.type as DocumentType);
+                              return !background ?
+                                    undefined :
+                                    <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;
+                  }
             }
-        }
-    }
-    @computed get renderBounds() {
-        const compView = this.props.tabView()?.ComponentView as CollectionFreeFormView;
-        const bounds = compView?.freeformData?.(true)?.bounds;
-        if (!bounds) return undefined;
-        const xbounds = bounds.r - bounds.x;
-        const ybounds = bounds.b - bounds.y;
-        const dim = Math.max(xbounds, ybounds);
-        return { l: bounds.x + xbounds / 2 - dim / 2, t: bounds.y + ybounds / 2 - dim / 2, cx: bounds.x + xbounds / 2, cy: bounds.y + ybounds / 2, dim };
-    }
-    childLayoutTemplate = () => Cast(this.props.document.childLayoutTemplate, Doc, null);
-    returnMiniSize = () => NumCast(this.props.document._miniMapSize, 150);
-    miniDown = (e: React.PointerEvent) => {
-        const doc = this.props.document;
-        const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 };
-        const miniSize = this.returnMiniSize();
-        doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
-            doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
-            doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
-            return false;
-        }), emptyFunction, emptyFunction);
-    }
-    render() {
-        if (!this.renderBounds) return (null);
-        const miniWidth = this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100;
-        const miniHeight = this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100;
-        const miniLeft = 50 + (NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2;
-        const miniTop = 50 + (NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2;
-        const miniSize = this.returnMiniSize();
-        return this.props.hideMinimap() ? (null) :
-            <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
-                <CollectionFreeFormView
-                    Document={this.props.document}
-                    CollectionView={undefined}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
-                    docViewPath={returnEmptyDoclist}
-                    childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
-                    noOverlay={true} // don't render overlay Docs since they won't scale
-                    setHeight={returnFalse}
-                    isContentActive={emptyFunction}
-                    isAnyChildContentActive={returnFalse}
-                    select={emptyFunction}
-                    dropAction={undefined}
-                    isSelected={returnFalse}
-                    dontRegisterView={true}
-                    fieldKey={Doc.LayoutFieldKey(this.props.document)}
-                    bringToFront={emptyFunction}
-                    rootSelected={returnTrue}
-                    addDocument={returnFalse}
-                    moveDocument={returnFalse}
-                    removeDocument={returnFalse}
-                    PanelWidth={this.returnMiniSize}
-                    PanelHeight={this.returnMiniSize}
-                    ScreenToLocalTransform={Transform.Identity}
-                    renderDepth={0}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    focus={DocUtils.DefaultFocus}
-                    styleProvider={TabMinimapView.miniStyleProvider}
-                    addDocTab={this.props.addDocTab}
-                    pinToPres={TabDocView.PinDoc}
-                    docFilters={CollectionDockingView.Instance.childDocFilters}
-                    docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
-                    searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
-                    fitContentsToDoc={returnTrue}
-                />
-                <div className="miniOverlay" onPointerDown={this.miniDown} >
-                    <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
-                </div>
-            </div>;
-    }
+      }
+      @computed get renderBounds() {
+            const compView = this.props.tabView()?.ComponentView as CollectionFreeFormView;
+            const bounds = compView?.freeformData?.(true)?.bounds;
+            if (!bounds) return undefined;
+            const xbounds = bounds.r - bounds.x;
+            const ybounds = bounds.b - bounds.y;
+            const dim = Math.max(xbounds, ybounds);
+            return { l: bounds.x + xbounds / 2 - dim / 2, t: bounds.y + ybounds / 2 - dim / 2, cx: bounds.x + xbounds / 2, cy: bounds.y + ybounds / 2, dim };
+      }
+      childLayoutTemplate = () => Cast(this.props.document.childLayoutTemplate, Doc, null);
+      returnMiniSize = () => NumCast(this.props.document._miniMapSize, 150);
+      miniDown = (e: React.PointerEvent) => {
+            const doc = this.props.document;
+            const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 };
+            const miniSize = this.returnMiniSize();
+            doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
+                  doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
+                  doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
+                  return false;
+            }), emptyFunction, emptyFunction);
+      }
+      render() {
+            if (!this.renderBounds) return (null);
+            const miniWidth = this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100;
+            const miniHeight = this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100;
+            const miniLeft = 50 + (NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2;
+            const miniTop = 50 + (NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2;
+            const miniSize = this.returnMiniSize();
+            return this.props.hideMinimap() ? (null) :
+                  <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
+                        <CollectionFreeFormView
+                              Document={this.props.document}
+                              CollectionView={undefined}
+                              ContainingCollectionView={undefined}
+                              ContainingCollectionDoc={undefined}
+                              docViewPath={returnEmptyDoclist}
+                              childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
+                              noOverlay={true} // don't render overlay Docs since they won't scale
+                              setHeight={returnFalse}
+                              isContentActive={emptyFunction}
+                              isAnyChildContentActive={returnFalse}
+                              select={emptyFunction}
+                              dropAction={undefined}
+                              isSelected={returnFalse}
+                              dontRegisterView={true}
+                              fieldKey={Doc.LayoutFieldKey(this.props.document)}
+                              bringToFront={emptyFunction}
+                              rootSelected={returnTrue}
+                              addDocument={returnFalse}
+                              moveDocument={returnFalse}
+                              removeDocument={returnFalse}
+                              PanelWidth={this.returnMiniSize}
+                              PanelHeight={this.returnMiniSize}
+                              ScreenToLocalTransform={Transform.Identity}
+                              renderDepth={0}
+                              whenChildContentsActiveChanged={emptyFunction}
+                              focus={DocUtils.DefaultFocus}
+                              styleProvider={TabMinimapView.miniStyleProvider}
+                              addDocTab={this.props.addDocTab}
+                              pinToPres={TabDocView.PinDoc}
+                              docFilters={CollectionDockingView.Instance.childDocFilters}
+                              docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
+                              searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
+                              fitContentsToDoc={returnTrue}
+                        />
+                        <div className="miniOverlay" onPointerDown={this.miniDown} >
+                              <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
+                        </div>
+                  </div>;
+      }
 }
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index a178be910..1919071df 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -5,155 +5,157 @@ $slide-background: #d5dce2;
 $slide-active: #5B9FDD;
 
 .presItem-container {
-    cursor: grab;
-    display: grid;
-    grid-template-columns: 20px auto;
-    font-family: Roboto;
-    letter-spacing: normal;
-    position: relative;
-    pointer-events: all;
-    width: 100%;
-    height: 100%;
-    font-weight: 400;
-    -webkit-touch-callout: none;
-    -webkit-user-select: none;
-    -khtml-user-select: none;
-    -moz-user-select: none;
-    -ms-user-select: none;
-    user-select: none;
-    align-items: center;
-
-    .presItem-number {
-        margin-top: 3.5px;
-        font-size: 12px;
-        font-weight: 700;
-        text-align: center;
-        justify-self: center;
-        align-self: flex-start;
-        position: relative;
-        display: inline-block;
-        overflow: hidden;
-    }
+      cursor: grab;
+      display: flex;
+      grid-template-columns: 20px auto;
+      font-family: Roboto;
+      letter-spacing: normal;
+      position: relative;
+      pointer-events: all;
+      width: 100%;
+      height: 100%;
+      font-weight: 400;
+      -webkit-touch-callout: none;
+      -webkit-user-select: none;
+      -khtml-user-select: none;
+      -moz-user-select: none;
+      -ms-user-select: none;
+      user-select: none;
+      align-items: center;
+
+      //     .presItem-number {
+      //         margin-top: 3.5px;
+      //         font-size: 12px;
+      //         font-weight: 700;
+      //         text-align: center;
+      //         justify-self: center;
+      //         align-self: flex-start;
+      //         position: relative;
+      //         display: inline-block;
+      //         overflow: hidden;
+      //     }
 
 }
 
 .presItem-slide {
-    position: relative;
-    background-color: #d5dce2;
-    border-radius: 5px;
-    height: calc(100% - 7px);
-    width: 100%;
-    display: grid;
-    grid-template-rows: 16px 10px auto;
-    grid-template-columns: max-content max-content max-content max-content auto;
-
-    .presItem-name {
-        min-width: 20px;
-        z-index: 300;
-        top: 2px;
-        align-self: center;
-        font-size: 11px;
-        font-family: Roboto;
-        font-weight: 500;
-        position: relative;
-        padding-left: 10px;
-        padding-right: 10px;
-        letter-spacing: normal;
-        width: max-content;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        white-space: pre;
-    }
-
-    .presItem-docName {
-        min-width: 20px;
-        z-index: 300;
-        align-self: center;
-        font-size: 9px;
-        font-family: Roboto;
-        font-weight: 300;
-        position: relative;
-        padding-left: 10px;
-        padding-right: 10px;
-        letter-spacing: normal;
-        width: max-content;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        white-space: pre;
-        grid-row: 2;
-        grid-column: 1/6;
-    }
-
-    .presItem-time {
-        align-self: center;
-        position: relative;
-        padding-right: 10px;
-        top: 1px;
-        font-size: 10;
-        font-weight: 300;
-        font-family: Roboto;
-        z-index: 300;
-        letter-spacing: normal;
-    }
-
-    .presItem-embedded {
-        overflow: hidden;
-        grid-row: 3;
-        grid-column: 1/8;
-        position: relative;
-        display: flex;
-        width: auto;
-        justify-content: center;
-        margin: auto;
-        margin-bottom: 2px;
-        border-bottom-left-radius: 5px;
-        border-bottom-right-radius: 5px;
-    }
-
-    .presItem-embeddedMask {
-        width: 100%;
-        height: 100%;
-        position: absolute;
-        border-radius: 3px;
-        top: 0;
-        left: 0;
-        z-index: 1;
-        overflow: hidden;
-    }
-
-
-    .presItem-slideButtons {
-        display: flex;
-        grid-column: 7;
-        grid-row: 1/3;
-        width: max-content;
-        justify-self: right;
-        justify-content: flex-end;
-
-        .slideButton {
-            cursor: pointer;
+      position: relative;
+      height: 100%;
+      width: 100%;
+      border-bottom: .5px solid grey;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      grid-template-rows: 16px 10px auto;
+      grid-template-columns: max-content max-content max-content max-content auto;
+
+      .presItem-name {
+            display: flex;
+            min-width: 20px;
+            z-index: 300;
+            top: 2px;
+            align-self: center;
+            font-size: 11px;
+            font-family: Roboto;
+            font-weight: 500;
             position: relative;
-            border-radius: 100px;
+            padding-left: 10px;
+            padding-right: 10px;
+            letter-spacing: normal;
+            width: max-content;
+            text-overflow: ellipsis;
+            overflow: hidden;
+            white-space: pre;
+      }
+
+      .presItem-docName {
+            min-width: 20px;
             z-index: 300;
-            width: 18px;
-            height: 18px;
-            display: flex;
-            font-size: 12px;
-            justify-self: center;
             align-self: center;
-            background-color: rgba(0, 0, 0, 0.5);
-            color: white;
+            font-size: 9px;
+            font-family: Roboto;
+            font-weight: 300;
+            position: relative;
+            padding-left: 10px;
+            padding-right: 10px;
+            letter-spacing: normal;
+            width: max-content;
+            text-overflow: ellipsis;
+            overflow: hidden;
+            white-space: pre;
+            grid-row: 2;
+            grid-column: 1/6;
+      }
+
+      .presItem-time {
+            align-self: center;
+            position: relative;
+            padding-right: 10px;
+            top: 1px;
+            font-size: 10;
+            font-weight: 300;
+            font-family: Roboto;
+            z-index: 300;
+            letter-spacing: normal;
+      }
+
+      .presItem-embedded {
+            overflow: hidden;
+            grid-row: 3;
+            grid-column: 1/8;
+            position: relative;
+            display: flex;
+            width: auto;
             justify-content: center;
-            align-items: center;
-            transition: 0.2s;
-            margin-right: 3px;
-        }
-
-        .slideButton:hover {
-            background-color: rgba(0, 0, 0, 1);
-            transform: scale(1.2);
-        }
-    }
+            margin: auto;
+            margin-bottom: 2px;
+            border-bottom-left-radius: 5px;
+            border-bottom-right-radius: 5px;
+      }
+
+      .presItem-embeddedMask {
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            border-radius: 3px;
+            top: 0;
+            left: 0;
+            z-index: 1;
+            overflow: hidden;
+      }
+
+
+      .presItem-slideButtons {
+            display: flex;
+            grid-column: 7;
+            grid-row: 1/3;
+            width: max-content;
+            justify-self: right;
+            justify-content: flex-end;
+
+            .slideButton {
+                  cursor: pointer;
+                  position: relative;
+                  border-radius: 100px;
+                  z-index: 300;
+                  width: 18px;
+                  height: 18px;
+                  display: flex;
+                  font-size: 12px;
+                  justify-self: center;
+                  align-self: center;
+                  background-color: rgba(0, 0, 0, 0.5);
+                  color: white;
+                  justify-content: center;
+                  align-items: center;
+                  transition: 0.2s;
+                  margin-right: 3px;
+            }
+
+            .slideButton:hover {
+                  background-color: rgba(0, 0, 0, 1);
+                  transform: scale(1.2);
+            }
+      }
 }
 
 // .presItem-slide:hover {
@@ -192,42 +194,42 @@ $slide-active: #5B9FDD;
 // }
 
 .presItem-slide.active {
-    box-shadow: 0 0 0px 1.5px $dark-blue;
+      box-shadow: 0 0 0px 1.5px $dark-blue;
 }
 
 .presItem-multiDrag {
-    font-family: Roboto;
-    font-weight: 600;
-    color: white;
-    text-align: center;
-    justify-content: center;
-    align-content: center;
-    width: 100px;
-    height: 30px;
-    position: absolute;
-    background-color: $dark-blue;
-    z-index: 4000;
-    border-radius: 10px;
-    box-shadow: black 0.4vw 0.4vw 0.8vw;
-    line-height: 30px;
+      font-family: Roboto;
+      font-weight: 600;
+      color: white;
+      text-align: center;
+      justify-content: center;
+      align-content: center;
+      width: 100px;
+      height: 30px;
+      position: absolute;
+      background-color: $dark-blue;
+      z-index: 4000;
+      border-radius: 10px;
+      box-shadow: black 0.4vw 0.4vw 0.8vw;
+      line-height: 30px;
 }
 
 .presItem-miniSlide {
-    font-weight: 700;
-    font-size: 12;
-    grid-column: 1/8;
-    align-self: center;
-    justify-self: center;
-    background-color: #d5dce2;
-    width: 26px;
-    text-align: center;
-    height: 26px;
-    line-height: 28px;
-    border-radius: 100%;
+      font-weight: 700;
+      font-size: 12;
+      grid-column: 1/8;
+      align-self: center;
+      justify-self: center;
+      background-color: #d5dce2;
+      width: 26px;
+      text-align: center;
+      height: 26px;
+      line-height: 28px;
+      border-radius: 100%;
 }
 
 .presItem-miniSlide.active {
-    box-shadow: 0 0 0px 1.5px $dark-blue;
+      box-shadow: 0 0 0px 1.5px $dark-blue;
 }
 
 // .presItem-slide:hover {
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f1144b32d..acdf9c43f 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -329,7 +329,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         hideDocumentButtonBar: true,
                         hideDecorationTitle: true,
                         hideOpenButton: true,
-                        hideDeleteButton: true,
+                        // hideDeleteButton: true,
                         cloneFieldFilter:
                               new List<string>(["system"])
                   });
@@ -387,7 +387,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         onPointerDown={this.headerDown}
                         onPointerUp={this.headerUp}
                   >
-                        {miniView ?
+                        {/* {miniView ?
                               // when width is LESS than 110 px 
                               <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
                                     {`${this.indexInPres + 1}.`}
@@ -396,13 +396,17 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                               // when width is MORE than 110 px 
                               <div className="presItem-number">
                                     {`${this.indexInPres + 1}.`}
-                              </div>}
+                              </div>} */}
+                        {/* <div className="presItem-number">
+                              {`${this.indexInPres + 1}.`}
+                        </div> */}
                         {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
                               style={{
                                     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>{`${this.indexInPres + 1}.  `}</div>
                                     <EditableView
                                           ref={this._titleRef}
                                           editing={!isSelected ? false : undefined}
@@ -412,8 +416,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                           SetValue={this.onSetValue}
                                     />
                               </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>
+                              {/* <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="slideButton"
@@ -439,7 +443,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     }
 
 
-                                    {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
+                                    {/* {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={{
@@ -453,7 +457,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                                       <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
                                                 </div>
                                           </div>
-                                    </Tooltip>}
+                                    </Tooltip>} */}
                                     {/* <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
                                           <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
                                     </div></Tooltip> */}
@@ -463,7 +467,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                           <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>
+                              {/* <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>}
                   </div >);
-- 
cgit v1.2.3-70-g09d2


From 5157f0c500b01a635c373cc661a2588ef7506d79 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Jun 2022 12:44:36 -0400
Subject: added context menu back to tab's via the doc icon.  made docicon
 draggable and clickable. removed collectiondockingviewmenu.

---
 src/client/goldenLayout.js                         |  5 ++-
 .../views/collections/CollectionDockingView.tsx    |  3 +-
 .../collections/CollectionDockingViewMenu.scss     | 34 ---------------
 .../collections/CollectionDockingViewMenu.tsx      | 48 --------------------
 src/client/views/collections/TabDocView.tsx        | 51 +++++++++++-----------
 5 files changed, 31 insertions(+), 110 deletions(-)
 delete mode 100644 src/client/views/collections/CollectionDockingViewMenu.scss
 delete mode 100644 src/client/views/collections/CollectionDockingViewMenu.tsx

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

diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 4735f216f..012ee163c 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -458,8 +458,9 @@
                 if (this._bDragging === true) {
                     this._bDragging = false;
                     this.emit('dragStop', oEvent, this._nOriginalX + this._nX);
-                } else if (oEvent.target) { // make title receive pointer events to allow setting insertion position or selecting texst range
-                    if (oEvent.target.className.includes("lm_title_wrap")) {
+                } else  { // make title receive pointer events to allow setting insertion position or selecting texst range
+                    const classname = typeof oEvent.target?.className === "string" ? oEvent.target.className : "";
+                    if (classname.includes("lm_title_wrap")) {
                         oEvent.target.children[0].style.pointerEvents = "all";
                         oEvent.target.children[0].focus();
                     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 745565ff5..476ffaf81 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -362,7 +362,8 @@ export class CollectionDockingView extends CollectionSubView() {
             const htmlTarget = e.target as HTMLElement;
             window.addEventListener("mouseup", this.onPointerUp);
             if (!htmlTarget.closest("*.lm_content") && (htmlTarget.closest("*.lm_tab") || htmlTarget.closest("*.lm_stack"))) {
-                if (!htmlTarget.className.includes("lm_close") && !htmlTarget.className.includes("lm_maximise")) {
+                const className = typeof htmlTarget.className === "string" ? htmlTarget.className : "";
+                if (!className.includes("lm_close") && !className.includes("lm_maximise")) {
                     this._flush = UndoManager.StartBatch("golden layout edit");
                 }
             }
diff --git a/src/client/views/collections/CollectionDockingViewMenu.scss b/src/client/views/collections/CollectionDockingViewMenu.scss
deleted file mode 100644
index 4157f0f7e..000000000
--- a/src/client/views/collections/CollectionDockingViewMenu.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-
-.dockingViewButtonSelector {
-    div {
-        overflow: visible !important;
-    }
-
-    display: inline-block;
-    width:100%;
-    height:100%;
-}
-.dockingViewButtonSelector-flyout {
-    position: relative;
-    z-index: 9999;
-    background-color: #eeeeee;
-    box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
-    color: black;
-
-    padding: 10px;
-    border-radius: 3px;
-    display: inline-block;
-    height: 100%;
-    width: 100%;
-    border-radius: 3px;
-
-    hr {
-        height: 1px;
-        margin: 0px;
-        background-color: gray;
-        border-top: 0px;
-        border-bottom: 0px;
-        border-right: 0px;
-        border-left: 0px;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingViewMenu.tsx b/src/client/views/collections/CollectionDockingViewMenu.tsx
deleted file mode 100644
index 1cab293a8..000000000
--- a/src/client/views/collections/CollectionDockingViewMenu.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { DocumentButtonBar } from "../DocumentButtonBar";
-import { DocumentView } from "../nodes/DocumentView";
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
-
-@observer
-export class CollectionDockingViewMenu extends React.Component<{ views: () => DocumentView[], Stack: any }> {
-    customStylesheet(styles: any) {
-        return {
-            ...styles,
-            panel: {
-                ...styles.panel,
-                minWidth: "100px"
-            },
-        };
-    }
-    _ref = React.createRef<HTMLDivElement>();
-
-    @computed get flyout() {
-        return (
-            <div className="dockingViewButtonSelector-flyout" title=" " ref={this._ref}>
-                <DocumentButtonBar views={this.props.views} stack={this.props.Stack} />
-            </div>
-        );
-    }
-
-    @observable _tooltipOpen: boolean = false;
-    render() {
-        return <Tooltip open={this._tooltipOpen} onClose={action(() => this._tooltipOpen = false)} title={<><div className="dash-tooltip">Tap for toolbar, drag to create alias in another pane</div></>} placement="bottom">
-            <span className="dockingViewButtonSelector"
-                onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}
-                onPointerDown={action(e => {
-                    this.props.views()[0]?.select(false);
-                    this._tooltipOpen = false;
-                })} >
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}>
-                    <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} />
-                </Flyout>
-            </span>
-        </Tooltip >;
-    }
-}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 75a8207dd..91c6975ea 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -4,15 +4,15 @@ import { Tooltip } from '@material-ui/core';
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
 import { clamp } from 'lodash';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
-import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from "../../../fields/RefField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -27,10 +27,10 @@ import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { MainView } from '../MainView';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
+import { DashFieldView } from '../nodes/formattedText/DashFieldView';
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
@@ -92,14 +92,28 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 Doc.GetProto(doc).title = e.currentTarget.value;
             }));
 
-            const dragBtnDown = (e: React.PointerEvent) => {
-                setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction);
-            };
-
-
             if (tab.element[0].children[1].children.length === 1) {
-                iconWrap.className = "lm_iconWrap";
-                iconWrap.id = "lm_iconWrap";
+                iconWrap.className = "lm_iconWrap lm_moreInfo";
+                const dragBtnDown = (e: React.PointerEvent) => {
+                    setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, action(e => {
+                        if (this.view) {
+                            SelectionManager.SelectView(this.view, false);
+                            let child = this.view.ContentDiv!.children[0];
+                            while (child.children.length) {
+                                const next = Array.from(child.children).find(c => c.className?.toString().includes("SVGAnimatedString") || typeof (c.className) === "string");
+                                if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
+                                if (next?.className?.toString().includes(DashFieldView.name)) break;
+                                if (next) child = next;
+                                else break;
+                            }
+                            simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+                        }
+                        else { this._activated = true;
+                              setTimeout(() =>this.view && SelectionManager.SelectView(this.view, false));
+                        }
+                    }));
+                };
+
                 closeWrap.className = "lm_iconWrap";
                 closeWrap.id = "lm_closeWrap";
                 closeWrap.onclick = (e: MouseEvent) => {
@@ -111,6 +125,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 const closeIcon = <FontAwesomeIcon icon={"times"} />;
                 ReactDOM.render(docIcon, iconWrap);
                 ReactDOM.render(closeIcon, closeWrap);
+                tab.reactComponents = [iconWrap, closeWrap];
                 // tab.element[0].append(closeWrap);
                 tab.element[0].prepend(iconWrap);
                 tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
@@ -122,7 +137,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         titleEle.style.backgroundColor = "transparent";
                         iconWrap.style.color = textColor;
                         closeWrap.style.color = textColor;
-                        moreInfoDrag.style.backgroundColor = textColor;
                         tab.element[0].style.background = !layer ? color : "dimgrey";
                     }, { fireImmediately: true });
             }
@@ -155,19 +169,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     // toggle.style.textTransform = selected ? "uppercase" : "";
                 }));
 
-            //attach the selection doc buttons menu to the drag handle
-            const stack: HTMLDivElement = tab.contentItem.parent;
-            const header: HTMLDivElement = tab;
-            stack.onscroll = action((e: any) => {
-                console.log('scrolling...');
-            });
-            const moreInfoDrag = document.createElement("div");
-            moreInfoDrag.className = "lm_iconWrap";
-            tab._disposers.buttonDisposer = reaction(() => this.view, view =>
-                view && [ReactDOM.render(<span><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, moreInfoDrag), tab._disposers.buttonDisposer?.()],
-                { fireImmediately: true });
-            // tab.reactComponents = [moreInfoDrag];
-            // tab.element[0].children[3].before(moreInfoDrag);
 
             // highlight the tab when the tab document is brushed in any part of the UI
             tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
-- 
cgit v1.2.3-70-g09d2


From 94975954fbd1ce9b68f6545b16a4eabe4b41cca0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Jun 2022 14:30:18 -0400
Subject: fixed bug with adding to pres when in miniview mode.

---
 .../views/collections/CollectionDockingView.tsx    | 69 ++++++++++------------
 src/client/views/collections/TabDocView.tsx        |  3 +
 src/client/views/nodes/trails/PresBox.tsx          |  4 +-
 3 files changed, 37 insertions(+), 39 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 476ffaf81..a790a0475 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -172,12 +172,6 @@ export class CollectionDockingView extends CollectionSubView() {
     @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
-        if (document.type === DocumentType.PRES) {
-            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
-            if (docs.includes(document)) {
-                docs.splice(docs.indexOf(document), 1);
-            }
-        }
         if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
 
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
@@ -186,6 +180,7 @@ export class CollectionDockingView extends CollectionSubView() {
             return true;
         }
         const instance = CollectionDockingView.Instance;
+        const glayRoot = instance._goldenLayout.root;
         if (!instance) return false;
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
 
@@ -194,29 +189,29 @@ export class CollectionDockingView extends CollectionSubView() {
             stack.setActiveContentItem(stack.contentItems[stack.contentItems.length - 1]);
         } else {
             const newItemStackConfig = { type: 'stack', content: [docContentConfig] };
-            const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
-            if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
-                instance._goldenLayout.root.addChild(newContentItem);
-            } else if (instance._goldenLayout.root.contentItems[0].isStack) {
-                instance._goldenLayout.root.contentItems[0].addChild(docContentConfig);
+            const newContentItem = glayRoot.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+            if (glayRoot.contentItems.length === 0) { // if no rows / columns
+                glayRoot.addChild(newContentItem);
+            } else if (glayRoot.contentItems[0].isStack) {
+                glayRoot.contentItems[0].addChild(docContentConfig);
             } else if (
-                instance._goldenLayout.root.contentItems.length === 1 &&
-                instance._goldenLayout.root.contentItems[0].contentItems.length === 1 &&
-                instance._goldenLayout.root.contentItems[0].contentItems[0].contentItems.length === 0) {
-                instance._goldenLayout.root.contentItems[0].contentItems[0].addChild(docContentConfig);
+                glayRoot.contentItems.length === 1 &&
+                glayRoot.contentItems[0].contentItems.length === 1 &&
+                glayRoot.contentItems[0].contentItems[0].contentItems.length === 0) {
+                glayRoot.contentItems[0].contentItems[0].addChild(docContentConfig);
             }
             else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
                 switch (pullSide) {
                     default:
-                    case "right": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
-                    case "left": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
+                    case "right": glayRoot.contentItems[0].addChild(newContentItem); break;
+                    case "left": glayRoot.contentItems[0].addChild(newContentItem, 0); break;
                     case "top":
                     case "bottom":
                         // if not going in a row layout, must add already existing content into column
-                        const rowlayout = instance._goldenLayout.root.contentItems[0];
+                        const rowlayout = glayRoot.contentItems[0];
                         const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
 
-                        CollectionDockingView.Instance._goldenLayout.saveScrollTops(rowlayout.element);
+                        instance._goldenLayout.saveScrollTops(rowlayout.element);
                         rowlayout.parent.replaceChild(rowlayout, newColumn);
                         if (pullSide === "top") {
                             newColumn.addChild(rowlayout, undefined, true);
@@ -225,23 +220,23 @@ export class CollectionDockingView extends CollectionSubView() {
                             newColumn.addChild(newContentItem, undefined, true);
                             newColumn.addChild(rowlayout, 0, true);
                         }
-                        CollectionDockingView.Instance._goldenLayout.restoreScrollTops(rowlayout.element);
+                        instance._goldenLayout.restoreScrollTops(rowlayout.element);
 
                         rowlayout.config.height = 50;
                         newContentItem.config.height = 50;
                 }
             } else {// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
                 switch (pullSide) {
-                    case "top": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
-                    case "bottom": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
+                    case "top": glayRoot.contentItems[0].addChild(newContentItem, 0); break;
+                    case "bottom": glayRoot.contentItems[0].addChild(newContentItem); break;
                     case "left":
                     case "right":
                     default:
                         // if not going in a row layout, must add already existing content into column
-                        const collayout = instance._goldenLayout.root.contentItems[0];
+                        const collayout = glayRoot.contentItems[0];
                         const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
 
-                        CollectionDockingView.Instance._goldenLayout.saveScrollTops(collayout.element);
+                        instance._goldenLayout.saveScrollTops(collayout.element);
                         collayout.parent.replaceChild(collayout, newRow);
                         if (pullSide === "left") {
                             newRow.addChild(collayout, undefined, true);
@@ -250,7 +245,7 @@ export class CollectionDockingView extends CollectionSubView() {
                             newRow.addChild(newContentItem, undefined, true);
                             newRow.addChild(collayout, 0, true);
                         }
-                        CollectionDockingView.Instance._goldenLayout.restoreScrollTops(collayout.element);
+                        instance._goldenLayout.restoreScrollTops(collayout.element);
 
                         collayout.config.width = 50;
                         newContentItem.config.width = 50;
@@ -289,19 +284,19 @@ export class CollectionDockingView extends CollectionSubView() {
                         this._goldenLayout.unbind('stackCreated', this.stackCreated);
                     } catch (e) { }
                 }
+                this.tabMap.clear();
+                this._goldenLayout.destroy();
             }
-            this.tabMap.clear();
-            this._goldenLayout?.destroy();
-            this._goldenLayout = new GoldenLayout(JSON.parse(config));
-            this._goldenLayout.on('tabCreated', this.tabCreated);
-            this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
-            this._goldenLayout.on('stackCreated', this.stackCreated);
-            this._goldenLayout.registerComponent('DocumentFrameRenderer', TabDocView);
-            this._goldenLayout.container = this._containerRef.current;
-            this._goldenLayout.init();
-            this._goldenLayout.root.layoutManager.on('itemDropped', this.tabItemDropped);
-            this._goldenLayout.root.layoutManager.on('dragStart', this.tabDragStart);
-            this._goldenLayout.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
+            const glay = this._goldenLayout = new GoldenLayout(JSON.parse(config));
+            glay.on('tabCreated', this.tabCreated);
+            glay.on('tabDestroyed', this.tabDestroyed);
+            glay.on('stackCreated', this.stackCreated);
+            glay.registerComponent('DocumentFrameRenderer', TabDocView);
+            glay.container = this._containerRef.current;
+            glay.init();
+            glay.root.layoutManager.on('itemDropped', this.tabItemDropped);
+            glay.root.layoutManager.on('dragStart', this.tabDragStart);
+            glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
         }
     }
 
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 91c6975ea..34fe572e3 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -35,6 +35,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
+import { listSpec } from '../../../fields/Schema';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -246,6 +247,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             }
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
             if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
+                const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+                if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
                 CollectionDockingView.AddSplit(curPres, "right");
                 setTimeout(() => DocumentManager.Instance.jumpToDocument(doc, false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
             }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 1a4be8e0c..591480023 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -403,11 +403,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // openInTab(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
+            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);
             //awaiting jump so that new scale can be found, since jumping is async     
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
+            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    
         }
         // 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.
-- 
cgit v1.2.3-70-g09d2


From ec2e780141fb41d3f70aa86b0312211e0b30257c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 10:39:14 -0400
Subject: fixed issues with pinWithView so that thumnails show the pinned area
 and navigating to the view shows the entire pinned area regardless of panel
 size.

---
 src/client/views/DocumentButtonBar.tsx             | 29 +------------------
 src/client/views/collections/TabDocView.tsx        | 33 ++++++++++++++++++++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 10 +++++--
 src/client/views/nodes/DocumentView.tsx            |  1 +
 src/client/views/nodes/trails/PresBox.tsx          | 20 +++++++++++--
 src/client/views/nodes/trails/PresElementBox.tsx   |  8 +++---
 6 files changed, 64 insertions(+), 37 deletions(-)

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

diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 103734b9b..41224b1c5 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -212,34 +212,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
 
     @undoBatch
     @action
-    pinWithView = (targetDoc: Doc) => {
-        const activeDoc = targetDoc && TabDocView.PinDoc(targetDoc);
-        if (activeDoc) {
-            const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
-            const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
-            if (scrollable) {
-                const scroll = targetDoc._scrollTop;
-                activeDoc.presPinView = true;
-                activeDoc.presPinViewScroll = scroll;
-            } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                activeDoc.presPinView = true;
-                activeDoc.presStartTime = targetDoc._currentTimecode;
-                activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
-            } else if (pannable) {
-                const x = targetDoc._panX;
-                const y = targetDoc._panY;
-                const scale = targetDoc._viewScale;
-                activeDoc.presPinView = true;
-                activeDoc.presPinViewX = x;
-                activeDoc.presPinViewY = y;
-                activeDoc.presPinViewScale = scale;
-            } else if (targetDoc.type === DocumentType.COMPARISON) {
-                const width = targetDoc._clipWidth;
-                activeDoc.presPinClipWidth = width;
-                activeDoc.presPinView = true;
-            }
-        }
-    }
+    pinWithView = (targetDoc: Doc) =>  targetDoc && TabDocView.PinDoc(targetDoc, {pinDocView: true});
 
     @computed
     get pinWithViewButton() {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 34fe572e3..f6a75e2db 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -227,6 +227,39 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             	pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
             	pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
             	pinDoc.presPinViewScale = viewProps.scale;
+                pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left+viewProps.bounds.width, viewProps.bounds.top+viewProps.bounds.height]);
+            }
+            if (pinProps?.pinDocView) {
+                const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking;
+                const pannable: boolean = ((pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG);
+                if (scrollable) {
+                    const scroll = doc._scrollTop;
+                    pinDoc.presPinView = true;
+                    pinDoc.presPinViewScroll = scroll;
+                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) {
+                    pinDoc.presPinView = true;
+                    pinDoc.presStartTime = doc._currentTimecode;
+                    pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1;
+                } else if (pannable) {
+                    pinDoc.presPinView = true;
+                    pinDoc.presPinViewX = pinDoc._panX;
+                    pinDoc.presPinViewY = pinDoc._panY;
+                    pinDoc.presPinViewScale = pinDoc._viewScale;
+                    const pw = NumCast(pinProps.panelWidth);
+                    const ph = NumCast(pinProps.panelHeight);
+                    const ps = NumCast(pinDoc._viewScale);
+                    if (pw && ph && ps) {
+                        pinDoc.contentBounds = new List<number>([
+                            NumCast(pinDoc.panX)-pw/2/ps,
+                            NumCast(pinDoc.panY)-ph/2/ps,
+                            NumCast(pinDoc.panX)+pw/2/ps,
+                            NumCast(pinDoc.panY)+ph/2/ps]);
+                    }
+                } else if (doc.type === DocumentType.COMPARISON) {
+                    const width = doc._clipWidth;
+                    pinDoc.presPinClipWidth = width;
+                    pinDoc.presPinView = true;
+                }
 			}
             Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
             if (!pinProps?.audioRange && duration !== undefined) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5f927a2a9..b70e6ff98 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -56,6 +56,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { FieldView, FieldViewProps } from "../../nodes/FieldView";
+import { TabDocView } from "../TabDocView";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -138,7 +139,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         };
     }
     @computed get fitToContent() { return (this.props.fitContentsToDoc?.() || this.Document._fitToBox) && !this.isAnnotationOverlay; }
-    @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); }
+    @computed get contentBounds() { 
+        const cb = Cast(this.rootDoc.contentBounds, listSpec("number"));
+        return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} : 
+            this.props.contentBounds?.() ?? aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); 
+    }
     @computed get nativeWidth() { return this.fitToContent ? 0 : Doc.NativeWidth(this.Document); }
     @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); }
     @computed get cachedCenteringShiftX(): number {
@@ -1612,7 +1617,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
         appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
-        appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
+        appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
+        //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
             appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 59a402e01..26dec3bd5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -112,6 +112,7 @@ export interface DocumentViewSharedProps {
     renderDepth: number;
     Document: Doc;
     DataDoc?: Doc;
+    contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
     fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 591480023..6de04bd31 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -35,6 +35,9 @@ export interface PinProps {
     setPosition?: boolean;
     hidePresBox?: boolean;
     pinWithView?: PinViewProps;
+    pinDocView?: boolean;
+    panelWidth?: number;
+    panelHeight?: number
 }
 
 export interface PinViewProps {
@@ -339,10 +342,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
                 bestTarget._currentTimecode = activeItem.presStartTime;
             } else {
+                const contentBounds= Cast(activeItem.contentBounds, listSpec("number"));
                 bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
-                bestTarget._panX = activeItem.presPinViewX;
-                bestTarget._panY = activeItem.presPinViewY;
-                bestTarget._viewScale = activeItem.presPinViewScale;
+                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;
+                }
             }
             this._navTimer = setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
         });
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index b9951eaae..5eff47a86 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from "@material-ui/core";
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Opt } from "../../../../fields/Doc";
+import { Doc, DocListCast, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
@@ -13,6 +13,7 @@ import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
 import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
+import { CollectionViewType } from "../../collections/CollectionView";
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { EditableView } from "../../EditableView";
 import { Colors } from "../../global/globalEnums";
@@ -23,7 +24,6 @@ import { PresBox } from "./PresBox";
 import "./PresElementBox.scss";
 import { PresMovement } from "./PresEnums";
 import React = require("react");
-import { CollectionViewType } from "../../collections/CollectionView";
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -75,8 +75,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? (null) :
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: this.embedWidth() }}>
                 <DocumentView
-                    Document={this.targetDoc}
-                    DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
+                    Document={this.rootDoc}
+                    DataDoc={undefined}//this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     styleProvider={this.styleProvider}
                     docViewPath={returnEmptyDoclist}
                     rootSelected={returnTrue}
-- 
cgit v1.2.3-70-g09d2


From 3213c24210d67af0a4b027863773ad383404a59c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 11:07:34 -0400
Subject: renamed fitToBox to fitContentsToBox.  fixed pinning multiple
 documents at same time to not bring up presBox multiple times.

---
 src/client/documents/Documents.ts                  |  3 +-
 src/client/views/DocumentButtonBar.tsx             | 21 ++----------
 src/client/views/LightboxView.tsx                  |  2 --
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/PropertiesButtons.tsx             |  2 +-
 src/client/views/PropertiesView.tsx                |  2 +-
 src/client/views/collections/CollectionMenu.tsx    |  4 +--
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 src/client/views/collections/TabDocView.tsx        | 25 +++++++--------
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 37 +++++++---------------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 .../CollectionMulticolumnView.tsx                  |  2 +-
 .../CollectionMultirowView.tsx                     |  2 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  4 +--
 src/client/views/nodes/MapBox/MapBox.tsx           |  6 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  6 ++--
 src/client/views/nodes/trails/PresBox.tsx          | 14 ++++----
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 src/fields/documentSchemas.ts                      |  6 ++--
 src/mobile/AudioUpload.tsx                         |  4 +--
 22 files changed, 60 insertions(+), 92 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index acc1e341a..144a3d9cf 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -113,7 +113,8 @@ export class DocumentOptions {
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", true);
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units", true);
     _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)", true);
-    _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
+    _fitContentsToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
+    _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
     _isPushpin?: boolean; // whether document, when clicked, toggles display of its link target
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 41224b1c5..0bbe473d7 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -204,27 +204,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="dash-tooltip">{SelectionManager.Views().length > 1 ? "Pin multiple documents to presentation" : "Pin to presentation"}</div>}>
             <div className="documentButtonBar-icon"
                 style={{ color: "white" }}
-                onClick={undoBatch(e => this.props.views().map(view => view && this.pinWithView(view.props.Document)))}>
+                onClick={(e => TabDocView.PinDoc(this.props.views().filter(v => v).map(dv => dv!.rootDoc), {pinDocView: true}))}
+             >
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>
         </Tooltip>;
     }
 
-    @undoBatch
-    @action
-    pinWithView = (targetDoc: Doc) =>  targetDoc && TabDocView.PinDoc(targetDoc, {pinDocView: true});
-
-    @computed
-    get pinWithViewButton() {
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 17, transform: 'translate(0, 1px)' }} />;
-        const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin with current view"}</div></>}>
-            <div className="documentButtonBar-icon" onClick={() => this.pinWithView(targetDoc)}>
-                {presPinWithViewIcon}
-            </div>
-        </Tooltip>;
-    }
-
     @computed
     get shareButton() {
         const targetDoc = this.view0?.props.Document;
@@ -349,9 +335,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.pinButton}
             </div>
-            {/* <div className="documentButtonBar-button">
-                {this.pinWithViewButton}
-            </div> */}
             {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.shareButton}
             </div>}
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index f67f37bfb..fcc4aea13 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -207,7 +207,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
 
     future = () => LightboxView._future;
     tourMap = () => LightboxView._tourMap;
-    fitToBox = () => LightboxView._docTarget === LightboxView.LightboxDoc;
     render() {
         let downx = 0, downy = 0;
         return !LightboxView.LightboxDoc ? (null) :
@@ -241,7 +240,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         DataDoc={undefined}
                         LayoutTemplate={LightboxView.LightboxDocTemplate}
                         addDocument={undefined}
-                        // fitContentsToDoc={this.fitToBox} // bcz: why do we want this?  when we initially open a colletion, we shrinkwrap it which allows for user navigation.  if we later encounter a collection, it's not clear to me that we want to make it either shrinkwrap or fitContents...
                         isDocumentActive={returnFalse}
                         isContentActive={returnTrue}
                         addDocTab={this.addDocTab}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 641178cd9..84a131a57 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -287,7 +287,7 @@ export class MainView extends React.Component {
                 styleProvider={DefaultStyleProvider}
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
-                fitContentsToDoc={returnTrue}
+                fitContentsToBox={returnTrue}
                 isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
                 isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                 ScreenToLocalTransform={this.headerBarScreenXf}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index f24ff09db..74055f057 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -71,7 +71,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return this.propertyToggleBtn("Lock\xA0View", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => "lock");
     }
     @computed get fitContentButton() {
-        return this.propertyToggleBtn("View All", "_fitToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
+        return this.propertyToggleBtn("View All", "_fitContentsToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
     }
     @computed get fitWidthButton() {
         return this.propertyToggleBtn("Fit\xA0Width", "_fitWidth", on => `${on ? "Don't" : "Do"} fit content to width of container`, on => "arrows-alt-h");
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index bcfd2dd56..16eb95cf4 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -300,7 +300,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     Document={layoutDoc}
                     DataDoc={this.dataDoc}
                     renderDepth={1}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 01e77f342..39f6466d6 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -252,8 +252,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     };
     _clusterCommand = {
         params: ["target"], title: "fit content",
-        script: "self.target._fitToBox = !self.target._fitToBox;",
-        immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox),
+        script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;",
+        immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox),
         initialize: emptyFunction
     };
     _fitContentCommand = {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 4f6f45d2f..7573b938a 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -135,7 +135,7 @@ export class CollectionTimeView extends CollectionSubView() {
             onClick={this.contentsDown}>
             <CollectionFreeFormView {...this.props}
                 engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }}
-                fitContentsToDoc={returnTrue}
+                fitContentsToBox={returnTrue}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f6a75e2db..5cc92a305 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -192,15 +192,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
      * Adds a document to the presentation view
      **/
     @action
-    public static PinDoc(doc: Doc, pinProps?: PinProps) {
-        //add this new doc to props.Document
+    public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) {
+        const docList = ((docs instanceof Doc) ? [docs]: docs);
+        const batch = UndoManager.StartBatch("pinning doc");
 
         // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
         const curPres = CurrentUserUtils.ActivePresentation;
-        if (curPres) {
+        curPres && docList.forEach(doc => {
             // Edge Case 1: Cannot pin document to itself
             if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const batch = UndoManager.StartBatch("pinning doc");
             const pinDoc = Doc.MakeAlias(doc);
             pinDoc.presentationTargetDoc = doc;
             pinDoc.title = doc.title + " - Slide";
@@ -279,17 +279,16 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presMovement = PresMovement.None;
             }
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
-                const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
-                if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
-                CollectionDockingView.AddSplit(curPres, "right");
-                setTimeout(() => DocumentManager.Instance.jumpToDocument(doc, false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
-            }
             PresBox.Instance?._selectedArray.clear();
             pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-            setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
-            return pinDoc;
+        });
+        if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
+            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+            if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
+            CollectionDockingView.AddSplit(curPres, "right");
+            setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
         }
+        setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
     }
 
     componentDidMount() {
@@ -572,7 +571,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     docFilters={CollectionDockingView.Instance.childDocFilters}
                     docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                     searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                 />
                 <div className="miniOverlay" onPointerDown={this.miniDown} >
                     <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 09f05f69a..8824750a3 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -796,7 +796,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             isDocumentActive={isActive}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
             hideTitle={asText}
-            fitContentsToDoc={returnTrue}
+            fitContentsToBox={returnTrue}
             hideDecorationTitle={this.props.treeView.outlineMode}
             hideResizeHandles={this.props.treeView.outlineMode}
             onClick={this.onChildClick}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b70e6ff98..ec3cf1fe8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,7 +9,7 @@ import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fiel
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
-import { createSchema, listSpec } from "../../../../fields/Schema";
+import { listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
@@ -41,6 +41,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn
 import { LightboxView } from "../../LightboxView";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
+import { FieldViewProps } from "../../nodes/FieldView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { VideoBox } from "../../nodes/VideoBox";
@@ -50,27 +51,13 @@ import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
 import { TreeViewType } from "../CollectionTreeView";
 import { CollectionViewType } from "../CollectionView";
+import { TabDocView } from "../TabDocView";
 import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { FieldView, FieldViewProps } from "../../nodes/FieldView";
-import { TabDocView } from "../TabDocView";
 
-export const panZoomSchema = createSchema({
-    _panX: "number",
-    _panY: "number",
-    _currentTimecode: "number",
-    _timecodeToShow: "number",
-    _currentFrame: "number",
-    _useClusters: "boolean",
-    _viewTransition: "string",
-    _xPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _yPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _fitToBox: "boolean",
-    scrollHeight: "number"    // this will be set when the collection is an annotation overlay for a PDF/Webpage
-});
 
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -138,20 +125,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x))
         };
     }
-    @computed get fitToContent() { return (this.props.fitContentsToDoc?.() || this.Document._fitToBox) && !this.isAnnotationOverlay; }
+    @computed get fitContentsToBox() { return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay; }
     @computed get contentBounds() { 
         const cb = Cast(this.rootDoc.contentBounds, listSpec("number"));
         return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} : 
             this.props.contentBounds?.() ?? aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); 
     }
-    @computed get nativeWidth() { return this.fitToContent ? 0 : Doc.NativeWidth(this.Document); }
-    @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); }
+    @computed get nativeWidth() { return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document); }
+    @computed get nativeHeight() { return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document); }
     @computed get cachedCenteringShiftX(): number {
-        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
         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.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
         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 {
@@ -176,8 +163,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.layoutDoc._panY = vals.bounds.cy;
         this.layoutDoc._viewScale = vals.scale;
     }
-    freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined;
-    reverseNativeScaling = () => this.fitToContent ? true : false;
+    freeformData = (force?: boolean) => !this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined;
+    reverseNativeScaling = () => this.fitContentsToBox ? true : false;
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
     panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
     zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1));
@@ -1272,7 +1259,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.pointerEvents}
             jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
-        //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
+        //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
         />;
     }
     addDocTab = action((doc: Doc, where: string) => {
@@ -1616,7 +1603,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
-        appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+        appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
         //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3fac3350a..6df2bb102 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -516,7 +516,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
 
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index e2dfb25e2..7147ae485 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -257,7 +257,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToDoc={this.props.fitContentsToDoc}
+            fitContentsToBox={this.props.fitContentsToBox}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 3010e36aa..3bda56c33 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -264,7 +264,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToDoc={this.props.fitContentsToDoc}
+            fitContentsToBox={this.props.fitContentsToBox}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index b731479a5..f45068b6a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -402,7 +402,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 <DocumentView
                     Document={this.previewDocument}
                     DataDoc={undefined}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     freezeDimensions={true}
                     dontCenter={"y"}
                     focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 26dec3bd5..2a518bb57 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -85,7 +85,7 @@ export interface DocComponentView {
     getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
     setViewSpec?: (anchor: Doc, preview: boolean) => void;  // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
+    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void;  // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
@@ -113,7 +113,7 @@ export interface DocumentViewSharedProps {
     Document: Doc;
     DataDoc?: Doc;
     contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
-    fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
+    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     suppressSetHeight?: boolean;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 9f1c019fe..5f4c17ee6 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -259,7 +259,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitToBox && this.fitBounds(this._map);
+                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -274,7 +274,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -284,7 +284,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 7d0302b26..4e431e7bd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -495,7 +495,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 // embed document when dragg marked as embed
             } else if (de.embedKey) {
                 const target = dragData.droppedDocuments[0];
-                target._fitToBox = true;
+                target._fitContentsToBox = true;
                 const node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](),
                     height: target[HeightSym](),
@@ -1557,7 +1557,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         }
     }
-    fitToBox = () => this.props.Document._fitToBox;
+    fitContentsToBox = () => this.props.Document._fitContentsToBox;
     sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
@@ -1632,7 +1632,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     ScreenToLocalTransform={this.sidebarScreenToLocal}
                     renderDepth={this.props.renderDepth + 1}
                     setHeight={this.setSidebarHeight}
-                    fitContentsToDoc={this.fitToBox}
+                    fitContentsToBox={this.fitContentsToBox}
                     noSidebar={true}
                     fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 6de04bd31..cebf9487a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -35,8 +35,8 @@ export interface PinProps {
     setPosition?: boolean;
     hidePresBox?: boolean;
     pinWithView?: PinViewProps;
-    pinDocView?: boolean;
-    panelWidth?: number;
+    pinDocView?: boolean;  // whether the current view specs of the document should be saved the pinned document
+    panelWidth?: number;  // panel width and height of the document (used to compute the bounds of the pinned view area)
     panelHeight?: number
 }
 
@@ -997,7 +997,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Method called for viewing paths which adds a single line with
      * points at the center of each document added.
-     * Design choice: When this is called it sets _fitToBox as true so the
+     * Design choice: When this is called it sets _fitContentsToBox as true so the
      * user can have an overview of all of the documents in the collection.
      * (Design needed for when documents in presentation trail are in another
      * collection)
@@ -1800,16 +1800,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
                 break;
             case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             default:
                 break;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5eff47a86..ba0193e4b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -85,7 +85,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     isContentActive={this.props.isContentActive}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
                     ScreenToLocalTransform={Transform.Identity}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index e532becb5..be39e0709 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -36,8 +36,8 @@ export const documentSchema = createSchema({
     _nativeHeight: "number",    // "
     _width: "number",           // width of document in its container's coordinate system
     _height: "number",          // "
-    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set
+    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitContentsToBox is set
+    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitContentsToBox is set
     _xMargin: "number",         // margin added on left/right of most documents to add separation from their container
     _yMargin: "number",         // margin added on top/bottom of most documents to add separation from their container
     _overflow: "string",        // sets overflow behvavior for CollectionFreeForm views
@@ -59,7 +59,7 @@ export const documentSchema = createSchema({
     borderRounding: "string",   // border radius rounding of document
     boxShadow: "string",        // the amount of shadow around the perimeter of a document
     color: "string",            // foreground color of document
-    fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
+    fitContentsToBox: "boolean",// whether freeform view contents should be zoomed/panned to fill the area of the document view box
     fontSize: "string",
     hidden: "boolean",          // whether a document should not be displayed
     isInkMask: "boolean",       // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 418464f0e..64baa351c 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -19,7 +19,7 @@ import React = require('react');
 
 @observer
 export class AudioUpload extends React.Component {
-    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
 
     /**
      * Handles the onclick functionality for the 'Restart' button
@@ -36,7 +36,7 @@ export class AudioUpload extends React.Component {
                     title: "mobile audio",
                     _width: 500,
                     _height: 100
-                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
     }
 
     /** 
-- 
cgit v1.2.3-70-g09d2


From 53bc43a9817f6a80b9dc4e0adad11b555c4236b6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 11:20:14 -0400
Subject: pinned docs now have click func to navigate to target.  restored
 embedded views within presBox

---
 src/client/views/collections/TabDocView.tsx      | 2 ++
 src/client/views/nodes/trails/PresElementBox.tsx | 4 ++--
 2 files changed, 4 insertions(+), 2 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 5cc92a305..5883f0496 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -36,6 +36,7 @@ import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
 import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -260,6 +261,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     pinDoc.presPinClipWidth = width;
                     pinDoc.presPinView = true;
                 }
+                pinDoc.onClick = ScriptField.MakeFunction("DocFocusOrOpen(self.presentationTargetDoc)");
 			}
             Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
             if (!pinProps?.audioRange && duration !== undefined) {
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index ef70f8e36..41a583580 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -582,9 +582,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                                 </div>
                                           </div>
                                     </Tooltip>}
-                                    {/* <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
+                                    <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
                                           <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
-                                    </div></Tooltip> */}
+                                    </div></Tooltip>
                                     <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
                                           className={"slideButton"}
                                           onClick={this.removeItem}>
-- 
cgit v1.2.3-70-g09d2


From 012a4681c824baaef60370de5904e9cc35b03f13 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 11:32:01 -0400
Subject: fixed pin doc navigation when not in presBox (eg. headerBar)

---
 src/client/views/collections/TabDocView.tsx      |   2 +-
 src/client/views/nodes/trails/PresBox.tsx        |  61 +--
 src/client/views/nodes/trails/PresElementBox.tsx | 490 +++++++++--------------
 3 files changed, 219 insertions(+), 334 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 5883f0496..142013d1a 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -261,8 +261,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     pinDoc.presPinClipWidth = width;
                     pinDoc.presPinView = true;
                 }
-                pinDoc.onClick = ScriptField.MakeFunction("DocFocusOrOpen(self.presentationTargetDoc)");
 			}
+            pinDoc.onClick = ScriptField.MakeFunction("navigateToDoc(self.presentationTargetDoc, self)");
             Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = "manual";
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index cebf9487a..db2f58be7 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -29,6 +29,7 @@ import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import "./PresBox.scss";
 import { PresEffect, PresMovement, PresStatus } from "./PresEnums";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 
 export interface PinProps {
     audioRange?: boolean;
@@ -332,34 +333,36 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
         if (bestTarget) console.log(bestTarget.title, bestTarget.type);
         else console.log("no best target");
-        bestTarget && runInAction(() => {
-            console.log(bestTarget.title, bestTarget.type);
-            if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
-                bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
-                bestTarget._scrollTop = activeItem.presPinViewScroll;
-            } else if (bestTarget.type === DocumentType.COMPARISON) {
-                bestTarget._clipWidth = activeItem.presPinClipWidth;
-            } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
-                bestTarget._currentTimecode = activeItem.presStartTime;
+        if (bestTarget)  this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem);
+    }
+
+    @action
+    static navigateToDoc(bestTarget:Doc, activeItem:Doc)  {
+        if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
+            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+            bestTarget._scrollTop = activeItem.presPinViewScroll;
+        } else if (bestTarget.type === DocumentType.COMPARISON) {
+            bestTarget._clipWidth = activeItem.presPinClipWidth;
+        } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
+            bestTarget._currentTimecode = activeItem.presStartTime;
+        } else {
+            const contentBounds= Cast(activeItem.contentBounds, listSpec("number"));
+            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+            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 {
-                const contentBounds= Cast(activeItem.contentBounds, listSpec("number"));
-                bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
-                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;
-                }
+                bestTarget._panX = activeItem.presPinViewX;
+                bestTarget._panY = activeItem.presPinViewY;
+                bestTarget._viewScale = activeItem.presPinViewScale;
             }
-            this._navTimer = setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
-        });
+        }
+        return setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
     }
 
     /**
@@ -2570,4 +2573,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div>;
     }
-}
\ No newline at end of file
+}
+
+ScriptingGlobals.add(function navigateToDoc(bestTarget:Doc, activeItem:Doc) {
+    PresBox.navigateToDoc(bestTarget, activeItem);
+});
\ 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 41a583580..ba0193e4b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -4,10 +4,9 @@ import { action, computed, IReactionDisposer, observable, reaction } from "mobx"
 import { observer } from "mobx-react";
 import { Doc, DocListCast, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
-import { List } from "../../../../fields/List";
-import { Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
-import { Docs, DocUtils } from "../../../documents/Documents";
+import { DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
@@ -31,8 +30,8 @@ import React = require("react");
  */
 @observer
 export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
-      public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
-      _heightDisposer: IReactionDisposer | undefined;
+    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
+    _heightDisposer: IReactionDisposer | undefined;
 
     @observable _dragging = false;
     @computed get indexInPres() { return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, "data")]).indexOf(this.rootDoc); }  // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
@@ -182,11 +181,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         e.preventDefault();
     }
 
-
-      /**
-       * Function to drag and drop the pres element to a diferent location
-       */
-       startDrag = (e: PointerEvent) => {
+    /**
+     * Function to drag and drop the pres element to a diferent location
+     */
+    startDrag = (e: PointerEvent) => {
         const miniView: boolean = this.toolbarWidth <= 100;
         const activeItem = this.rootDoc;
         const dragArray = PresBox.Instance._dragArray;
@@ -197,32 +195,27 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         dragData.moveDocument = this.props.docViewPath().lastElement()?.props.moveDocument;
         const dragItem: HTMLElement[] = [];
         if (dragArray.length === 1) {
-              const doc = this._itemRef.current || dragArray[0];
-              doc.className = miniView ? "presItem-miniSlide" : "presItem-slide";
-              dragItem.push(doc);
+            const doc = this._itemRef.current || dragArray[0];
+            doc.className = miniView ? "presItem-miniSlide" : "presItem-slide";
+            dragItem.push(doc);
         } else if (dragArray.length >= 1) {
-              const doc = document.createElement('div');
-              doc.className = "presItem-multiDrag";
-              doc.innerText = "Move " + PresBox.Instance._selectedArray.size + " slides";
-              doc.style.position = 'absolute';
-              doc.style.top = (e.clientY) + 'px';
-              doc.style.left = (e.clientX - 50) + 'px';
-              dragItem.push(doc);
+            const doc = document.createElement('div');
+            doc.className = "presItem-multiDrag";
+            doc.innerText = "Move " + PresBox.Instance._selectedArray.size + " slides";
+            doc.style.position = 'absolute';
+            doc.style.top = (e.clientY) + 'px';
+            doc.style.left = (e.clientX - 50) + 'px';
+            dragItem.push(doc);
         }
 
         // const dropEvent = () => runInAction(() => this._dragging = false);
         if (activeItem) {
-              DragManager.StartDocumentDrag(dragItem.map(ele => ele), dragData, e.clientX, e.clientY, undefined);
-              // runInAction(() => this._dragging = true);
-              return true;
+            DragManager.StartDocumentDrag(dragItem.map(ele => ele), dragData, e.clientX, e.clientY, undefined);
+            // runInAction(() => this._dragging = true);
+            return true;
         }
         return false;
-  }
-
-      /**
-       * Returns a local transformed coordinate array for given coordinates.
-       */
-      ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord];
+    }
 
     onPointerOver = (e: any) => {
         document.removeEventListener("pointermove", this.onPointerMove);
@@ -321,283 +314,168 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return width;
     }
 
-      onPointerOver = (e: any) => {
-            document.removeEventListener("pointermove", this.onPointerMove);
-            document.addEventListener("pointermove", this.onPointerMove);
-      }
-
-      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;
-            }
-            if (slide && dragIsPresItem) {
-                  const rect = slide.getBoundingClientRect();
-                  const y = e.clientY - rect.top;  //y position within the element.
-                  const height = slide.clientHeight;
-                  const halfLine = height / 2;
-                  if (y <= halfLine) {
-                        slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                        slide.style.borderBottom = "0px";
-                  } else if (y > halfLine) {
-                        slide.style.borderTop = "0px";
-                        slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                  }
-            }
-            document.removeEventListener("pointermove", this.onPointerMove);
-      }
-
-      onPointerLeave = (e: any) => {
-            this._itemRef.current!.style.borderTop = "0px";
-            this._itemRef.current!.style.borderBottom = "0px";
-            document.removeEventListener("pointermove", this.onPointerMove);
-      }
-
-      @action
-      toggleProperties = () => {
-            if (CurrentUserUtils.propertiesWidth < 5) {
-                  action(() => (CurrentUserUtils.propertiesWidth = 250));
-            }
-      }
-
-      @undoBatch
-      removeItem = action((e: React.MouseEvent) => {
-            this.props.removeDocument?.(this.rootDoc);
-            if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
-                  PresBox.Instance._selectedArray.delete(this.rootDoc);
-            }
-            this.hideRecording();
-            e.stopPropagation();
-      });
-
-      // set the value/title of the individual pres element
-      @undoBatch
-      @action
-      onSetValue = (value: string) => {
-            this.rootDoc.title = !value.trim().length ? "-untitled-" : value;
-            return true;
-      }
-
-      /**
-       * Method called for updating the view of the currently selected document
-       * 
-       * @param targetDoc 
-       * @param activeItem 
-       */
-      @undoBatch
-      @action
-      updateView = (targetDoc: Doc, activeItem: Doc) => {
-            if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
-                  const scroll = targetDoc._scrollTop;
-                  activeItem.presPinViewScroll = scroll;
-            } else if (targetDoc.type === DocumentType.VID) {
-                  activeItem.presPinTimecode = targetDoc._currentTimecode;
-            } else if (targetDoc.type === DocumentType.COMPARISON) {
-                  const clipWidth = targetDoc._clipWidth;
-                  activeItem.presPinClipWidth = clipWidth;
-            } else {
-                  const x = targetDoc._panX;
-                  const y = targetDoc._panY;
-                  const scale = targetDoc._viewScale;
-                  activeItem.presPinViewX = x;
-                  activeItem.presPinViewY = y;
-                  activeItem.presPinViewScale = scale;
-            }
-      }
-
-      @computed get recordingIsInOverlay() {
-            let isInOverlay = false
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
-                  if (doc.slides === this.rootDoc) {
-                        isInOverlay = true
-                        return
-                  }
-            })
-            return isInOverlay
-      }
-
-      @undoBatch
-      @action
-      hideRecording = () => {
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
-                  if (doc.slides === this.rootDoc) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
-                  }
-            })
-      }
-
-      @undoBatch
-      @action
-      showRecording = (activeItem: Doc) => {
-            // Remove every recording that already exists in overlay view
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
-                  if (doc.slides !== null) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
-                  }
-            })
-            if (activeItem.recording) {
-                  // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
-
-            }
-      }
-
-      @undoBatch
-      @action
-      startRecording = (activeItem: Doc) => {
-            // Remove every recording that already exists in overlay view
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
-                  if (doc.slides !== null) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
-                  }
-            })
-
-            if (activeItem.recording) {
-                  // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
-
-            } else {
-                  // if we dont have any recording
-                  const recording = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", cloneFieldFilter: new List<string>(["system"]) });
-
-                  // attach the recording to the slide, and attach the slide to the recording
-                  recording.slides = activeItem
-                  activeItem.recording = recording
-
-                  // TODO: Figure out exactly where we want the video to appear
-                  const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-                  recording.x = pt[0];
-                  recording.y = pt[1];
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording);
-            }
-      }
-
-      @computed
-      get toolbarWidth(): number {
-            const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
-            let width: number = NumCast(this.presBox._width);
-            if (presBoxDocView) width = presBoxDocView.props.PanelWidth();
-            if (width === 0) width = 300;
-            return width;
-      }
-
-      @computed get mainItem() {
-            const isSelected: boolean = PresBox.Instance?._selectedArray.has(this.rootDoc);
-            const toolbarWidth: number = this.toolbarWidth;
-            const showMore: boolean = this.toolbarWidth >= 300;
-            const miniView: boolean = this.toolbarWidth <= 110;
-            const presBox: Doc = this.presBox; //presBox
-            const presBoxColor: string = StrCast(presBox._backgroundColor);
-            const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
-            const targetDoc: Doc = this.targetDoc;
-            const activeItem: Doc = this.rootDoc;
-            return (
-                  <div className={`presItem-container`}
-                        key={this.props.Document[Id] + this.indexInPres}
-                        ref={this._itemRef}
+    @computed get mainItem() {
+        const isSelected: boolean = PresBox.Instance._selectedArray.has(this.rootDoc);
+        const toolbarWidth: number = this.toolbarWidth;
+        const showMore: boolean = this.toolbarWidth >= 300;
+        const miniView: boolean = this.toolbarWidth <= 110;
+        const presBox: Doc = this.presBox; //presBox
+        const presBoxColor: string = StrCast(presBox._backgroundColor);
+        const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
+        const targetDoc: Doc = this.targetDoc;
+        const activeItem: Doc = this.rootDoc;
+        const isGroup: boolean = BoolCast(targetDoc._isGroup);
+        return (
+            <div className={`presItem-container`}
+                key={this.props.Document[Id] + this.indexInPres}
+                ref={this._itemRef}
+                style={{
+                    opacity: this._dragging ? 0.3 : 1
+                }}
+                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);
+                }}
+                onDoubleClick={action(e => {
+                    if (isGroup) {
+                        this.rootDoc.presExpandGroup = !this.rootDoc.presExpandGroup;
+                    } else {
+                        this.toggleProperties();
+                        PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
+                    }
+                })}
+                onPointerOver={this.onPointerOver}
+                onPointerLeave={this.onPointerLeave}
+                onPointerDown={this.headerDown}
+                onPointerUp={this.headerUp}
+            >
+                {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>}
+                {isGroup ?
+                    <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "activeGroup" : "group"}`}
                         style={{
-                              backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent",
-                              opacity: this._dragging ? 0.3 : 1
-                        }}
-                        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.showRecording(activeItem);
-                        }}
-                        onDoubleClick={action(e => {
-                              this.toggleProperties();
-                              PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
-                        })}
-                        onPointerOver={this.onPointerOver}
-                        onPointerLeave={this.onPointerLeave}
-                        onPointerDown={this.headerDown}
-                        onPointerUp={this.headerUp}
-                  >
-                        {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>}
-                        {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
-                              style={{
-                                    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' }}>
-                                    <EditableView
-                                          ref={this._titleRef}
-                                          editing={!isSelected ? false : undefined}
-                                          contents={activeItem.title}
-                                          overflow={'ellipsis'}
-                                          GetValue={() => StrCast(activeItem.title)}
-                                          SetValue={this.onSetValue}
-                                    />
-                              </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="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={this.hideRecording}
-                                                      style={{ fontWeight: 700 }}>
-                                                      <FontAwesomeIcon icon={"video-slash"} onPointerDown={e => e.stopPropagation()} />
-                                                </div>
-                                          </Tooltip> :
-                                          <Tooltip title={<><div className="dash-tooltip">{"Start recording"}</div></>}>
-                                                <div className="slideButton"
-                                                      onClick={() => 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>
-                                          </div>
-                                    </Tooltip>}
-                                    <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
-                                          <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
-                                    </div></Tooltip>
-                                    <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>}
-                  </div >);
-      }
+                            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' }}>
+                            <EditableView
+                                ref={this._titleRef}
+                                editing={!isSelected ? false : undefined}
+                                contents={activeItem.title}
+                                overflow={'ellipsis'}
+                                GetValue={() => StrCast(activeItem.title)}
+                                SetValue={this.onSetValue}
+                            />
+                        </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.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={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
+                                <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
+                            </div></Tooltip>
+                            <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 className="group"></div>
+                        </div>
+                        <div className="presItem-groupSlideContainer" style={{ top: 28, height: 'calc(100% - 28px)' }}>
+                            {this.rootDoc.presExpandGroup ? this.renderGroupSlides : (null)}
+                        </div>
+                        <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div>
+                        <div className="expandButton"
+                            onClick={e => {
+                                if (isGroup) {
+                                    this.rootDoc.presExpandGroup = !this.rootDoc.presExpandGroup;
+                                }
+                            }}
+                        >
+                            <FontAwesomeIcon icon={"caret-down"} style={{ transform: this.rootDoc.presExpandGroup ? "rotate(180deg)" : "rotate(0deg)" }} />
+                        </div>
+                    </div>
+                    : (null)}
+                {miniView || isGroup ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
+                    style={{
+                        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 - (this.presBox._viewType === CollectionViewType.Stacking ? 195 : 220)) : toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 105 : 145), cursor: isSelected ? 'text' : 'grab' }}>
+                        <EditableView
+                            ref={this._titleRef}
+                            editing={!isSelected ? false : undefined}
+                            contents={activeItem.title}
+                            overflow={'ellipsis'}
+                            GetValue={() => StrCast(activeItem.title)}
+                            SetValue={this.onSetValue}
+                        />
+                    </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="slideButton"
+                                onClick={() => this.updateView(targetDoc, activeItem)}
+                                style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</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={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
+                            <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
+                        </div></Tooltip>
+                        <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>}
+            </div >);
+    }
 
-      render() {
-            return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem;
-      }
+    render() {
+        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem;
+    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 1ab1ff51b5b5769079db1922d5013ec4680a1aa1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 10 Jun 2022 09:10:54 -0400
Subject: fixed closing tabs to move to headerBar

---
 src/client/views/collections/CollectionDockingView.tsx |  2 ++
 src/client/views/collections/TabDocView.tsx            | 12 ++----------
 2 files changed, 4 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index a790a0475..e657bd502 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -412,6 +412,8 @@ export class CollectionDockingView extends CollectionSubView() {
     }
 
     tabDestroyed = (tab: any) => {
+        Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
+        Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
         const dview = CollectionDockingView.Instance.props.Document;
         const fieldKey = CollectionDockingView.Instance.props.fieldKey;
         Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 142013d1a..6c4437aed 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -115,20 +115,12 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         }
                     }));
                 };
-
-                closeWrap.className = "lm_iconWrap";
-                closeWrap.id = "lm_closeWrap";
-                closeWrap.onclick = (e: MouseEvent) => {
-                    tab.header.parent.contentItem.remove();
-                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
-                    Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
-                };
+                
                 const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
-                const closeIcon = <FontAwesomeIcon icon={"times"} />;
+                const closeIcon = <FontAwesomeIcon icon={"eye"} />;
                 ReactDOM.render(docIcon, iconWrap);
                 ReactDOM.render(closeIcon, closeWrap);
                 tab.reactComponents = [iconWrap, closeWrap];
-                // tab.element[0].append(closeWrap);
                 tab.element[0].prepend(iconWrap);
                 tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
                     ({ layer, color }) => {
-- 
cgit v1.2.3-70-g09d2


From b292a055401af6236e0537cfad603016d77a535a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 13:40:07 -0400
Subject: a bunch of changes to make some userDoc() field accesses more
 typesafe by going through CurrentUserUtils.  Some normalization of
 naming/functionality in CurrentUserUtils

---
 src/client/apis/youtube/YoutubeBox.tsx             |   2 +-
 src/client/documents/Documents.ts                  |   6 +-
 src/client/util/CurrentUserUtils.ts                | 118 +++++++++++----------
 src/client/util/SettingsManager.tsx                |  16 +--
 src/client/util/SharingManager.tsx                 |   4 +-
 src/client/views/DashboardView.tsx                 |   4 +-
 src/client/views/DocComponent.tsx                  |   6 +-
 src/client/views/DocumentButtonBar.tsx             |   2 +-
 src/client/views/GestureOverlay.tsx                |  12 +--
 src/client/views/GlobalKeyHandler.ts               |   8 +-
 src/client/views/InkStrokeProperties.ts            |   4 +-
 src/client/views/InkTranscription.tsx              |   2 +-
 src/client/views/InkingStroke.tsx                  |   2 +-
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/OverlayView.tsx                   |   8 +-
 src/client/views/PropertiesButtons.tsx             |   4 +-
 src/client/views/PropertiesView.tsx                |  86 ++++++++-------
 src/client/views/TemplateMenu.tsx                  |  11 +-
 .../views/collections/CollectionDockingView.tsx    |  22 ++--
 src/client/views/collections/CollectionMenu.tsx    |  35 +++---
 .../views/collections/CollectionStackingView.tsx   |   2 +-
 .../CollectionStackingViewFieldColumn.tsx          |   4 +-
 .../views/collections/CollectionTreeView.tsx       |   8 +-
 src/client/views/collections/CollectionView.tsx    |  14 +--
 src/client/views/collections/TabDocView.tsx        |   2 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  30 +++---
 .../collections/collectionFreeForm/MarqueeView.tsx |   6 +-
 .../collectionSchema/CollectionSchemaHeaders.tsx   |   2 +-
 src/client/views/nodes/AudioBox.tsx                |   5 +-
 src/client/views/nodes/ColorBox.tsx                |   3 +-
 src/client/views/nodes/DocumentView.tsx            |  40 +++----
 src/client/views/nodes/FilterBox.tsx               |  47 ++++----
 src/client/views/nodes/ImageBox.tsx                |   4 +-
 src/client/views/nodes/LabelBox.tsx                |   2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   4 +-
 src/client/views/nodes/VideoBox.tsx                |   6 +-
 src/client/views/nodes/WebBox.tsx                  |   6 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  35 +++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |  12 +--
 .../views/nodes/formattedText/RichTextMenu.tsx     |   1 -
 src/client/views/nodes/trails/PresBox.tsx          |  30 +++---
 src/client/views/nodes/trails/PresElementBox.tsx   |  16 +--
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 src/client/views/topbar/TopBar.tsx                 |  10 +-
 src/client/views/webcam/DashWebRTCVideo.tsx        |   2 +-
 src/fields/Doc.ts                                  |  18 ++--
 src/fields/util.ts                                 |   2 +-
 src/mobile/MobileInterface.tsx                     |  20 ++--
 49 files changed, 360 insertions(+), 335 deletions(-)

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

diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index c5ff2db68..e14dc60b4 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -351,7 +351,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
         return (
             <>
                 <div className={classname}  >
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 927996af2..f96bffb3c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -687,7 +687,7 @@ export namespace Docs {
             ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
             !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
-                !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc);
+                !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, dataDoc);
 
             updateCachedAcls(dataDoc);
             updateCachedAcls(viewDoc);
@@ -802,7 +802,7 @@ export namespace Docs {
             I.rotation = 0;
             I.data = new InkField(points);
             I.creationDate = new DateField;
-            I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            I["acl-Public"] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             I["acl-Override"] = "None";
             I.links = ComputedField.MakeFunction("links(self)");
             I[Initializing] = false;
@@ -1204,7 +1204,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(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
+            created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, 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 afb1442bf..7a38886fe 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -74,16 +74,16 @@ export class CurrentUserUtils {
     @observable public static headerBarHeight: number = 0;
     @observable public static searchPanelWidth: number = 0;
 
-    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, functions?:{[key:string]: string}) {
+    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, funcs?:{[key:string]: string}) {
         scripts && Object.keys(scripts).map(key => {
             if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
                 doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name,  value:"any", scriptContext: "any" }, {"_readOnly_": true});
             }
         }); 
-        functions && Object.keys(functions).map(key => {
+        funcs && Object.keys(funcs).map(key => {
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            if (ScriptCast(cfield)?.script.originalScript !== functions[key] && functions[key]) {
-                doc[key] = ComputedField.MakeFunction(functions[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
+            if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) {
+                doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
             }
         });
         return doc;
@@ -180,14 +180,14 @@ export class CurrentUserUtils {
     }
 
     /// Initializes collection of templates for notes and click functions
-    static setupDocTemplates(doc: Doc) {
+    static setupDocTemplates(doc: Doc, field="myTemplates") {
         const templates = [
             CurrentUserUtils.setupNoteTemplates(doc),
             CurrentUserUtils.setupClickEditorTemplates(doc)
         ];
         const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
         const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
-        return this.AssignScripts(this.AssignOpts(DocCast(doc.templateDocs), reqdOpts, templates) ?? (doc.templateDocs = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts);
+        return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, templates) ?? (doc[field] = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
@@ -329,7 +329,7 @@ export class CurrentUserUtils {
     static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc {
         const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
-            const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["backgroundColor"]).omit,
+            const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit,
                 _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, _dropAction: "alias",
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
@@ -562,6 +562,7 @@ export class CurrentUserUtils {
         if (Cast(myDashboards.childContextMenuFilters, listSpec(ScriptField), null)?.length !== childContextMenuFilters.length) {
             myDashboards.childContextMenuFilters = new List<ScriptField>(childContextMenuFilters.map(script => !script ? script: ScriptField.MakeFunction(script)!));
         }
+        return myDashboards;
     }
 
     /// initializes the left sidebar Trails pane
@@ -728,13 +729,13 @@ export class CurrentUserUtils {
 
     static inkTools():Button[] {
         return [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveInkTool("pen", _readOnly_);}' }},
-            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveInkTool("write", _readOnly_);}'} },
-            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveInkTool("eraser", _readOnly_);}' }},
-            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveInkTool("highlighter")'} },
-            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveInkTool("circle", _readOnly_);}'} },
-            // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveInkTool("line", _readOnly_);}' }},
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveTool("pen", _readOnly_);}' }},
+            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveTool("write", _readOnly_);}'} },
+            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveTool("eraser", _readOnly_);}' }},
+            // { 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, ignoreClick: true, icon: "fill-drip", scripts: {script: "{ return setFillColor(value, _readOnly_);}"} },
             { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'} },
             { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} },
@@ -941,41 +942,39 @@ export class CurrentUserUtils {
     }
 
     static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
-        if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument();
-        await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
-        reaction(() => DateCast((doc.globalGroupDatabase as Doc)["data-lastModified"]),
+        doc.globalGroupDatabase ?? (doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument());
+        await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
+        reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]),
             async () => {
-                const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
+                const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
                 SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
             }, { fireImmediately: true });
         // Document properties on load
-        doc.system = true;
-        doc.title = Doc.CurrentUserEmail;
-        doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
-        doc._raiseWhenDragged = true;
-        doc._showLabel = true;
-        doc._showMenuLabel = true;
-        doc.textAlign = StrCast(doc.textAlign, "left");
-        doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
-        doc.activeInkWidth = Number(StrCast(doc.activeInkWidth, "1"));
-        doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
-        doc.activeFillColor = StrCast(doc.activeFillColor, "");
-        doc.activeArrowStart = StrCast(doc.activeArrowStart, "");
-        doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "");
-        doc.activeDash = StrCast(doc.activeDash, "0");
-        doc.fontSize = StrCast(doc.fontSize, "12px");
-        doc.fontFamily = StrCast(doc.fontFamily, "Arial");
-        doc.fontColor = StrCast(doc.fontColor, "black");
-        doc.fontHighlight = StrCast(doc.fontHighlight, "");
-        doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false);
-        doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
-        doc.noviceMode = BoolCast(doc.noviceMode, true);
-        !doc.savedFilters && (doc.savedFilters = new List<Doc>());
+        doc.system ?? (doc.system = true);
+        doc.title ?? (doc.title = Doc.CurrentUserEmail);
+        Doc.noviceMode ?? (Doc.noviceMode = true);
+        doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true);
+        doc._showLabel ?? (doc._showLabel = true);
+        doc._showMenuLabel ?? (doc._showMenuLabel = true);
+        doc.textAlign ?? (doc.textAlign = "left");
+        doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");;
+        doc.activeInkWidth ?? (doc.activeInkWidth = 1);
+        doc.activeInkBezier ?? (doc.activeInkBezier = "0");
+        doc.activeFillColor ?? (doc.activeFillColor = "");
+        doc.activeArrowStart ?? (doc.activeArrowStart = "");
+        doc.activeArrowEnd ?? (doc.activeArrowEnd = "");
+        doc.activeDash ?? (doc.activeDash == "0");
+        doc.fontSize ?? (doc.fontSize = "12px");
+        doc.fontFamily ?? (doc.fontFamily = "Arial");
+        doc.fontColor ?? (doc.fontColor = "black");
+        doc.fontHighlight ?? (doc.fontHighlight = "");
+        doc.defaultAclPrivate ?? (doc.defaultAclPrivate = false);
+        doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myPublishedDocs = doc.myPublishedDocs ?? new List<Doc>();
-        doc.myHeaderBar = doc.myHeaderBar?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        doc.myPublishedDocs ?? (doc.myPublishedDocs = new List<Doc>());
+        doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
         await this.setupLinkDocs(doc, linkDatabaseId);
         await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
@@ -985,7 +984,7 @@ export class CurrentUserUtils {
         this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
-        if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument();
+        doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument());
 
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
@@ -1022,7 +1021,7 @@ export class CurrentUserUtils {
                     const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
                     Docs.newAccount &&(userDoc.activePage = "home");
                     const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
-                    (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
+                    (await DocListCastAsync(Doc.LinkDBDoc()?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
                         const a1 = await Cast(link?.anchor1, Doc, null);
                         const a2 = await Cast(link?.anchor2, Doc, null);
                     });
@@ -1097,7 +1096,7 @@ export class CurrentUserUtils {
                     }
                 }
             } else if (input.files && input.files.length !== 0) {
-                const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null);
+                const importDocs = CurrentUserUtils.MyImports;
                 const disposer = OverlayView.ShowSpinner();
                 DocListCastAsync(importDocs.data).then(async list => {
                     const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {});
@@ -1115,9 +1114,10 @@ export class CurrentUserUtils {
     }
 
     public static CaptureDashboardThumbnail() {
+        const activeDashboard = CurrentUserUtils.ActiveDashboard;
         const docView = CollectionDockingView.Instance.props.DocumentView?.();
         const content = docView?.ContentDiv;
-        if (docView && content) {
+        if (docView && content && activeDashboard) {
             const _width = Number(getComputedStyle(content).width.replace("px",""));
             const _height = Number(getComputedStyle(content).height.replace("px",""));
             return CollectionFreeFormView.UpdateIcon(
@@ -1130,20 +1130,22 @@ export class CurrentUserUtils {
                     const proto = Cast(img.proto, Doc, null)!;
                     proto["data-nativeWidth"] = _width;
                     proto["data-nativeHeight"] = _height;
-                    Doc.GetProto(CurrentUserUtils.ActiveDashboard).thumb = img;
+                    Doc.GetProto(activeDashboard).thumb = img;
                 });
         }
 
     }
 
     public static async snapshotDashboard(userDoc: Doc) {
-        const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
-        Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
-        CurrentUserUtils.openDashboard(userDoc, copy);
+        if (CurrentUserUtils.ActiveDashboard) {
+            const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
+            Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
+            CurrentUserUtils.openDashboard(userDoc, copy);
+        }
     }
 
     public static closeActiveDashboard = () => {
-          Doc.UserDoc().activeDashboard = undefined;
+        CurrentUserUtils.ActiveDashboard = undefined;
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
@@ -1195,6 +1197,8 @@ export class CurrentUserUtils {
     public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); }
     public static get MyTools() { return DocCast(Doc.UserDoc().myTools); }
     public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); }
+    public static get MyFileOrphans() { return DocCast(Doc.UserDoc().myFileOrphans); }
+    public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); }
     public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); }
     public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); }
     public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); }
@@ -1202,12 +1206,16 @@ export class CurrentUserUtils {
     public static get MyImports() { return DocCast(Doc.UserDoc().myImports); }
     public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
     public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
-    public static get MyOverlayDocs() { return DocListCast(DocCast(Doc.UserDoc().myOverlayDocs)?.data); }
+    public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); }
     public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
+    public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; }
     public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
+    public static set ActivePresentation(val) { Doc.UserDoc().activePresentation = val; }
+    public static get ActivePage() { return StrCast(Doc.UserDoc().activePage); }
+    public static set ActivePage(val) { Doc.UserDoc().activePage = val; }
     public static get EmptyPane() { return DocCast(Doc.UserDoc().emptyPane); }
-    public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
-    @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
+    public static set ActiveTool(tool: InkTool) { Doc.UserDoc().activeTool = tool; }
+    public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; }
 }
 
 ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
@@ -1219,7 +1227,7 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
     }
 });
 ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
-ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode");
+ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
 ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
 ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called");
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 6a26dfdc7..382274462 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -38,7 +38,7 @@ export class SettingsManager extends React.Component<{}> {
     @observable activeTab = "Accounts";
 
     @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
-    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard.colorScheme; }
+    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme; }
 
     constructor(props: {}) {
         super(props);
@@ -59,7 +59,7 @@ export class SettingsManager extends React.Component<{}> {
         }
     }
 
-    @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.UserDoc().noviceMode = (e.currentTarget as any)?.value === "Novice");
+    @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.noviceMode = (e.currentTarget as any)?.value === "Novice");
     @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value ? "title" : undefined);
     @undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
     @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
@@ -78,19 +78,21 @@ export class SettingsManager extends React.Component<{}> {
     @undoBatch
     @action
     changeColorScheme = action((e: React.ChangeEvent) => {
+        const activeDashboard= CurrentUserUtils.ActiveDashboard;
+        if (!activeDashboard) return;
         const scheme: ColorScheme = (e.currentTarget as any).value;
         switch (scheme) {
             case ColorScheme.Light:
-                CurrentUserUtils.ActiveDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+                activeDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" });
                 break;
             case ColorScheme.Dark:
-                CurrentUserUtils.ActiveDashboard.colorScheme = ColorScheme.Dark;
+                activeDashboard.colorScheme = ColorScheme.Dark;
                 addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" });
                 break;
             case ColorScheme.System: default:
                 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
-                    CurrentUserUtils.ActiveDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+                    activeDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 });
                 break;
         }
@@ -254,7 +256,7 @@ export class SettingsManager extends React.Component<{}> {
             <div className="tab-column">
                 <div className="tab-column-title">Modes</div>
                 <div className="tab-column-content">
-                    <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.UserDoc().noviceMode ? "Novice" : "Developer"}>
+                    <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.noviceMode ? "Novice" : "Developer"}>
                         <option key={"Novice"} value={"Novice"}> Novice </option>
                         <option key={"Developer"} value={"Developer"}> Developer</option>
                     </select>
@@ -270,7 +272,7 @@ export class SettingsManager extends React.Component<{}> {
                     <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
                     <div className="default-acl">
                         <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)}
-                            onChange={action(() => Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
+                            onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
                         <div className="acl-text">Default access private</div>
                     </div>
                 </div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index f439d4488..d77633b8d 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -369,7 +369,7 @@ export class SharingManager extends React.Component<{}> {
         const dropdownValues: string[] = Object.values(SharingPermissions);
         if (!uniform) dropdownValues.unshift("-multiple-");
         if (override) dropdownValues.unshift("None");
-        return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
+        return dropdownValues.filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
         (
             <option key={permission} value={permission}>
                 {permission}
@@ -680,7 +680,7 @@ export class SharingManager extends React.Component<{}> {
                         </div>
 
                         <div className="acl-container">
-                            {Doc.UserDoc().noviceMode ? (null) :
+                            {Doc.noviceMode ? (null) :
                                 <div className="layoutDoc-acls">
                                     <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
                                 </div>}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index efc1644fe..9a0f25fe3 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -34,8 +34,8 @@ export class DashboardView extends React.Component {
 
       clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
             if (e.detail === 2) {
-                  Doc.UserDoc().activeDashboard = dashboard;
-                  Doc.UserDoc().activePage = "dashboard";
+                  CurrentUserUtils.ActiveDashboard = dashboard;
+                  CurrentUserUtils.ActivePage = "dashboard";
             }
       }
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index be5927497..169bd3873 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -65,7 +65,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
 
         isContentActive = (outsideReaction?: boolean) => (
             this.props.isContentActive?.() === false ? false :
-                (CurrentUserUtils.SelectedTool !== InkTool.None ||
+                (CurrentUserUtils.ActiveTool !== InkTool.None ||
                     (this.props.isContentActive?.() || this.props.Document.forceActive ||
                         this.props.isSelected(outsideReaction) ||
                         this.props.rootSelected(outsideReaction)) ? true : undefined))
@@ -200,7 +200,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                     if (effectiveAcl === AclAugment) {
                         added.map(doc => {
-                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
+                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
@@ -213,7 +213,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
 
-                            inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
+                            CurrentUserUtils.ActiveDashboard && inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                         });
                         const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                         if (annoDocs instanceof List) annoDocs.push(...added);
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 0bbe473d7..9b8f7238d 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -347,7 +347,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.menuButton}
             </div>
-            {/* {Doc.UserDoc().noviceMode ? (null) : <div className="documentButtonBar-button">
+            {/* {Doc.noviceMode ? (null) : <div className="documentButtonBar-button">
                 {this.moreButton}
             </div>} */}
         </div>;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 6afe64868..e960f5cca 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -129,7 +129,7 @@ export class GestureOverlay extends Touchable {
             // and this seems to be the only way of differentiating pen and touch on touch events
             if (pt.radiusX > 1 && pt.radiusY > 1) {
                 InkTranscription.Instance.createInkGroup();
-                Doc.UserDoc().activeInkTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
         }
@@ -498,18 +498,18 @@ export class GestureOverlay extends Touchable {
             setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
                 if (doubleTap) {
                     InkTranscription.Instance.createInkGroup();
-                    CurrentUserUtils.SelectedTool = InkTool.None;
+                    CurrentUserUtils.ActiveTool = InkTool.None;
                     return;
                 }
             }));
         }
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                CurrentUserUtils.SelectedTool = InkTool.Write;
+                CurrentUserUtils.ActiveTool = InkTool.Write;
             }
             this._points.push({ X: e.clientX, Y: e.clientY });
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
-            // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+            // if (CurrentUserUtils.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
         }
     }
 
@@ -606,7 +606,7 @@ export class GestureOverlay extends Touchable {
                 this._points = [];
                 if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
                     this.InkShape = "";
-                    Doc.UserDoc().activeInkTool = InkTool.None;
+                    CurrentUserUtils.ActiveTool = InkTool.None;
                 }
             }
             // if we're not drawing in a toolglass try to recognize as gesture
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 16d7c9a23..f5122df3f 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -57,7 +57,7 @@ export class KeyManager {
 
     public handle = action((e: KeyboardEvent) => {
         if (e.key?.toLowerCase() === "shift") DocumentDecorations.Instance.AddToSelection = true;
-        //if (!Doc.UserDoc().noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
+        //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
         const keyname = e.key && e.key.toLowerCase();
         this.handleGreedy(keyname);
 
@@ -112,7 +112,7 @@ export class KeyManager {
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
                 InkStrokeProperties.Instance._controlButton = false;
-                CurrentUserUtils.SelectedTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 DragManager.CompleteWindowDrag?.(true);
                 var doDeselect = true;
                 if (SnappingManager.GetIsDragging()) {
@@ -241,9 +241,9 @@ export class KeyManager {
                     }
                 }
                 break;
-            case "e": CurrentUserUtils.SelectedTool = InkTool.Eraser;
+            case "e": CurrentUserUtils.ActiveTool = InkTool.Eraser;
                 break;
-            case "p": CurrentUserUtils.SelectedTool = InkTool.Pen;
+            case "p": CurrentUserUtils.ActiveTool = InkTool.Pen;
                 break;
             case "o":
                 const target = SelectionManager.Docs().lastElement();
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 0449da483..471ad09e9 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -25,8 +25,8 @@ export class InkStrokeProperties {
 
     constructor() {
         InkStrokeProperties._Instance = this;
-        reaction(() => this._controlButton, button => button && (CurrentUserUtils.SelectedTool = InkTool.None));
-        reaction(() => CurrentUserUtils.SelectedTool, tool => (tool !== InkTool.None) && (this._controlButton = false));
+        reaction(() => this._controlButton, button => button && (CurrentUserUtils.ActiveTool = InkTool.None));
+        reaction(() => CurrentUserUtils.ActiveTool, tool => (tool !== InkTool.None) && (this._controlButton = false));
     }
 
     /**
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 8ab54918c..487bbcd00 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -254,7 +254,7 @@ export class InkTranscription extends React.Component {
  */
 createInkGroup() {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index aa5a815ac..35d89d2b1 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -345,7 +345,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             onClick: (e: React.MouseEvent) => this._handledClick && e.stopPropagation(),
             onContextMenu: () => {
                 const cm = ContextMenu.Instance;
-                !Doc.UserDoc().noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, 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" });
             }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7e3916b3f..791c64630 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -381,7 +381,7 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === CurrentUserUtils.MyDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -656,7 +656,7 @@ export class MainView extends React.Component {
                             <GestureOverlay> {this.mainDashboardArea}   </GestureOverlay>
                         </>;
                     case "home":  return <DashboardView/>;
-                } })(StrCast(Doc.UserDoc().activePage))
+                } })(CurrentUserUtils.ActivePage)
             }
             
             <PreviewCursor />
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index bdb3bb857..598fff29a 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import * as React from "react";
 import ReactLoading from 'react-loading';
-import { Doc, WidthSym, HeightSym } from "../../fields/Doc";
+import { Doc, WidthSym, HeightSym, DocListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast } from "../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../Utils";
@@ -149,7 +149,7 @@ export class OverlayView extends React.Component {
     }
 
     removeOverlayDoc = (doc: Doc | Doc[]) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc));
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc));
         return true;
     }
     
@@ -158,7 +158,7 @@ export class OverlayView extends React.Component {
     }.bind(this));
 
     @computed get overlayDocs() {
-        return CurrentUserUtils.MyOverlayDocs?.map(d => {
+        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).map(d => {
             let offsetx = 0, offsety = 0;
             const dref = React.createRef<HTMLDivElement>();
             const onPointerMove = action((e: PointerEvent, down: number[]) => {
@@ -172,7 +172,7 @@ export class OverlayView extends React.Component {
                     dragData.dropAction = "move";
                     dragData.removeDocument = (doc: Doc | Doc[]) => {
                         const docs = (doc instanceof Doc) ? [doc] : doc;
-                        docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocs, Doc, null), "data", d));
+                        docs.forEach(d => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, d));
                         return true;
                     };
                     dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 74055f057..9c6d9a108 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -200,7 +200,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     {list}
                 </div>
             </div>
-            {Doc.UserDoc().noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
+            {Doc.noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
         </div>;
     }
     @computed
@@ -229,7 +229,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isTree = this.selectedDoc?._viewType === CollectionViewType.Tree;
         const isTabView = this.selectedTabView;
         const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => <div className="propertiesButtons-button" style={style}> {ele} </div>;
-        const isNovice = Doc.UserDoc().noviceMode;
+        const isNovice = Doc.noviceMode;
         return !this.selectedDoc ? (null) :
             <div className="propertiesButtons">
                 {toggle(this.titleButton)}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 2cf334ae1..90c86fa18 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -11,7 +11,7 @@ import { Id } from "../../fields/FieldSymbols";
 import { InkField } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { ComputedField } from "../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast, DocCast } from "../../fields/Types";
 import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from "../../fields/util";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils";
 import { DocumentType } from "../documents/DocumentTypes";
@@ -340,7 +340,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     changePermissions = (e: any, user: string) => {
-        const docs = SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+        const docs = SelectionManager.Views().length < 2 ? (this.selectedDoc ? [this.selectedDoc]:[]) : SelectionManager.Views().map(docView => docView.props.Document);
         SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
     }
 
@@ -355,7 +355,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             value={permission}
             onChange={e => this.changePermissions(e, user)}>
             {dropdownValues.filter(permission =>
-                !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
+                !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
                     <option key={permission} value={permission}>  {permission}  </option>)}
         </select>;
     }
@@ -419,7 +419,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // all selected docs
         const docs = SelectionManager.Views().length < 2 ?
-            [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc[DataSym]]
+            [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]]
             : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]);
 
         const target = docs[0];
@@ -907,7 +907,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             {!this.openSharing ? (null) :
                 <div className="propertiesView-sharing-content">
                     <div className="propertiesView-buttonContainer">
-                        {!Doc.UserDoc().noviceMode ? (<div className="propertiesView-acls-checkbox">
+                        {!Doc.noviceMode ? (<div className="propertiesView-acls-checkbox">
                             <Checkbox
                                 color="primary"
                                 onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)}
@@ -931,42 +931,46 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * If it doesn't exist, it creates it.
      */
     checkFilterDoc() {
-        if (!this.selectedDoc.currentFilter) this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (!this.selectedDoc?.currentFilter) this.selectedDoc!.currentFilter = CurrentUserUtils.createFilterDoc();
     }
 
     /**
      * Creates a new currentFilter for this.filterDoc, 
      */
     createNewFilterDoc = () => {
-        const currentDocFilters = this.selectedDoc._docFilters;
-        const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
-        this.selectedDoc._docFilters = new List<string>();
-        this.selectedDoc._docRangeFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
-        (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-        this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (this.selectedDoc) {
+            const currentDocFilters = this.selectedDoc._docFilters;
+            const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
+            this.selectedDoc._docFilters = new List<string>();
+            this.selectedDoc._docRangeFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+            (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+            this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        }
     }
 
     /**
      * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter
      */
     updateFilterDoc = (doc: Doc) => {
-        if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
-        const savedDocFilters = doc._docFiltersList;
-        const currentDocFilters = this.selectedDoc._docFilters;
-        this.selectedDoc._docFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
-        this.selectedDoc.currentFilter = doc;
-        doc._docFiltersList = new List<string>();
-        this.selectedDoc._docFilters = savedDocFilters;
-
-        const savedDocRangeFilters = doc._docRangeFiltersList;
-        const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
-        this.selectedDoc._docRangeFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-        this.selectedDoc.currentFilter = doc;
-        doc._docRangeFiltersList = new List<string>();
-        this.selectedDoc._docRangeFilters = savedDocRangeFilters;
+        if (this.selectedDoc) {
+            if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
+            const savedDocFilters = doc._docFiltersList;
+            const currentDocFilters = this.selectedDoc._docFilters;
+            this.selectedDoc._docFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+            this.selectedDoc.currentFilter = doc;
+            doc._docFiltersList = new List<string>();
+            this.selectedDoc._docFilters = savedDocFilters;
+
+            const savedDocRangeFilters = doc._docRangeFiltersList;
+            const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
+            this.selectedDoc._docRangeFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+            this.selectedDoc.currentFilter = doc;
+            doc._docRangeFiltersList = new List<string>();
+            this.selectedDoc._docRangeFilters = savedDocRangeFilters;
+        }
     }
 
     @computed get filtersSubMenu() {
@@ -1059,13 +1063,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
                 </div>
             </div>
-            {!Doc.UserDoc().noviceMode && this.openFields ? <div className="propertiesView-fields-checkbox">
+            {!Doc.noviceMode && this.openFields ? <div className="propertiesView-fields-checkbox">
                 {this.fieldsCheckbox}
                 <div className="propertiesView-fields-checkbox-text">Layout</div>
             </div> : null}
             {!this.openFields ? (null) :
                 <div className="propertiesView-fields-content">
-                    {Doc.UserDoc().noviceMode ? this.noviceFields : this.expandedField}
+                    {Doc.noviceMode ? this.noviceFields : this.expandedField}
                 </div>}
         </div>;
     }
@@ -1122,7 +1126,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     handleDescriptionChange = action((value: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.description = value;
             this.description = value;
             return true;
@@ -1131,7 +1135,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     handleLinkRelationshipChange = action((value: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.linkRelationship = value;
             this.relationship = value;
             return true;
@@ -1188,7 +1192,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     changeFollowBehavior = action((follow: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.followLinkLocation = follow;
             return true;
         }
@@ -1219,18 +1223,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     toggleAnchor = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove))));
     }
 
     toggleArrow = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow))));
     }
 
     toggleZoomToTarget1 = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor1).followLinkZoom = !DocCast(this.selectedDoc.anchor1).followLinkZoom))));
     }
     toggleZoomToTarget2 = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor2).followLinkZoom = !DocCast(this.selectedDoc.anchor2).followLinkZoom))));
     }
 
     @computed
@@ -1238,7 +1242,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return <input
             autoComplete={"off"}
             id="link_relationship_input"
-            value={StrCast(this.selectedDoc.linkRelationship)}
+            value={StrCast(this.selectedDoc?.linkRelationship)}
             onKeyDown={this.onRelationshipKey}
             onBlur={this.onSelectOutRelationship}
             onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
@@ -1252,7 +1256,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return <input
             autoComplete={"off"}
             id="link_description_input"
-            value={StrCast(this.selectedDoc.description)}
+            value={StrCast(this.selectedDoc?.description)}
             onKeyDown={this.onDescriptionKey}
             onBlur={this.onSelectOutDesc}
             onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
@@ -1273,7 +1277,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     // }
 
     render() {
-        const isNovice = BoolCast(Doc.UserDoc().noviceMode);
+        const isNovice = Doc.noviceMode;
         if (!this.selectedDoc && !this.isPres) {
             return <div className="propertiesView" style={{ width: this.props.width }}>
                 <div className="propertiesView-title" style={{ width: this.props.width }}>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 636f7042f..689ee4fc1 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -15,6 +15,7 @@ import { DocumentView } from "./nodes/DocumentView";
 import { DefaultStyleProvider } from "./StyleProvider";
 import './TemplateMenu.scss';
 import React = require("react");
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
 
 @observer
 class TemplateToggle extends React.Component<{ template: string, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
@@ -111,22 +112,22 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const firstDoc = this.props.docViews[0].props.Document;
         const templateName = StrCast(firstDoc.layoutKey, "layout").replace("layout_", "");
         const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
-        const addedTypes = Doc.UserDoc().noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
+        const addedTypes = Doc.noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
         const layout = Doc.Layout(firstDoc);
         const templateMenu: Array<JSX.Element> = [];
         this.props.templates?.forEach((checked, template) =>
             templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
         templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
-        !Doc.UserDoc().noviceMode && templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={!layout._chromeHidden} toggle={this.toggleChrome} />);
+        !Doc.noviceMode && templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={!layout._chromeHidden} toggle={this.toggleChrome} />);
         addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template));
         this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push(
             <OtherToggle key={template} name={template} checked={templateName === template} toggle={e => this.toggleLayout(e, template)} />));
         return <ul className="template-list" style={{ display: "block" }}>
-            {Doc.UserDoc().noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
+            {Doc.noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
             {templateMenu}
-            {Doc.UserDoc().noviceMode ? (null) : <CollectionTreeView
-                Document={Doc.UserDoc().templateDocs as Doc}
+            {Doc.noviceMode ? (null) : <CollectionTreeView
+                Document={CurrentUserUtils.MyTemplates}
                 CollectionView={undefined}
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1ebbe08ef..5f36a7a51 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -368,7 +368,7 @@ export class CollectionDockingView extends CollectionSubView() {
             }
         }
         if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
-            ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+            ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             e.stopPropagation();
         }
     }
@@ -433,8 +433,8 @@ export class CollectionDockingView extends CollectionSubView() {
 
     stackCreated = (stack: any) => {
         stack.header?.element.on('mousedown', (e: any) => {
-            if (e.target === stack.header?.element[0] && e.button === 2) {
-                const dashboard= CurrentUserUtils.ActiveDashboard;
+            const dashboard = CurrentUserUtils.ActiveDashboard;
+            if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
                 dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`,
@@ -462,12 +462,14 @@ export class CollectionDockingView extends CollectionSubView() {
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
                 const dashboard = CurrentUserUtils.ActiveDashboard;
-                dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
-                const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
-                });
-                this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
-                CollectionDockingView.AddSplit(docToAdd, "", stack);
+                if (dashboard) {
+                    dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
+                    const docToAdd = Docs.Create.FreeformDocument([], {
+                        _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
+                    });
+                    this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
+                    CollectionDockingView.AddSplit(docToAdd, "", stack);
+                }
             }));
     }
 
@@ -480,6 +482,6 @@ ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(
     "opens up document in a lightbox", "(doc: any)");
 ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
     "opens up document in tab on right side of the screen", "(doc: any)");
-ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); },
+ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); },
     "opens up document in screen overlay layer", "(doc: any)");
 ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 1c25421f5..9b1bb5b97 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -268,17 +268,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                  self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
         immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
         initialize: (button: Doc) => {
-            button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) instanceof ObjectField ?
-                ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) as any as ObjectField) : undefined;
-            button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined;
+            const activeDash = CurrentUserUtils.ActiveDashboard;
+            if (activeDash) {
+                button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ?
+                    ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined;
+                button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined;
+            }
         },
     };
 
-    @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
-    @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
-    @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
-    @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
-    @computed get _doc_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
+    @computed get _freeform_commands() { return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
+    @computed get _stacking_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
+    @computed get _masonry_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
+    @computed get _schema_commands() { return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
+    @computed get _doc_commands() { return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
     @computed get _tree_commands() { return undefined; }
     private get _buttonizableCommands() {
         switch (this.props.type) {
@@ -467,7 +470,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     @action
     startRecording = () => {
         const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
-        //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+        //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
         CollectionDockingView.AddSplit(doc, "right");
     }
 
@@ -691,8 +694,8 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
     @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; }
     @action primCreated() {
         if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
-            if (CurrentUserUtils.SelectedTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
-            CurrentUserUtils.SelectedTool = InkTool.None;
+            if (CurrentUserUtils.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
+            CurrentUserUtils.ActiveTool = InkTool.None;
             this._selectedPrimitive = this._shapePrims.length;
             SetActiveArrowStart("none");
             SetActiveArrowEnd("none");
@@ -734,11 +737,11 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
             if (this._selectedPrimitive !== i) {
                 this._selectedPrimitive = i;
                 if (this._title[i] === "highlighter") {
-                    CurrentUserUtils.SelectedTool = InkTool.Highlighter;
+                    CurrentUserUtils.ActiveTool = InkTool.Highlighter;
                     GestureOverlay.Instance.SavedColor = ActiveInkColor();
                     SetActiveInkColor("rgba(245, 230, 95, 0.75)");
                 } else {
-                    CurrentUserUtils.SelectedTool = InkTool.Pen;
+                    CurrentUserUtils.ActiveTool = InkTool.Pen;
                 }
                 SetActiveArrowStart(this._head[i]);
                 SetActiveArrowEnd(this._end[i]);
@@ -747,7 +750,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                 GestureOverlay.Instance.InkShape = this._shapePrims[i];
             } else {
                 this._selectedPrimitive = this._shapePrims.length;
-                CurrentUserUtils.SelectedTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 SetActiveArrowStart("");
                 SetActiveArrowEnd("");
                 GestureOverlay.Instance.InkShape = "";
@@ -840,7 +843,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                         {this.widthPicker}
                         {this.colorPicker}
                         {this.fillPicker}
-                        {Doc.UserDoc().noviceMode || this.props.isDoc ? (null) :
+                        {Doc.noviceMode || this.props.isDoc ? (null) :
                             <>
                                 <Tooltip key="back" title={<div className="dash-tooltip">Back Frame</div>} placement="bottom">
                                     <div className="backKeyframe" onClick={this.prevKeyframe}>
@@ -879,7 +882,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
         const val = value.toLowerCase();
         const docs = DocListCast(this.document[this.props.fieldKey]);
 
-        if (Doc.UserDoc().noviceMode) {
+        if (Doc.noviceMode) {
             if (docs instanceof Doc) {
                 const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
                     key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index dddae4a34..277fcd59c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -598,7 +598,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             <>
                 {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
                     {buttonMenu ? this.buttonMenu : null}
-                    {Doc.UserDoc().noviceMode && noviceExplainer ?
+                    {Doc.noviceMode && noviceExplainer ?
                         <div className="documentExplanation">
                             {noviceExplainer}
                         </div>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 7f96217b8..f3a798143 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -224,8 +224,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                     }
                 }, icon: "compress-arrows-alt"
             }));
-        !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" });
-        !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
+        !Doc.noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" });
+        !Doc.noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
         ContextMenu.Instance.setDefaultItem("::", (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = "";
             const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e809bfbce..ba72fb7b9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -67,7 +67,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; }
     @computed get outlineMode() { return this.doc.treeViewType === TreeViewType.outline; }
     @computed get fileSysMode() { return this.doc.treeViewType === TreeViewType.fileSystem; }
-    @computed get dashboardMode() { return this.doc === Doc.UserDoc().myDashboards; }
+    @computed get dashboardMode() { return this.doc === CurrentUserUtils.MyDashboards; }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
 
@@ -76,7 +76,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
     @observable _isAnyChildContentActive = false;
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
-    isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None ||
+    isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.ActiveTool !== InkTool.None ||
         (this.props.isContentActive?.() || this.props.Document.forceActive ||
             this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
             this.props.rootSelected(outsideReaction)) ? true : false)
@@ -164,7 +164,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             const layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: "Make tree state " + (this.doc.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
@@ -286,7 +286,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     @computed get noviceExplainer() {
-        return !Doc.UserDoc().noviceMode || !this.rootDoc.explainer ? (null) :
+        return !Doc.noviceMode || !this.rootDoc.explainer ? (null) :
             <div className="documentExplanation"> {this.rootDoc.explainer} </div>;
     }
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 965f0a352..4b5c5e3fb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -160,8 +160,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
         subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
         subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" });
-        !Doc.UserDoc().noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
-        !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
+        !Doc.noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
+        !Doc.noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
         subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
 
         if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
@@ -184,16 +184,16 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
             const options = cm.findByDescription("Options...");
             const optionItems = options && "subitems" in options ? options.subitems : [];
-            !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null;
+            !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null;
             if (this.rootDoc.childLayout instanceof Doc) {
                 optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" });
             }
             if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) {
                 optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" });
             }
-            !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
+            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
 
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 optionItems.push({
                     description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
                 });
@@ -210,7 +210,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
 
-            if (!Doc.UserDoc().noviceMode && !this.rootDoc.annotationOn) {
+            if (!Doc.noviceMode && !this.rootDoc.annotationOn) {
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
                 const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }];
@@ -230,7 +230,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
             }
 
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 const more = cm.findByDescription("More...");
                 const moreItems = more && "subitems" in more ? more.subitems : [];
                 moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) });
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 022b9fa24..70db121d1 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -277,7 +277,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
         });
         if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
-            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+            const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []);
             if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
             CollectionDockingView.AddSplit(curPres, "right");
             setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 59dc5671b..704b8989a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -114,7 +114,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey :
             this.props.treeView.dashboardMode ? this.fieldKey :
                 this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : // for displaying 
-                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
+                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
     }
 
     @computed get doc() { return this.props.document; }
@@ -560,8 +560,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : "";
         const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : "";
         const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases";
-        const fields = () => Doc.UserDoc().noviceMode ? "" : "fields";
-        const layout = (Doc.UserDoc().noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+        const fields = () => Doc.noviceMode ? "" : "fields";
+        const layout = (Doc.noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
         return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c07e44fcc..ffe146ae4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -458,7 +458,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
                 !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
                 !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                switch (CurrentUserUtils.SelectedTool) {
+                switch (CurrentUserUtils.ActiveTool) {
                     case InkTool.Highlighter:
                         break;
                     // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
@@ -497,7 +497,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.addMoveListeners();
                     this.removeEndListeners();
                     this.addEndListeners();
-                    if (CurrentUserUtils.SelectedTool === InkTool.None) {
+                    if (CurrentUserUtils.ActiveTool === InkTool.None) {
                         this._lastX = pt.pageX;
                         this._lastY = pt.pageY;
                         e.preventDefault();
@@ -519,9 +519,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
-                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
+                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
-                if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+                if (CurrentUserUtils.ActiveTool === InkTool.Write) {
                     this.unprocessedDocs.push(inkDoc);
                     CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
                 }
@@ -711,7 +711,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onPointerMove = (e: PointerEvent): void => {
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
             if (this.tryDragCluster(e, this._hitCluster)) {
@@ -834,7 +834,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
             if (myTouches[0]) {
-                if (CurrentUserUtils.SelectedTool === InkTool.None) {
+                if (CurrentUserUtils.ActiveTool === InkTool.None) {
                     if (this.tryDragCluster(e, this._hitCluster)) {
                         e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
                         e.preventDefault();
@@ -985,7 +985,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.MyOverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.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();
         }
@@ -1031,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
             }
         }
-        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.MyOverlayDocs.includes(this.Document)) {
+        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) {
             this._viewTransition = panTime;
             const scale = this.getLocalTransform().inverse().Scale;
             const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
@@ -1629,7 +1629,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearance = ContextMenu.Instance.findByDescription("Appearance...");
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
-        !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
+        !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
         appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
         //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
@@ -1640,27 +1640,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         // this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" });
 
-        !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
+        !Doc.noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
         !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-        !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
-        !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
 
         const options = ContextMenu.Instance.findByDescription("Options...");
         const optionItems = options && "subitems" in options ? options.subitems : [];
-        !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode &&
+        !this.props.isAnnotationOverlay && !Doc.noviceMode &&
             optionItems.push({ description: (this._showAnimTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this._showAnimTimeline = !this._showAnimTimeline), icon: "eye" });
         this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
         }
         !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
         const mores = ContextMenu.Instance.findByDescription("More...");
         const moreItems = mores && "subitems" in mores ? mores.subitems : [];
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
             moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) });
         }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 051da795f..b62020a04 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -330,7 +330,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     onClick = (e: React.MouseEvent): void => {
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            if (CurrentUserUtils.SelectedTool === InkTool.None) {
+            if (CurrentUserUtils.ActiveTool === InkTool.None) {
                 if (!(e.nativeEvent as any).marqueeHit) {
                     (e.nativeEvent as any).marqueeHit = true;
                     if (!this.props.trySelectCluster(e.shiftKey)) {
@@ -367,7 +367,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
             Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
-            !this.props.isAnnotationOverlay && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(doc));
+            !this.props.isAnnotationOverlay && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc));
             doc._panX = doc._panY = 0;
             return doc;
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
@@ -643,7 +643,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         return <div className="marqueeView"
             style={{
                 overflow: StrCast(this.props.Document._overflow),
-                cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
+                cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
             }}
 
             onDragOver={e => e.preventDefault()}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
index 0875c80b3..9653f2808 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
@@ -321,7 +321,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
         const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"];
         const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
         const showKeys = new Set<string>();
-        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.UserDoc().noviceMode ||
+        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.noviceMode ||
             whitelistKeys.includes(key)
             || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null);
         return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm));
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 669622455..8e24fce11 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -348,11 +348,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             Doc.GetProto(newDoc).recordingSource = this.dataDoc;
             Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
             Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-            const overlayDoc = Doc.UserDoc().myOverlayDocs as Doc;
-            if (DocListCast(overlayDoc[Doc.LayoutFieldKey(overlayDoc)]).includes(this.rootDoc)) {
+            if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc)) {
                 newDoc.x = this.rootDoc.x;
                 newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-                Doc.AddDocToList(overlayDoc, undefined, newDoc);
+                Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, newDoc);
             } else {
                 this.props.addDocument?.(newDoc);
             }
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index d975baf9b..6d05fca5e 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -22,7 +22,6 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     static switchColor(color: ColorState) {
-        // Doc.UserDoc().backgroundColor = Utils.colorString(color); // bcz: this can't go here ... needs a proper home in the settings panel
         SetActiveInkColor(color.hex);
 
         SelectionManager.Views().map(view => {
@@ -49,7 +48,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }} >
 
             <SketchPicker
-                onChange={c => CurrentUserUtils.SelectedTool === InkTool.None && ColorBox.switchColor(c)}
+                onChange={c => CurrentUserUtils.ActiveTool === InkTool.None && ColorBox.switchColor(c)}
                 color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())}
                 presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986',
                     '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e2b37d2b4..4d84a8ad2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -327,7 +327,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._downX = touch.clientX;
             this._downY = touch.clientY;
             if (!e.nativeEvent.cancelBubble) {
-                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
+                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
                 this.removeEndListeners();
@@ -341,7 +341,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.cancelBubble && this.props.isDocumentActive?.()) {
             this.removeMoveListeners();
         }
-        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
@@ -543,9 +543,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     });
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.SelectedTool === InkTool.Eraser) return;
+        if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.ActiveTool === InkTool.Eraser) return;
         // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
-        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) {
+        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) {
             if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
                 e.stopPropagation();
                 if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
@@ -563,7 +563,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 !this.Document.ignoreClick &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+                !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
                 e.stopPropagation();
                 // don't preventDefault anymore.  Goldenlayout, PDF text selection and RTF text selection all need it to go though
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault();
@@ -579,9 +579,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     onPointerMove = (e: PointerEvent): void => {
         if (e.cancelBubble) return;
-        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return;
+        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
                     document.removeEventListener("pointermove", this.onPointerMove);
@@ -697,7 +697,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
-        if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) {
+        if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) {
             e.preventDefault();
             e.stopPropagation();
             //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
@@ -747,8 +747,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
             const appearance = cm.findByDescription("UI Controls...");
             const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
-            !Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
-            !Doc.UserDoc().noviceMode && appearanceItems.push({
+            !Doc.noviceMode && templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
+            !Doc.noviceMode && appearanceItems.push({
                 description: "Add a Field", event: () => {
                     const alias = Doc.MakeAlias(this.rootDoc);
                     alias.layout = FormattedTextBox.LayoutString("newfield");
@@ -765,7 +765,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
 
             if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
-                !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
+                !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
 
@@ -778,8 +778,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 }
                 !zorders && cm.addItem({ description: "ZOrder...", noexpand: true, subitems: zorderItems, icon: "compass" });
 
-                !Doc.UserDoc().noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-                !Doc.UserDoc().noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
+                !Doc.noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
+                !Doc.noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
                 this.props.CollectionFreeFormDocumentView && onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
 
                 if (!this.Document.annotationOn) {
@@ -803,7 +803,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
 
             const funcs: ContextMenuProps[] = [];
-            if (!Doc.UserDoc().noviceMode && this.layoutDoc.onDragStart) {
+            if (!Doc.noviceMode && this.layoutDoc.onDragStart) {
                 funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
                 funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
                 funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
@@ -813,8 +813,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const more = cm.findByDescription("More...");
             const moreItems = more && "subitems" in more ? more.subitems : [];
             if (!Doc.IsSystem(this.rootDoc)) {
-                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.UserDoc().noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
-                if (!Doc.UserDoc().noviceMode) {
+                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
+                if (!Doc.noviceMode) {
                     moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
                     moreItems.push({ description: `${this.Document._chromeHidden ? "Show" : "Hide"} Chrome`, event: () => this.Document._chromeHidden = !this.Document._chromeHidden, icon: "project-diagram" });
 
@@ -835,9 +835,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const help = cm.findByDescription("Help...");
             const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
             helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
+            !Doc.noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+            !Doc.noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+            !Doc.noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
 
@@ -860,7 +860,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive() === false ? false : (
-            CurrentUserUtils.SelectedTool !== InkTool.None ||
+            CurrentUserUtils.ActiveTool !== InkTool.None ||
             SnappingManager.GetIsDragging() ||
             this.rootSelected() ||
             this.props.Document.forceActive ||
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 5add09653..17b57cb3b 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -8,7 +8,7 @@ import { List } from "../../../fields/List";
 import { RichTextField } from "../../../fields/RichTextField";
 import { listSpec } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast, DocCast } from "../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -84,7 +84,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return "data";
     }
     @computed static get targetDocChildren() {
-        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard.data);
+        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard?.data);
     }
 
     @observable _loaded = false;
@@ -115,7 +115,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
         const keys = new Set<string>(noviceFields);
         this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
-        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.UserDoc().noviceMode).sort();
+        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.noviceMode).sort();
     }
 
 
@@ -167,23 +167,25 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeFilterDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false;
     public removeFilter = (filterName: string) => {
         const targetDoc = FilterBox.targetDoc;
-        const filterDoc = targetDoc.currentFilter as Doc;
-        const attributes = DocListCast(filterDoc.data);
-        const found = attributes.findIndex(doc => doc.title === filterName);
-        if (found !== -1) {
-            (filterDoc.data as List<Doc>).splice(found, 1);
-            const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
-            if (docFilter) {
-                let index: number;
-                while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
-                    docFilter.splice(index, 1);
+        if (targetDoc) {
+            const filterDoc = targetDoc.currentFilter as Doc;
+            const attributes = DocListCast(filterDoc.data);
+            const found = attributes.findIndex(doc => doc.title === filterName);
+            if (found !== -1) {
+                (filterDoc.data as List<Doc>).splice(found, 1);
+                const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
+                if (docFilter) {
+                    let index: number;
+                    while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                        docFilter.splice(index, 1);
+                    }
                 }
-            }
-            const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
-            if (docRangeFilters) {
-                let index: number;
-                while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
-                    docRangeFilters.splice(index, 3);
+                const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
+                if (docRangeFilters) {
+                    let index: number;
+                    while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                        docRangeFilters.splice(index, 3);
+                    }
                 }
             }
         }
@@ -194,6 +196,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     facetClick = (facetHeader: string) => {
         const { targetDoc, targetDocChildren } = FilterBox;
+        if (!targetDoc) return;
         const found = this.activeAttributes.findIndex(doc => doc.title === facetHeader);
         if (found !== -1) {
             this.removeFilter(facetHeader);
@@ -267,7 +270,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     changeBool = (e: any) => {
-        (FilterBox.targetDoc.currentFilter as Doc).filterBoolean = e.currentTarget.value;
+        FilterBox.targetDoc && (DocCast(FilterBox.targetDoc.currentFilter).filterBoolean = e.currentTarget.value);
     }
 
     /**
@@ -322,7 +325,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * Changes the title of the filterDoc
      */
     onTitleValueChange = (val: string) => {
-        this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc.title}`;
+        this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc?.title}`;
         return true;
     }
 
@@ -373,7 +376,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             </div>
 
             <div className="filterBox-select-bool">
-                <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc.currentFilter as Doc)?.filterBoolean)}>
+                <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc?.currentFilter as Doc)?.filterBoolean)}>
                     <option value="AND" key="AND">AND</option>
                     <option value="OR" key="OR">OR</option>
                 </select>
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 8c27b3508..60eb48114 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -179,7 +179,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
             funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? "Dynamic Res" : "Full Res"}`, event: this.resolution, icon: "expand-arrows-alt" });
             funcs.push({ description: "Copy path", event: () => Utils.CopyText(this.choosePath(field.url)), icon: "expand-arrows-alt" });
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
 
                 const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers...");
@@ -357,7 +357,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index b0b050cea..b58a9affb 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -52,7 +52,7 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
     get paramsDoc() { return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; }
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        !Doc.UserDoc().noviceMode && funcs.push({
+        !Doc.noviceMode && funcs.push({
             description: "Clear Script Params", event: () => {
                 const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
                 params?.map(p => this.paramsDoc[p] = undefined);
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 5f4c17ee6..0b7264d79 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -471,7 +471,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
@@ -570,7 +570,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     moveDocument={this.moveDocument}
         //     addDocument={this.sidebarAddDocument}
         //     childPointerEvents={true}
-        //     pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
+        //     pointerEvents={CurrentUserUtils.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return <div className="mapBox" ref={this._ref}>
             {/*console.log(apiKey)*/}
             {/* <LoadScript
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b14a1f0f6..ddfbf50df 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -428,7 +428,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
       // renders the video and audio
       @computed get content() {
             const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-            const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
+            const interactive = CurrentUserUtils.ActiveTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
             const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
             return !field ? <div key="loading">Loading</div> :
                   <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply" }}>
@@ -484,7 +484,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                   this._disposers.youtubeReactionDisposer?.();
                   this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
                   this._disposers.youtubeReactionDisposer = reaction(
-                        () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
+                        () => CurrentUserUtils.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
                         (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
             };
             if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
@@ -707,7 +707,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
       // starts marquee selection
       marqueeDown = (e: React.PointerEvent) => {
-            if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+            if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.ActiveTool)) {
                   setupMoveUpEvents(this, e, action(e => {
                         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                         this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 967158cbf..d14af49ea 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -536,7 +536,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
         if (!cm.findByDescription("Options...")) {
-            !Doc.UserDoc().noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
+            !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
             funcs.push({
                 description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => {
                     this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts;
@@ -561,7 +561,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
@@ -678,7 +678,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
 
     @computed get content() {
-        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
+        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
         return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
             onKeyDown={e => e.stopPropagation()}
             style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index f29dfe489..3af6a3d51 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 { StringIterator } from 'lodash';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -70,7 +71,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); };
 
     specificContextMenu = (): void => {
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             const cm = ContextMenu.Instance;
             cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" });
             cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" });
@@ -269,7 +270,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         // Get items to place into the list
         const list = this.buttonList.map((value) => {
-            if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
+            if (Doc.noviceMode && !noviceList.includes(value)) {
                 return;
             }
             return <div className="list-item" key={`${value}`}
@@ -567,8 +568,8 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    if (checkResult && selected) {
-        if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
+    if (checkResult) {
+        if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE;
         return "transparent";
     }
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
@@ -710,7 +711,7 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 
 export function checkInksToGroup() {
     // console.log("getting here to inks group");
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
@@ -723,7 +724,7 @@ export function checkInksToGroup() {
 
 export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
@@ -785,38 +786,38 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
 
 
 /** INK
- *      setActiveInkTool
+ *      setActiveTool
  *      setStrokeWidth
  *      setStrokeColor
  **/
 
-ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) {
     InkTranscription.Instance?.createInkGroup();
     if (checkResult) {
-        return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
+        return ((CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
             Colors.MEDIUM_BLUE : "transparent";
     }
     if (["circle", "square", "line"].includes(tool)) {
         if (GestureOverlay.Instance.InkShape === tool) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             GestureOverlay.Instance.InkShape = InkTool.None;
         } else {
-            Doc.UserDoc().activeInkTool = InkTool.Pen;
+            CurrentUserUtils.ActiveTool = InkTool.Pen;
             GestureOverlay.Instance.InkShape = tool;
         }
     } else if (tool) { // pen or eraser
-        if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
-        } else if (tool == "write") {
+        if (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance.InkShape) {
+            CurrentUserUtils.ActiveTool = InkTool.None;
+        } else if (tool == InkTool.Write) {
             // console.log("write mode selected - create groupDoc here!", tool)
-            Doc.UserDoc().activeInkTool = tool;
+            CurrentUserUtils.ActiveTool = tool;
             GestureOverlay.Instance.InkShape = "";
         } else {
-            Doc.UserDoc().activeInkTool = tool;
+            CurrentUserUtils.ActiveTool = tool as any;
             GestureOverlay.Instance.InkShape = "";
         }
     } else {
-        Doc.UserDoc().activeInkTool = InkTool.None;
+        CurrentUserUtils.ActiveTool = InkTool.None;
     }
 });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 16a523b40..90199618b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -648,7 +648,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const highlighting: ContextMenuProps[] = [];
         const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others", "Bold Text"];
         const expertHighlighting = [...noviceHighlighting, "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"];
-        (Doc.UserDoc().noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
+        (Doc.noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
             highlighting.push({
                 description: (FormattedTextBox._globalHighlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
                     e.stopPropagation();
@@ -663,11 +663,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }));
 
         const uicontrols: ContextMenuProps[] = [];
-        !Doc.UserDoc().noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" });
+        !Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" });
         uicontrols.push({ description: !this.Document._noSidebar ? "Hide Sidebar Handle" : "Show Sidebar Handle", event: () => this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar, icon: "expand-arrows-alt" });
         uicontrols.push({ description: `${this.layoutDoc._showAudio ? "Hide" : "Show"} Dictation Icon`, event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
         uicontrols.push({ description: "Show Highlights...", noexpand: true, subitems: highlighting, icon: "hand-point-right" });
-        !Doc.UserDoc().noviceMode && uicontrols.push({
+        !Doc.noviceMode && uicontrols.push({
             description: "Broadcast Message", event: () => DocServer.GetRefField("rtfProto").then(proto =>
                 proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), icon: "expand-arrows-alt"
         });
@@ -677,7 +677,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
         // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
-        !Doc.UserDoc().noviceMode && appearanceItems.push({
+        !Doc.noviceMode && appearanceItems.push({
             description: "Make Default Layout", event: () => {
                 if (!this.layoutDoc.isTemplateDoc) {
                     const title = StrCast(this.rootDoc.title);
@@ -1636,7 +1636,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     noSidebar={true}
                     fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
-        return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.SelectedTool !== InkTool.None ? "-inking" : "")}
+        return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.ActiveTool !== InkTool.None ? "-inking" : "")}
             style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
             {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
         </div>;
@@ -1647,7 +1647,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const active = this.props.isContentActive();
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
-        const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
+        const interactive = (CurrentUserUtils.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9bc2e5628..98343a261 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -67,7 +67,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         runInAction(() => {
             RichTextMenu.Instance = this;
             this._canFade = false;
-            //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
             this.Pinned = true;
         });
     }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 52b03b0a5..0d2cffc2c 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -134,7 +134,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
     constructor(props: any) {
         super(props);
-        if (Doc.UserDoc().activePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
+        if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
         if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
             Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
                 title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
@@ -178,16 +178,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.layoutDoc._gridGap = 0;
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
-        DocListCastAsync((Doc.UserDoc().myTrails as Doc).data).then(pres =>
-            !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myTrails as Doc, "data", this.rootDoc));
+        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres =>
+            !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", this.rootDoc));
         this._disposers.selection = reaction(() => SelectionManager.Views(),
             views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation());
     }
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
-        if (pres) Doc.UserDoc().activePresentation = pres;
-        else Doc.UserDoc().activePresentation = this.rootDoc;
+        if (pres) CurrentUserUtils.ActivePresentation = pres;
+        else CurrentUserUtils.ActivePresentation = this.rootDoc;
         document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
         document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = true;
@@ -623,9 +623,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     updateMinimize = async () => {
-        if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
-            Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+            Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, "right");
         } else {
             this.layoutDoc.presStatus = PresStatus.Edit;
@@ -635,7 +635,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.rootDoc.y = pt[1] + 10;
             this.rootDoc._height = 30;
             this.rootDoc._width = 248;
-            Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+            Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
             this.props.removeDocument?.(this.layoutDoc);
         }
     }
@@ -732,7 +732,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
+    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
         (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     /**
@@ -868,7 +868,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
                 break;
             case "Escape":
-                if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
+                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) { this.updateMinimize(); }
                 else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; }
                 else this.layoutDoc.presStatus = "edit";
                 if (this._presTimer) clearTimeout(this._presTimer);
@@ -2247,7 +2247,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel";
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
+        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
         const activeColor = Colors.LIGHT_BLUE;
         const inactiveColor = Colors.WHITE;
         return (mode === CollectionViewType.Carousel3D) ? (null) : (
@@ -2305,7 +2305,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     value={mode}>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>Tree</option>
-                    {Doc.UserDoc().noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
+                    {Doc.noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
                 </select>}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
                     <span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}>
@@ -2508,10 +2508,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
+        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
         const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
         const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
-        return CurrentUserUtils.MyOverlayDocs.includes(this.rootDoc) ?
+        return DocListCast(CurrentUserUtils.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></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
@@ -2536,7 +2536,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div >
             :
-            <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
+            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }} >
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 50df00612..1a2f4b93f 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -309,7 +309,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
       @computed get recordingIsInOverlay() {
             let isInOverlay = false
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides === this.rootDoc) {
                         isInOverlay = true
                         return
@@ -319,9 +319,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
       }
 
       removeAllRecordingInOverlay = () => {
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides === this.rootDoc) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                        Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
                   }
             })
       }
@@ -339,7 +339,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.removeAllRecordingInOverlay()
             if (activeItem.recording) {
                   // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
 
             }
       }
@@ -348,15 +348,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
       @action
       startRecording = (activeItem: Doc) => {
             // Remove every recording that already exists in overlay view
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides !== null) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                        Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
                   }
             })
 
             if (activeItem.recording) {
                   // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
 
             } else {
                   // if we dont have any recording
@@ -376,7 +376,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                   // make recording box appear in the bottom right corner of the screen
                   recording.x = window.innerWidth - recording[WidthSym]() - 20;
                   recording.y = window.innerHeight - recording[HeightSym]() - 20;
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording);
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, recording);
             }
       }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0ded1bb3c..2869d4f2d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -340,7 +340,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
             this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
         }
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             this.props.select(false);
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0db3950a2..1de0c0b10 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -27,12 +27,12 @@ import "./TopBar.scss";
 export class TopBar extends React.Component {
       navigateToHome = () => {
             CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => {
-                Doc.UserDoc().activePage = "home";
+                CurrentUserUtils.ActivePage = "home";
                 CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
             });
       }
       render() {
-            const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null)
+            const activeDashboard = CurrentUserUtils.ActiveDashboard;
             return (
                   //TODO:glr Add support for light / dark mode
                   <div style={{ pointerEvents: "all", background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
@@ -44,11 +44,11 @@ export class TopBar extends React.Component {
                                     }}>{Doc.CurrentUserEmail}</div> : (null)}
                               </div>
                               <div className="topbar-center" >
-                                    <div className="topbar-title" onClick={() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
+                                    <div className="topbar-title" onClick={() => activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
                                           {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
                                     </div>
                                     <div className="topbar-button-icon" onClick={e => {
-                                        const dashView = DocumentManager.Instance.getDocumentView(activeDashboard);
+                                        const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard);
                                         ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
                                         ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
                                             const batch = UndoManager.StartBatch("snapshot");
@@ -69,7 +69,7 @@ export class TopBar extends React.Component {
                                     <div className="topbar-button-text" onClick={() => {SharingManager.Instance.open(undefined, activeDashboard)}}>
                                         {/* TODO: if this is my dashboard, display share
                                         if this is a shared dashboard, display "view original or view annotated" */}
-                                        { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original" }
+                                        { CurrentUserUtils.ActiveDashboard && (Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original") }
                                     </div>
                                     <div className="topbar-button-icon" onClick={() => window.open(
                                           "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index e0d328c89..6c0c9b301 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -71,7 +71,7 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
             </div >;
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
 
         return (
             <>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 94be286f5..74213652b 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -21,7 +21,7 @@ import { FieldId, RefField } from "./RefField";
 import { RichTextField } from "./RichTextField";
 import { listSpec } from "./Schema";
 import { ComputedField, ScriptField } from "./ScriptField";
-import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField";
 import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util";
 import JSZip = require("jszip");
@@ -233,6 +233,10 @@ export class Doc extends RefField {
     }
 
     private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
+    public static get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode); }
+    public static set noviceMode(val) { Doc.UserDoc().noviceMode = val; }
+    public static get defaultAclPrivate() { return Doc.UserDoc().defaultAclPrivate; }
+    public static set defaultAclPrivate(val) { Doc.UserDoc().defaultAclPrivate = val; }
     public static CurrentUserEmail: string = "";
     public static get CurrentUserEmailNormalized() { return normalizeEmail(Doc.CurrentUserEmail); }
     public async [HandleUpdate](diff: any) {
@@ -805,7 +809,7 @@ export namespace Doc {
             Doc.AddDocToList(Doc.GetProto(copy)[DataSym], "aliases", copy);
         }
         copy.context = undefined;
-        Doc.UserDoc().defaultAclPrivate && (copy["acl-Public"] = "Not Shared");
+        Doc.defaultAclPrivate && (copy["acl-Public"] = "Not Shared");
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
         }
@@ -861,7 +865,7 @@ export namespace Doc {
             const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
             target.layoutKey = targetKey;
             applied && (Doc.GetProto(applied).type = templateDoc.type);
-            Doc.UserDoc().defaultAclPrivate && (applied["acl-Public"] = "Not Shared");
+            Doc.defaultAclPrivate && (applied["acl-Public"] = "Not Shared");
             return applied;
         }
         return undefined;
@@ -1234,19 +1238,19 @@ export namespace Doc {
 
     export function isDocPinned(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        const curPres = CurrentUserUtils.ActivePresentation;
         return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
     }
 
     export function copyDragFactory(dragFactory: Doc) {
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
-        ndoc && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(ndoc));
+        ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc));
         if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
             dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
             Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
         }
 
-        if (ndoc) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc);
+        if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc);
 
         return ndoc;
     }
@@ -1448,7 +1452,7 @@ ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a docum
 ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
 ScriptingGlobals.add(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
 ScriptingGlobals.add(function activePresentationItem() {
-    const curPres = Doc.UserDoc().activePresentation as Doc;
+    const curPres = CurrentUserUtils.ActivePresentation;
     return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
 });
 ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 37b9be31b..8fb35981b 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -145,7 +145,7 @@ export function inheritParentAcls(parent: Doc, child: Doc) {
     const dataDoc = parent[DataSym];
     for (const key of Object.keys(dataDoc)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        const permission = (key === "acl-Public" && Doc.UserDoc().defaultAclPrivate) ? AclPrivate : dataDoc[key];
+        const permission = (key === "acl-Public" && Doc.defaultAclPrivate) ? AclPrivate : dataDoc[key];
         key.startsWith("acl") && distributeAcls(key, permission, child);
     }
 }
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index c5c6fb688..fe8100997 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -51,7 +51,7 @@ library.add(...[faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, fa
 @observer
 export class MobileInterface extends React.Component {
     static Instance: MobileInterface;
-    private _library: Promise<Doc>;
+    private _library: Doc;
     private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc());
     @observable private _sidebarActive: boolean = false; //to toggle sidebar display
     @observable private _imageUploadActive: boolean = false; //to toggle image upload
@@ -68,7 +68,7 @@ export class MobileInterface extends React.Component {
 
     constructor(props: Readonly<{}>) {
         super(props);
-        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc()); // to access documents in Dash Web
+        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), "myDashboards"); // to access documents in Dash Web
         MobileInterface.Instance = this;
     }
 
@@ -76,7 +76,7 @@ export class MobileInterface extends React.Component {
     componentDidMount = () => {
         // if the home menu is in list view -> adjust the menu toggle appropriately
         this._menuListView = this._homeDoc._viewType === "stacking" ? true : false;
-        CurrentUserUtils.SelectedTool = InkTool.None; // ink should intially be set to none
+        CurrentUserUtils.ActiveTool = InkTool.None; // ink should intially be set to none
         Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
         AudioBox.Enabled = true;
 
@@ -125,7 +125,7 @@ export class MobileInterface extends React.Component {
      * Method called when 'Library' button is pressed on the home screen
      */
     switchToLibrary = async () => {
-        this._library.then(library => this.switchCurrentView(library));
+        this.switchCurrentView(this._library);
         runInAction(() => this._homeMenu = false);
         this.toggleSidebar();
     }
@@ -140,7 +140,7 @@ export class MobileInterface extends React.Component {
         // Case 1: Parent document is 'dashboards'
         if (doc === Cast(this._library, Doc) as Doc) {
             this.dashboards = null;
-            this._library.then(library => this.switchCurrentView(library));
+            this.switchCurrentView(this._library);
             // Case 2: Parent document is the 'home' menu (root node)
         } else if (doc === Cast(this._homeDoc, Doc) as Doc) {
             this._homeMenu = true;
@@ -179,7 +179,7 @@ export class MobileInterface extends React.Component {
     @action
     returnMain = () => {
         this._parents = [this._homeDoc];
-        this._library.then(library => this.switchCurrentView(library));
+        this.switchCurrentView(this._library);
         this._homeMenu = false;
         this.dashboards = null;
     }
@@ -419,10 +419,10 @@ export class MobileInterface extends React.Component {
         button.style.color = this._ink ? "black" : "white";
 
         if (!this._ink) {
-            CurrentUserUtils.SelectedTool = InkTool.Pen;
+            CurrentUserUtils.ActiveTool = InkTool.Pen;
             this._ink = true;
         } else {
-            CurrentUserUtils.SelectedTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             this._ink = false;
         }
     }
@@ -514,7 +514,7 @@ export class MobileInterface extends React.Component {
             return <div className="docButton"
                 title={Doc.isDocPinned(this._activeDoc) ? "Unpin from presentation" : "Pin to presentation"}
                 style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }}
-                onClick={e => TabDocView.PinDoc(this._activeDoc, { unpin: isPinned })}>
+                onClick={e => TabDocView.PinDoc(this._activeDoc)}>
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>;
         } else return (null);
@@ -573,7 +573,7 @@ export class MobileInterface extends React.Component {
     // For setting up the presentation document for the home menu
     @action
     setupDefaultPresentation = () => {
-        const presentation = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        const presentation = CurrentUserUtils.ActivePresentation;
 
         if (presentation) {
             this.switchCurrentView(presentation);
-- 
cgit v1.2.3-70-g09d2


From 3e22a44461095d95062e3b809b525a0a87342ac3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 22:18:42 -0400
Subject: more cleanup to get rid of unecessary references to Doc.UserDoc().

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/CurrentUserUtils.ts                | 48 +++++++++++++---------
 src/client/util/DragManager.ts                     |  4 +-
 src/client/util/History.ts                         |  2 +-
 src/client/util/SettingsManager.tsx                | 19 ++++-----
 src/client/util/SnappingManager.ts                 |  4 ++
 src/client/views/DashboardView.tsx                 |  2 +-
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/MainView.tsx                      | 21 ++++------
 .../views/collections/CollectionDockingView.tsx    |  4 +-
 src/client/views/collections/CollectionMenu.tsx    |  4 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  4 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 30 +++++++-------
 .../nodes/button/colorDropdown/ColorDropdown.tsx   |  3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +-
 src/client/views/nodes/trails/PresBox.tsx          | 11 +----
 src/client/views/topbar/TopBar.tsx                 |  2 +-
 src/mobile/MobileInterface.tsx                     |  2 +-
 22 files changed, 93 insertions(+), 89 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e36103938..73b756a8d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -662,7 +662,7 @@ export namespace Docs {
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_");
 
             dataProps["acl-Override"] = "None";
-            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps.system = viewProps.system;
             dataProps.isPrototype = true;
@@ -679,7 +679,7 @@ export namespace Docs {
             const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
 
             const viewFirstProps: { [id: string]: any } = {};
-            viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             viewFirstProps["acl-Override"] = "None";
             viewFirstProps.author = Doc.CurrentUserEmail;
             const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1d5a9fa09..9b5fc5b98 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -181,7 +181,12 @@ export class CurrentUserUtils {
 
     /// Initializes collection of templates for notes and click functions
     static setupDocTemplates(doc: Doc, field="myTemplates") {
+        const preEleOpts:DocumentOptions = {
+            title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
+        };
+        this.AssignOpts(DocCast(doc.presElement), preEleOpts) ?? (doc.presElement= Docs.Create.PresElementBoxDocument(preEleOpts));
         const templates = [
+            DocCast(doc.presElement),
             CurrentUserUtils.setupNoteTemplates(doc),
             CurrentUserUtils.setupClickEditorTemplates(doc)
         ];
@@ -323,8 +328,7 @@ export class CurrentUserUtils {
             { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
             { toolTip: "Tap or drag to create a mobile view",     title: "Phone", icon: "mobile",       dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)',     onDragStart: 'this.dragFactory'},                             funcs: {hidden: 'IsNoviceMode()'} },
             { 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 presentation",    title: "Trails", icon: "pres-trail",  dragFactory: doc.emptyPresentation as Doc,scripts: {onClick: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', onDragStart: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`}, funcs: {hidden: 'IsNoviceMode()'} },
-             ];
+          ];
     }
 
     /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
@@ -533,7 +537,7 @@ export class CurrentUserUtils {
     static setupDashboards(doc: Doc, field:string) {
         var myDashboards = DocCast(doc[field]);
 
-        const newDashboard = `createNewDashboard(Doc.UserDoc())`;
+        const newDashboard = `createNewDashboard()`;
         const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
              title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
         const reqdBtnScript = {onClick: newDashboard,}
@@ -830,6 +834,11 @@ export class CurrentUserUtils {
         const reqdOpts = { title: "overlay documents", backgroundColor: "#aca3a6", system: true };
         return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.FreeformDocument([], reqdOpts));
     }
+
+    static setupPublished(doc:Doc, field = "myPublishedDocs") {
+        const reqdOpts = { title: "published docs", backgroundColor: "#aca3a6", system: true };
+        return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+    }
     
     /// The database of all links on all documents
     static async setupLinkDocs(doc: Doc, linkDatabaseId: string) {
@@ -958,7 +967,6 @@ export class CurrentUserUtils {
         Doc.noviceMode ?? (Doc.noviceMode = true);
         doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true);
         doc._showLabel ?? (doc._showLabel = true);
-        doc._showMenuLabel ?? (doc._showMenuLabel = true);
         doc.textAlign ?? (doc.textAlign = "left");
         doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");;
         doc.activeInkWidth ?? (doc.activeInkWidth = 1);
@@ -975,17 +983,17 @@ export class CurrentUserUtils {
         doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myPublishedDocs ?? (doc.myPublishedDocs = new List<Doc>());
         doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
         await this.setupLinkDocs(doc, linkDatabaseId);
         await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
-        this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard
+        this.setupPublished(doc);  // sets up the list doc of all docs that have been published (meaning that they can be auto-linked by typing their title into another text box)
         this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
+        this.setupDocTemplates(doc); // sets up the template menu of templates
         doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument());
 
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
@@ -1037,7 +1045,8 @@ export class CurrentUserUtils {
 
     public static _urlState: HistoryUtil.DocUrl;
 
-    public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => {
+    public static openDashboard = (doc: Doc, fromHistory = false) => {
+        const userDoc =  Doc.UserDoc();
         CurrentUserUtils.MainDocId = doc[Id];
         if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) {
             Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc);
@@ -1045,7 +1054,7 @@ export class CurrentUserUtils {
 
         if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
             !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
-            userDoc ? (userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
+            userDoc ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
         }
         const state = CurrentUserUtils._urlState;
         if (state.sharing === true && !userDoc) {
@@ -1138,11 +1147,11 @@ export class CurrentUserUtils {
 
     }
 
-    public static async snapshotDashboard(userDoc: Doc) {
+    public static async snapshotDashboard() {
         if (CurrentUserUtils.ActiveDashboard) {
             const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
             Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
-            CurrentUserUtils.openDashboard(userDoc, copy);
+            CurrentUserUtils.openDashboard(copy);
         }
     }
 
@@ -1150,9 +1159,9 @@ export class CurrentUserUtils {
         CurrentUserUtils.ActiveDashboard = undefined;
     }
 
-    public static createNewDashboard = async (userDoc: Doc, id?: string) => {
-        const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
-        const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
+    public static createNewDashboard = (id?: string) => {
+        const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
+        const dashboards = CurrentUserUtils.MyDashboards;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
@@ -1172,10 +1181,10 @@ export class CurrentUserUtils {
         dashboardDoc.data = new List<Doc>(dashboardTabs);
         dashboardDoc["pane-count"] = 1;
 
-        userDoc.activePresentation = presentation;
+        CurrentUserUtils.ActivePresentation = presentation;
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
-      //   CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
+      //   CurrentUserUtils.openDashboard(dashboardDoc);
     }
 
     public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
@@ -1209,6 +1218,7 @@ export class CurrentUserUtils {
     public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
     public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
     public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); }
+    public static get MyPublishedDocs() { return DocCast(Doc.UserDoc().myPublishedDocs); }
     public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
     public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; }
     public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
@@ -1230,8 +1240,8 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
 ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
 ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
-ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called");
+ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
+ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(); }, "creates a new dashboard when called");
 ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)");
@@ -1240,7 +1250,7 @@ ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.In
 ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
     const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
     if (dashboards && dashboards.length > 1) {
-        if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboards.find(doc => doc !== dashboard)!);
+        if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!);
         Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard);
     }
 },
@@ -1248,7 +1258,7 @@ ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
 ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
     const dashboardAlias = Doc.MakeAlias(dashboard);
     Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias);
-    CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias);
+    CurrentUserUtils.openDashboard(dashboardAlias);
 },
     "adds Dashboard to set of Dashboards");
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index dbfba8992..2b62945af 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -6,7 +6,7 @@ import { PrefetchProxy } from "../../fields/Proxy";
 import { listSpec } from "../../fields/Schema";
 import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { ScriptField } from "../../fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
+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";
@@ -72,6 +72,8 @@ export namespace DragManager {
     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 Root() {
         const root = document.getElementById("root");
         if (!root) {
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 632348306..7dcff9c56 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -197,7 +197,7 @@ export namespace HistoryUtil {
             await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
         }
         if (field instanceof Doc) {
-            CurrentUserUtils.openDashboard(Doc.UserDoc(), field, true);
+            CurrentUserUtils.openDashboard(field, true);
         }
     }
 
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 382274462..22e33ab1e 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -10,7 +10,9 @@ import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager
 import { DocServer } from "../DocServer";
 import { Networking } from "../Network";
 import { MainViewModal } from "../views/MainViewModal";
+import { FontIconBox } from "../views/nodes/button/FontIconBox";
 import { CurrentUserUtils } from "./CurrentUserUtils";
+import { DragManager } from "./DragManager";
 import { GroupManager } from "./GroupManager";
 import "./SettingsManager.scss";
 import { undoBatch } from "./UndoManager";
@@ -155,19 +157,14 @@ export class SettingsManager extends React.Component<{}> {
                 <div className="preferences-check">Show full toolbar</div>
             </div>
             <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._raiseWhenDragged = !Doc.UserDoc()._raiseWhenDragged}
-                    checked={BoolCast(Doc.UserDoc()._raiseWhenDragged)} />
+                <input type="checkbox" onChange={e => DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged())}
+                    checked={DragManager.GetRaiseWhenDragged()} />
                 <div className="preferences-check">Raise on drag</div>
             </div>
             <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._showLabel = !Doc.UserDoc()._showLabel}
-                    checked={BoolCast(Doc.UserDoc()._showLabel)} />
-                <div className="preferences-check">Show tool button labels</div>
-            </div>
-            <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._showMenuLabel = !Doc.UserDoc()._showMenuLabel}
-                    checked={BoolCast(Doc.UserDoc()._showMenuLabel)} />
-                <div className="preferences-check">Show menu button labels</div>
+                <input type="checkbox" onChange={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())}
+                    checked={FontIconBox.GetShowLabels()} />
+                <div className="preferences-check">Show button labels</div>
             </div>
         </div>;
     }
@@ -271,7 +268,7 @@ export class SettingsManager extends React.Component<{}> {
                 <div className="tab-column-content">
                     <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
                     <div className="default-acl">
-                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)}
+                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.defaultAclPrivate)}
                             onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
                         <div className="acl-text">Default access private</div>
                     </div>
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 069f81d38..057843c68 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -1,5 +1,6 @@
 import { observable, action, runInAction } from "mobx";
 import { computedFn } from "mobx-utils";
+import { Doc } from "../../fields/Doc";
 
 export namespace SnappingManager {
 
@@ -30,6 +31,9 @@ export namespace SnappingManager {
     export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
     export function GetIsDragging() { return manager.IsDragging; }
 
+    export function SetShowSnapLines(show: boolean) { runInAction(() => Doc.UserDoc().showSnapLines = show); }
+    export function GetShowSnapLines() { return Doc.UserDoc().showSnapLines; }
+
     /// bcz; argh!! TODO;   These do not belong here, but there were include order problems with leaving them in util.ts
     // need to investigate further what caused the mobx update problems and move to a better location.
     const getCachedGroupByNameCache = computedFn(function (name: string) { return manager.cachedGroups.includes(name); }, true);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 9a0f25fe3..a126218c4 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -28,7 +28,7 @@ export class DashboardView extends React.Component {
 
       newDashboard = async () => {
             const batch = UndoManager.StartBatch("new dash");
-            await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+            await CurrentUserUtils.createNewDashboard();
             batch.end();
       }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 18cf785b9..4247501bb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -87,10 +87,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                         if (titleFieldKey === "title") {
                               d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
                               if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
-                                    Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                                    Doc.RemoveDocFromList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
                               }
                               if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
-                                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                                    Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
                               }
                         }
                         //@ts-ignore
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 61cca0421..6ee8065f5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -225,26 +225,21 @@ export class MainView extends React.Component {
         // Load the user's active dashboard, or create a new one if initial session after signup
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
-            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
+            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(), { fireImmediately: true });
         } else {
             PromiseValue(this.userDoc.activeDashboard).then(dash => {
-                if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
-                else CurrentUserUtils.createNewDashboard(this.userDoc);
+                if (dash instanceof Doc) CurrentUserUtils.openDashboard(dash);
+                else CurrentUserUtils.createNewDashboard();
             });
         }
     }
 
     @action
     createNewPresentation = async () => {
-        if (!await this.userDoc.myTrails) {
-            this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
-            }));
-        }
         const pres = Docs.Create.PresDocument({ title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "left");
-        this.userDoc.activePresentation = pres;
-        Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
+        CurrentUserUtils.ActivePresentation = pres;
+        Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", pres);
     }
 
     @action
@@ -353,9 +348,9 @@ export class MainView extends React.Component {
     addDocTabFunc = (doc: Doc, location: string): boolean => {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
         const locationParams = locationFields.length > 1 ? locationFields[1] : "";
-        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(doc);
         switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            case "dashboard": return CurrentUserUtils.openDashboard(doc);
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "lightbox": return LightboxView.AddDocTab(doc, location);
@@ -563,7 +558,7 @@ export class MainView extends React.Component {
             </div>;
     }
     @computed get snapLines() {
-        return !this.userDoc.showSnapLines ? (null) : <div className="mainView-snapLines">
+        return !SnappingManager.GetShowSnapLines() ? (null) : <div className="mainView-snapLines">
             <svg style={{ width: "100%", height: "100%" }}>
                 {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
                 {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 5f36a7a51..19355b8e1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -121,7 +121,7 @@ export class CollectionDockingView extends CollectionSubView() {
         SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
-            return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            return CurrentUserUtils.openDashboard(doc);
         }
         const newItemStackConfig = {
             type: 'stack',
@@ -172,7 +172,7 @@ export class CollectionDockingView extends CollectionSubView() {
     @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
-        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
+        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(document);
 
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
         if (tab) {
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 9b1bb5b97..668d82387 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -164,7 +164,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
         //     </button>
         // </Tooltip>;
 
-        // OLD BUTTONS
+        // //OLD BUTTONS
         // return this.getElement(!this.SelectedCollection ? [/*button*/] :
         //     [<CollectionViewBaseChrome key="chrome"
         //         docView={this.SelectedCollection}
@@ -306,7 +306,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
 
     @undoBatch
     viewChanged = (e: React.ChangeEvent) => {
-        const target = this.document !== Doc.UserDoc().sidebar ? this.document : this.document.proto as Doc;
+        const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc;
         //@ts-ignore
         target._viewType = e.target.selectedOptions[0].value;
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4b5c5e3fb..63616263e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -205,7 +205,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 });
             }
             if (this.Document._viewType === CollectionViewType.Docking) {
-                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "project-diagram" });
+                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(), icon: "project-diagram" });
             }
 
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 70db121d1..62d07b0e4 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -329,7 +329,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
         const locationParams = locationFields.length > 1 ? locationFields[1] : "";
         switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            case "dashboard": return CurrentUserUtils.openDashboard(doc);
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ffe146ae4..99eec7892 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -253,7 +253,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged(): d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -1645,7 +1645,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-        !Doc.noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? "Hide" : "Show") + " Snap Lines", event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: "compress-arrows-alt" }) : null;
         !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b62020a04..081a1a924 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -6,7 +6,7 @@ import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { RichTextField } from "../../../../fields/RichTextField";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { Cast, DocCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
 import { GetEffectiveAcl } from "../../../../fields/util";
 import { intersectRect, returnFalse, Utils } from "../../../../Utils";
@@ -156,7 +156,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                     }));
             } else if (e.key === "s" && e.ctrlKey) {
                 e.preventDefault();
-                const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!;
+                const slide = Doc.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
                 slide.x = x;
                 slide.y = y;
                 FormattedTextBox.SelectOnLoad = slide[Id];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4d84a8ad2..89096c948 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -484,7 +484,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             let stopPropagate = true;
             let preventDefault = true;
             const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
-            (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+            (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
             if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (this._timeout) {
                     clearTimeout(this._timeout);
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 3af6a3d51..1b37e729b 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -79,6 +79,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         }
     }
 
+    static GetShowLabels() { return BoolCast(Doc.UserDoc()._showLabel); }
+    static SetShowLabels(show:boolean) { Doc.UserDoc()._showLabel = show; }
+
     // Determining UI Specs
     @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
     @observable private icon = StrCast(this.dataDoc.icon, "user") as any;
@@ -111,7 +114,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         // Script for checking the outcome of the toggle
         const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
 
-        const label = !Doc.UserDoc()._showLabel ? (null) :
+        const label = !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label">
                 {this.label}
             </div>;
@@ -212,7 +215,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                {!this.label || !FontIconBox.GetShowLabels() ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 <div
                     className="menuButton-dropdown"
                     style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
@@ -283,7 +286,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             </div>;
         });
 
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ bottom: 0, position: "absolute", color: color, backgroundColor: backgroundColor }}>
                 {this.label}
             </div>;
@@ -337,7 +340,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
 
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
@@ -349,7 +352,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         </div>;
         setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
         return (
-            <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")} ${this.colorPickerClosed}`}
+            <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")} ${this.colorPickerClosed}`}
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)}
                 onPointerDown={e => e.stopPropagation()}>
@@ -381,7 +384,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
         // Button label
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
@@ -400,7 +403,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             );
         } else {
             return (
-                <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`}
+                <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`}
                     style={{ opacity: 1, backgroundColor, color }}>
                     <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                     {label}
@@ -423,7 +426,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
                 <div className="menuButton-wrap">
                     <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
-                    {!this.label || !Doc.UserDoc()._showLabel ? (null) :
+                    {!this.label || !FontIconBox.GetShowLabels() ? (null) :
                         <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 </div>
             </div>
@@ -450,12 +453,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     render() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
 
-        const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
+        const menuLabel = !this.label || !FontIconBox.GetShowLabels()  ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
                 {this.label}
             </div>;
@@ -497,7 +500,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
             case ButtonType.ToolButton:
                 button = (
-                    <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`} style={{ opacity: 1, backgroundColor, color }}>
+                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} style={{ opacity: 1, backgroundColor, color }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -509,7 +512,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
             case ButtonType.ClickButton:
                 button = (
-                    <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`} style={{ color, backgroundColor, opacity: 1 }}>
+                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} style={{ color, backgroundColor, opacity: 1 }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -675,10 +678,9 @@ ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: b
 ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.noAutoLink : Doc.UserDoc().noAutoLink) ? Colors.MEDIUM_BLUE : "transparent";
+        return (editorView ? RichTextMenu.Instance.noAutoLink : false) ? Colors.MEDIUM_BLUE : "transparent";
     }
     if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor();
-    else Doc.UserDoc().noAutoLink = Doc.UserDoc().noAutoLink ? true : false;
 });
 
 ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
index 235495250..7f414ddbb 100644
--- a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
+++ b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
@@ -5,6 +5,7 @@ import { IButtonProps } from '../ButtonInterface';
 import { ColorState, SketchPicker } from 'react-color';
 import { ScriptField } from '../../../../../fields/ScriptField';
 import { Doc } from '../../../../../fields/Doc';
+import { FontIconBox } from '../FontIconBox';
 
 export class ColorDropdown extends Component<IButtonProps> {
     render() {
@@ -31,7 +32,7 @@ export class ColorDropdown extends Component<IButtonProps> {
             disableAlpha={!stroke}
             onChange={func} color={boolResult ? boolResult : "#FFFFFF"}
             presetColors={colorOptions} />;
-        const label = !this.props.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.props.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color: this.props.color, backgroundColor: this.props.backgroundColor, position: "absolute" }}>
                 {this.props.label}
             </div>;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 90199618b..600730d87 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -355,7 +355,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             var tr = this._editorView.state.tr as any;
             const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
             tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
-            DocListCast(Doc.UserDoc().myPublishedDocs).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks));
+            DocListCast(CurrentUserUtils.MyPublishedDocs.data).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks));
             tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
             oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
@@ -376,7 +376,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             if (!(cfield instanceof ComputedField)) {
                 this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : "");
                 if (str.startsWith("@") && str.length > 1) {
-                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc);
+                    Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, this.rootDoc);
                 }
             }
         }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 0d2cffc2c..9f858539f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -10,7 +10,7 @@ import { InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { PrefetchProxy } from "../../../../fields/Proxy";
 import { listSpec } from "../../../../fields/Schema";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
@@ -131,17 +131,10 @@ 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;
     }
-    @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
     constructor(props: any) {
         super(props);
         if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
-        if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
-            Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
-                title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
-            }));
-        }
         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];
@@ -728,7 +721,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     }
-    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : this.presElement;
+    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement);
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 1de0c0b10..6a4deca38 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -52,7 +52,7 @@ export class TopBar extends React.Component {
                                         ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
                                         ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
                                             const batch = UndoManager.StartBatch("snapshot");
-                                            await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
+                                            await CurrentUserUtils.snapshotDashboard();
                                             batch.end();
                                         }, icon: "edit" });
                                         dashView?.showContextMenu(e.clientX+20, e.clientY+30);
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index fe8100997..bf06faeb9 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -391,7 +391,7 @@ export class MobileInterface extends React.Component {
      * Handles the 'Create New Dashboard' button in the menu (taken from MainView.tsx)
      */
     @action
-    createNewDashboard = async (id?: string) => {
+    createNewDashboard = (id?: string) => {
         const scens = CurrentUserUtils.MyDashboards;
         const dashboardCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
-- 
cgit v1.2.3-70-g09d2

