From 1536f8d6e5f44fd14954550e9bd670561727b417 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 28 Aug 2020 15:45:07 -0400
Subject: fixed undo for collection tabs. fixed treeview to support
 freezeChildren to prevent children from being removed from the tree.  Made
 _isBackground a layoutfield.  fixed formattedTextBox exceptions on undo from
 a tab.

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

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 7e233ecbb..a24761195 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -507,7 +507,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                     </div>
                     <div className="searchBox-lozenge-dashboard"  >
                         <select className="searchBox-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])}
-                            value={myDashboards.indexOf(Cast(Doc.UserDoc().activeDashboard, Doc, null)!)}>
+                            value={myDashboards.indexOf(Cast(Doc.UserDoc().activeDashboard, Doc, null))}>
                             {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
                         </select>
                         <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}>
-- 
cgit v1.2.3-70-g09d2


From 7b8924d203c6367edbf7e6ffbe2e8f2e6f24b859 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 29 Aug 2020 00:53:11 -0400
Subject: added some link follow options to link Editor.  Big overhaul of
 dockingView to make things undoable and cleaner

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/CurrentUserUtils.ts                |  12 +-
 src/client/util/DragManager.ts                     |   3 +-
 src/client/views/GlobalKeyHandler.ts               |   4 +-
 .../views/collections/CollectionDockingView.tsx    | 487 ++++++++-------------
 .../views/collections/CollectionTreeView.tsx       |   2 +-
 src/client/views/linking/LinkEditor.tsx            |   8 +
 src/client/views/search/SearchBox.tsx              |   2 +-
 src/fields/Doc.ts                                  |   4 -
 src/mobile/MobileInterface.tsx                     |   4 +-
 10 files changed, 215 insertions(+), 313 deletions(-)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 18ff993fe..9186cea87 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -848,7 +848,7 @@ export namespace Docs {
                     {
                         type: type,
                         content: [
-                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth, config.path))
+                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth))
                         ]
                     }
                 ]
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c60403701..7937072da 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1025,7 +1025,7 @@ export class CurrentUserUtils {
         CurrentUserUtils.openDashboard(userDoc, copy);
     }
 
-    public static createNewDashboard = async (userDoc: Doc, id?: string) => {
+    public static createNewDashboard = (userDoc: Doc, id?: string) => {
         const myPresentations = userDoc.myPresentations as Doc;
         const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
         const dashboards = Cast(userDoc.myDashboards, Doc) as Doc;
@@ -1051,11 +1051,13 @@ export class CurrentUserUtils {
         dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]);
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
-        // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
-        setTimeout(() => {
-            CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
-        }, 0);
+        CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
     }
+
+    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); }
+    public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); }
 }
 
 Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0cca61841..8bf6faf03 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -117,10 +117,11 @@ export namespace DragManager {
     }
 
     export class DocumentDragData {
-        constructor(dragDoc: Doc[]) {
+        constructor(dragDoc: Doc[], dropAction?: dropActionType) {
             this.draggedDocuments = dragDoc;
             this.droppedDocuments = [];
             this.offset = [0, 0];
+            this.dropAction = dropAction;
         }
         draggedDocuments: Doc[];
         droppedDocuments: Doc[];
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 84b3d64fd..1cbbfd67b 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -99,7 +99,7 @@ export class KeyManager {
                 if (main.isPointerDown) {
                     DragManager.AbortDrag();
                 } else {
-                    if (CollectionDockingView.Instance.HasFullScreen()) {
+                    if (CollectionDockingView.Instance.HasFullScreen) {
                         CollectionDockingView.Instance.CloseFullScreen();
                     } else {
                         doDeselect = !ContextMenu.Instance.closeMenu();
@@ -253,7 +253,7 @@ export class KeyManager {
                 break;
             case "o":
                 const target = SelectionManager.SelectedDocuments()[0];
-                target && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(target);
+                target && CollectionDockingView.OpenFullScreen(target.props.Document);
                 break;
             case "r":
                 preventDefault = false;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 816f784ff..201cada09 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -37,9 +37,8 @@ const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
 export class CollectionDockingView extends CollectionSubView(doc => doc) {
-    @observable public static Instances: CollectionDockingView[] = [];
-    @computed public static get Instance() { return CollectionDockingView.Instances[0]; }
-    public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) {
+    @observable public static Instance: CollectionDockingView;
+    public static makeDocumentConfig(document: Doc, width?: number) {
         return {
             type: 'react-component',
             component: 'DocumentFrameRenderer',
@@ -47,79 +46,46 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             width: width,
             props: {
                 documentId: document[Id],
-                libraryPath: libraryPath?.map(d => d[Id])
             }
         };
     }
 
-    @computed public get initialized() {
-        return this._goldenLayout !== null;
-    }
-
-    @observable private _goldenLayout: any = null;
+    private _reactionDisposer?: IReactionDisposer;
     private _containerRef = React.createRef<HTMLDivElement>();
     private _flush: UndoManager.Batch | undefined;
     private _ignoreStateChange = "";
-    private _isPointerDown = false;
-    private _maximizedSrc: Opt<DocumentView>;
+    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;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
-        runInAction(() => !CollectionDockingView.Instances ? CollectionDockingView.Instances = [this] : CollectionDockingView.Instances.push(this));
+        runInAction(() => CollectionDockingView.Instance = this);
         //Why is this here?
         (window as any).React = React;
         (window as any).ReactDOM = ReactDOM;
         DragManager.StartWindowDrag = this.StartOtherDrag;
     }
+
     public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
-        console.log("START drag batch");
         !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, i) => CollectionDockingView.makeDocumentConfig(doc)) };
         const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config);
-        dragSource._dragListener.on("dragStop", () => dragSource.destroy());
+        dragSource._dragListener.on("dragStop", dragSource.destroy);
         dragSource._dragListener.onMouseDown(e);
     }
 
-    @undoBatch
-    @action
-    public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) {
-        if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") {
-            return CurrentUserUtils.openDashboard(Doc.UserDoc(), docView.props.Document);
-        }
-        const document = Doc.MakeAlias(docView.props.Document);
-        const newItemStackConfig = {
-            type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
-        };
-        const docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout);
-        this._goldenLayout.root.contentItems[0].addChild(docconfig);
-        docconfig.callDownwards('_$init');
-        this._goldenLayout._$maximiseItem(docconfig);
-        this._maximizedSrc = docView;
-        this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
-        this.stateChanged();
-        SelectionManager.DeselectAll();
-    }
-
     @undoBatch
     public CloseFullScreen = () => {
         const target = this._goldenLayout._maximisedItem;
-        if (target !== null && this._maximizedSrc) {
-            this._goldenLayout._maximisedItem.remove();
-            SelectionManager.SelectDoc(this._maximizedSrc, false);
-            this._maximizedSrc = undefined;
+        if (target) {
+            target.remove();
             this.stateChanged();
         }
     }
 
-    public HasFullScreen = () => {
-        return this._goldenLayout._maximisedItem !== null;
-    }
-
     @undoBatch
     @action
     public static CloseRightSplit(document: Opt<Doc>): boolean {
@@ -136,17 +102,29 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         const retVal = !CollectionDockingView.Instance?._goldenLayout.root.contentItems[0].isRow ? false :
             Array.from(CollectionDockingView.Instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose));
 
-        retVal && CollectionDockingView.Instance.layoutChanged(document);
+        retVal && document && CollectionDockingView.Instance.layoutChanged();
         return retVal;
     }
 
     @undoBatch
     @action
-    layoutChanged(removed?: Doc) {
-        this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]);
-        this._goldenLayout.emit('stateChanged');
-        this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
-        this.stateChanged();
+    public static OpenFullScreen(doc: Doc, libraryPath?: Doc[]) {
+        const instance = CollectionDockingView.Instance;
+        if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
+            return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        }
+        const newItemStackConfig = {
+            type: 'stack',
+            content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc), undefined)]
+        };
+        const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+        instance._goldenLayout.root.contentItems[0].addChild(docconfig);
+        docconfig.callDownwards('_$init');
+        instance._goldenLayout._$maximiseItem(docconfig);
+        instance._goldenLayout.emit('stateChanged');
+        instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig());
+        instance.stateChanged();
+        return true;
     }
 
     public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean {
@@ -156,14 +134,16 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => {
                 if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" &&
                     DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanel) {
-                    const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
+                    const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined);
+                    runInAction(() => document.isDisplayPanel = true);
                     child.addChild(newItemStackConfig, undefined);
                     !addToSplit && child.contentItems[0].remove();
                     return true;
                 }
                 return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => {
                     if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)?.Document.isDisplayPanel) {
-                        const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
+                        const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, undefined);
+                        runInAction(() => document.isDisplayPanel = true);
                         child.addChild(newItemStackConfig, undefined);
                         !addToSplit && child.contentItems[j].remove();
                         return true;
@@ -171,7 +151,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                     return false;
                 });
             });
-        retVal && instance.layoutChanged(document);
+        retVal && instance.layoutChanged();
         return retVal;
     }
 
@@ -183,9 +163,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     public static AddRightSplit(document: Doc, dontSelect: boolean = false, isDisplayPanel: Opt<boolean> = undefined) {
         if (!CollectionDockingView.Instance) return false;
 
-        const ind = CollectionDockingView.Instance._tabMap.findIndex((val) => val.doc === document);
+        const ind = Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === document);
         if (ind !== -1) {
-            const tab = CollectionDockingView.Instance._tabMap[ind].tab;
+            const tab = Array.from(CollectionDockingView.Instance.tabMap.keys())[ind];
             const activeContentItem = tab.header.parent.getActiveContentItem();
             if (tab.contentItem !== activeContentItem) {
                 tab.header.parent.setActiveContentItem(tab.contentItem);
@@ -198,7 +178,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             const instance = CollectionDockingView.Instance;
             const newItemStackConfig = {
                 type: 'stack',
-                content: [CollectionDockingView.makeDocumentConfig(document, undefined, [])]
+                content: [CollectionDockingView.makeDocumentConfig(document, undefined)]
             };
 
             const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
@@ -238,7 +218,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         const instance = CollectionDockingView.Instance;
         const newItemStackConfig = {
             type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath)]
+            content: [CollectionDockingView.makeDocumentConfig(document, undefined)]
         };
 
         const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
@@ -299,16 +279,20 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     //
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to that split
     //
+    @undoBatch
     public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) {
         if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) {
-            CollectionDockingView.AddRightSplit(document, false, true);
+            return CollectionDockingView.AddRightSplit(document, false, true);
         }
+        return false;
     }
 
-    public AddTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
+    @undoBatch
+    public static AddTab(stack: any, document: Doc, libraryPath?: Doc[]) {
+        const instance = CollectionDockingView.Instance;
+        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined);
         if (stack === undefined) {
-            let stack: any = this._goldenLayout.root;
+            stack = instance._goldenLayout.root;
             while (!stack.isStack) {
                 if (stack.contentItems.length) {
                     stack = stack.contentItems[0];
@@ -318,41 +302,21 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                     break;
                 }
             }
-            if (stack) {
-                stack.addChild(docContentConfig);
-            }
-        } else {
-            stack.addChild(docContentConfig, undefined);
         }
-        this.layoutChanged();
+        stack?.addChild(docContentConfig, undefined);
+        instance.layoutChanged();
         return true;
     }
 
-    public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => {
-        const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath);
-        if (stack === undefined) {
-            let stack: any = this._goldenLayout.root;
-            while (!stack.isStack) {
-                if (stack.contentItems.length) {
-                    stack = stack.contentItems[0];
-                } else {
-                    stack.addChild({ type: 'stack', content: [docContentConfig] });
-                    stack = undefined;
-                    break;
-                }
-            }
-            if (stack) {
-                stack.addChild(docContentConfig);
-            }
-        } else {
-            stack.addChild(docContentConfig, undefined);
-        }
-        this.layoutChanged();
-        return true;
+    @undoBatch
+    @action
+    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();
     }
 
-    _tabMap: { tab: any, doc: Doc }[] = [];
-
     async setupGoldenLayout() {
         const config = StrCast(this.props.Document.dockingConfig);
         if (config) {
@@ -360,27 +324,23 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) ?? [];
             await Promise.all(docids.map(id => DocServer.GetRefField(id)));
 
-            if (!this._goldenLayout) {
-                runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
-            }
-            else {
+            if (this._goldenLayout) {
                 if (config === JSON.stringify(this._goldenLayout.toConfig())) {
                     return;
+                } else {
+                    try {
+                        this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
+                        this._goldenLayout.unbind('stackCreated', this.stackCreated);
+                    } catch (e) { }
                 }
-                try {
-                    this._goldenLayout.unbind('tabCreated', this.tabCreated);
-                    this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
-                    this._goldenLayout.unbind('stackCreated', this.stackCreated);
-                } catch (e) { }
-                this._goldenLayout.destroy();
-                runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
             }
-            this._goldenLayout.on('tabCreated', this.tabCreated);
+            this.tabMap.clear();
+            this._goldenLayout?.destroy();
+            runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
             this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
             this._goldenLayout.on('stackCreated', this.stackCreated);
             this._goldenLayout.registerComponent('DocumentFrameRenderer', DockedFrameRenderer);
             this._goldenLayout.container = this._containerRef.current;
-            //this._goldenLayout.on("stateChanged", () => console.log("STATE CHANGED"));
             if (this._goldenLayout.config.maximisedItemId === '__glMaximised') {
                 try {
                     this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise();
@@ -391,17 +351,15 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             this._goldenLayout.init();
         }
     }
-    reactionDisposer?: Lambda;
+
     componentDidMount: () => void = () => {
         if (this._containerRef.current) {
-            const observer = new _global.ResizeObserver(this.onResize);
-            observer.observe(this._containerRef.current);
-            this.reactionDisposer = reaction(
-                () => StrCast(this.props.Document.dockingConfig),
+            new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
+            this._reactionDisposer = reaction(() => StrCast(this.props.Document.dockingConfig),
                 config => {
                     if (!this._goldenLayout || this._ignoreStateChange !== config) {
                         this.setupGoldenLayout();
-                        DocListCast((Doc.UserDoc().myDashboards as Doc).data).map(d => d.dashboardBrush = false);
+                        DocListCast(CurrentUserUtils.MyDashboards.data).map(d => d.dashboardBrush = false);
                         this.props.Document.dashboardBrush = true;
                     }
                     this._ignoreStateChange = "";
@@ -410,24 +368,19 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
         }
     }
+
     componentWillUnmount: () => void = () => {
         try {
             this.props.Document.dashboardBrush = false;
-            this._goldenLayout.unbind('tabCreated', this.tabCreated);
             this._goldenLayout.unbind('stackCreated', this.stackCreated);
             this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
-        } catch (e) {
-
-        }
+        } catch (e) { }
         this._goldenLayout?.destroy();
-        runInAction(() => {
-            CollectionDockingView.Instances.splice(CollectionDockingView.Instances.indexOf(this), 1);
-            this._goldenLayout = null;
-        });
         window.removeEventListener('resize', this.onResize);
 
-        this.reactionDisposer?.();
+        this._reactionDisposer?.();
     }
+
     @action
     onResize = (event: any) => {
         const cur = this._containerRef.current;
@@ -438,31 +391,29 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     @action
     onPointerUp = (e: MouseEvent): void => {
         window.removeEventListener("pointerup", this.onPointerUp);
-        this._isPointerDown = false;
         if (this._flush) {
             setTimeout(() => {
                 CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
                 this.stateChanged();
-                console.log("END BATCH Up");
                 this._flush!.end();
                 this._flush = undefined;
             }, 10);
         }
     }
+
     @action
     onPointerDown = (e: React.PointerEvent): void => {
         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"))) {
-            console.log("START BATCH dwn");
             this._flush = UndoManager.StartBatch("golden layout edit");
         }
-        if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) {
-            return;
-        } else {
+        if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
+            Doc.GetSelectedTool() !== InkTool.Highlighter && Doc.GetSelectedTool() !== InkTool.Pen) {
             e.stopPropagation();
         }
     }
+
     public static Copy(doc: Doc) {
         let json = StrCast(doc.dockingConfig);
         const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -504,124 +455,18 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()));
     }
 
-    tabCreated = (tab: any) => {
-        tab.titleElement[0].Tab = tab;
-        if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
-            if (tab.contentItem.config.fixed) {
-                tab.contentItem.parent.config.fixed = true;
-            }
-
-            const doc = DocServer.GetCachedRefField(tab.contentItem.config.props.documentId) as Doc;
-            if (doc instanceof Doc) {
-                this._tabMap.push({ tab: tab, doc: doc });
-                tab.titleElement[0].onclick = (e: any) => {
-                    if (Date.now() - tab.titleElement[0].lastClick < 1000) tab.titleElement[0].select();
-                    tab.titleElement[0].lastClick = Date.now();
-                    tab.titleElement[0].focus();
-                };
-                tab.titleElement[0].onchange = (e: any) => {
-                    tab.titleElement[0].size = e.currentTarget.value.length + 1;
-                    Doc.GetProto(doc).title = e.currentTarget.value, true;
-                };
-                tab.titleElement[0].size = StrCast(doc.title).length + 1;
-                tab.titleElement[0].value = doc.title;
-                tab.titleElement[0].style["max-width"] = "100px";
-                const gearSpan = document.createElement("span");
-                gearSpan.className = "collectionDockingView-gear";
-                gearSpan.style.position = "relative";
-                gearSpan.style.paddingLeft = "0px";
-                gearSpan.style.paddingRight = "12px";
-                const stack = tab.contentItem.parent;
-                tab.element[0].onpointerdown = (e: any) => {
-                    if (e.target.className !== "lm_close_tab") {
-                        const view = DocumentManager.Instance.getDocumentView(doc);
-                        view && SelectionManager.SelectDoc(view, false);
-                    }
-                };
-                // shifts the focus to this tab when another tab is dragged over it
-                tab.element[0].onmouseenter = (e: any) => {
-                    if (!this._isPointerDown || !SnappingManager.GetIsDragging()) return;
-                    const activeContentItem = tab.header.parent.getActiveContentItem();
-                    if (tab.contentItem !== activeContentItem) {
-                        tab.header.parent.setActiveContentItem(tab.contentItem);
-                    }
-                    tab.setActive(true);
-                };
-                const onDown = (e: React.PointerEvent) => {
-                    setupMoveUpEvents(this, e, (e) => {
-                        if (!(e as any).defaultPrevented) {
-                            const dragData = new DragManager.DocumentDragData([doc]);
-                            dragData.dropAction = doc.dropAction as dropActionType;
-                            DragManager.StartDocumentDrag([gearSpan], dragData, e.clientX, e.clientY);
-                            return true;
-                        }
-                        return false;
-                    }, returnFalse, emptyFunction);
-                };
-
-                tab.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
-                    (sel) => {
-                        const selected = sel.some(v => v.props.Document === doc);
-                        selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem);
-                    }
-                );
-                tab.buttonDisposer = reaction(() => ((view: Opt<DocumentView>) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)),
-                    (views) => {
-                        if (views.length) {
-                            ReactDOM.render(<span title="Drag as document" className="collectionDockingView-dragAsDocument" onPointerDown={onDown} >
-                                <DockingViewButtonSelector views={() => views} Stack={stack} />
-                            </span>,
-                                gearSpan);
-                            tab.buttonDisposer?.();
-                        }
-                    }, { fireImmediately: true });
-
-                tab.reactComponents = [gearSpan];
-                tab.element.append(gearSpan);
-                tab.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
-                    tab.titleElement[0].value = title;
-                    tab.titleElement[0].style.padding = degree ? 0 : 2;
-                    tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
-                }, { fireImmediately: true });
-                //TODO why can't this just be doc instead of the id?
-                tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId;
-            }
-        }
-        tab.closeElement.off('click') //unbind the current click handler
-            .click(function () {
-                tab.selectionDisposer?.();
-                tab.reactionDisposer?.();
-                tab.buttonDisposer?.();
-                const doc = DocServer.GetCachedRefField(tab.contentItem.config.props.documentId);
-                if (doc instanceof Doc) {
-                    const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null);
-                    recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
-                    SelectionManager.DeselectAll();
-                }
-                CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
-                tab.contentItem.remove();
-                CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
-            });
-    }
-
     tabDestroyed = (tab: any) => {
-        const ind = this._tabMap.findIndex((val) => val.tab === tab);
-        ind !== -1 && this._tabMap.splice(ind, 1);
-        if (tab.reactComponents) {
-            for (const ele of tab.reactComponents) {
-                ReactDOM.unmountComponentAtNode(ele);
-            }
-        }
+        this.tabMap.delete(tab);
+        Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+        tab.reactComponents && Array.from(tab.reactComponents).forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
     }
 
-
     stackCreated = (stack: any) => {
-        //stack.header.controlsContainer.find('.lm_popout').hide();
         stack.header.element.on('mousedown', (e: any) => {
             if (e.target === stack.header.element[0] && e.button === 2) {
-                const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null);
+                const emptyPane = CurrentUserUtils.EmptyPane;
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
-                this.AddTab(stack, Docs.Create.FreeformDocument([], {
+                CollectionDockingView.AddTab(stack, Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
                 }));
             }
@@ -632,59 +477,125 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
             .click(action(() => {
                 //if (confirm('really close this?')) {
                 stack.remove();
-                stack.contentItems.forEach((contentItem: any) => {
-                    const doc = Cast(DocServer.GetCachedRefField(contentItem.config.props.documentId), Doc, null);
-                    if (doc instanceof Doc) {
-                        const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null);
-                        recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
-                    }
-                });
+                stack.contentItems.forEach((contentItem: any) => Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", contentItem.tab.DashDoc, undefined, true, true));
             }));
         stack.header.controlsContainer.find('.lm_popout') //get the close icon
             .off('click') //unbind the current click handler
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
-                const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null);
+                const emptyPane = CurrentUserUtils.EmptyPane;
                 emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
-                this.AddTab(stack, Docs.Create.FreeformDocument([], {
+                CollectionDockingView.AddTab(stack, Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
                 }));
             }));
     }
 
     render() {
-        if (this.props.renderDepth > 0) {
-            return <div style={{ width: "100%", height: "100%" }}>Nested dashboards can't be rendered</div>;
-        }
-        return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef} />;
+        return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef}>
+            {this.props.renderDepth > 0 ? "Nested dashboards can't be rendered" : (null)}
+        </div>;
     }
 }
 
 interface DockedFrameProps {
     documentId: FieldId;
     glContainer: any;
-    libraryPath: (FieldId[]);
-    //collectionDockingView: CollectionDockingView
 }
 @observer
 export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     _mainCont: HTMLDivElement | null = null;
+    _tabReaction: IReactionDisposer | undefined;
     @observable private _panelWidth = 0;
     @observable private _panelHeight = 0;
-    @observable private _document: Opt<Doc>;
     @observable private _isActive: boolean = false;
-    _tabReaction: IReactionDisposer | undefined;
 
-    get _stack(): any {
-        return (this.props as any).glContainer.parent.parent;
-    }
-    get _tab(): any {
-        const tab = (this.props as any).glContainer.tab?.element[0] as HTMLElement;
-        return tab?.getElementsByClassName("lm_title")?.[0];
-    }
+    get stack(): any { return (this.props as any).glContainer.parent.parent; }
+    get tab() { return (this.props as any).glContainer.tab; }
+    get tabTitle(): any { return (this.tab?.element[0] as HTMLElement).getElementsByClassName("lm_title")?.[0]; }
+    get view() { return this._document && DocumentManager.Instance.getDocumentView(this._document); }
+    @observable _document: Doc | undefined;
+
     constructor(props: any) {
         super(props);
-        this._document = Cast(DocServer.GetCachedRefField(this.props.documentId), Doc, null);
+        setTimeout(() => DocServer.GetRefField(this.tab.contentItem.config.props.documentId).then(doc => this.init(doc as Doc)), 0);
+    }
+
+    @action
+    init = (doc: Doc) => {
+        const tab = this.tab;
+        tab._disposers = {} as { [name: string]: IReactionDisposer };
+        tab.DashDoc = this._document = doc;
+        if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
+            tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
+
+            CollectionDockingView.Instance.tabMap.add(tab);
+            tab.titleElement[0].onclick = (e: any) => {
+                if (Date.now() - tab.titleElement[0].lastClick < 1000) tab.titleElement[0].select();
+                tab.titleElement[0].lastClick = Date.now();
+                tab.titleElement[0].focus();
+            };
+            tab.titleElement[0].onchange = (e: any) => {
+                tab.titleElement[0].size = e.currentTarget.value.length + 1;
+                Doc.GetProto(doc).title = e.currentTarget.value;
+            };
+            tab.titleElement[0].size = StrCast(this._document.title).length + 1;
+            tab.titleElement[0].value = this._document.title;
+            tab.titleElement[0].style["max-width"] = "100px";
+            const gearSpan = document.createElement("span");
+            gearSpan.className = "collectionDockingView-gear";
+            gearSpan.style.position = "relative";
+            gearSpan.style.paddingLeft = "0px";
+            gearSpan.style.paddingRight = "12px";
+            const stack = tab.contentItem.parent;
+            tab.element[0].onpointerdown = (e: any) => {
+                e.target.className !== "lm_close_tab" && this.view && SelectionManager.SelectDoc(this.view, false);
+            };
+            // 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);
+            };
+            const onDown = (e: React.PointerEvent) => {
+                setupMoveUpEvents(this, e, (e) => {
+                    !e.defaultPrevented && DragManager.StartDocumentDrag([gearSpan], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY);
+                    return !e.defaultPrevented;
+                }, returnFalse, emptyFunction);
+            };
+
+            tab._disposers.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments().some(v => v.props.Document === doc),
+                (selected) => {
+                    selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem);
+                }
+            );
+            tab._disposers.buttonDisposer = reaction(() => this.view,
+                (view) => {
+                    if (view) {
+                        ReactDOM.render(<span title="Drag as document" className="collectionDockingView-dragAsDocument" onPointerDown={onDown} >
+                            <DockingViewButtonSelector views={() => [view]} Stack={stack} />
+                        </span>,
+                            gearSpan);
+                        tab._disposers.buttonDisposer?.();
+                    }
+                }, { fireImmediately: true });
+
+            tab.reactComponents = [gearSpan];
+            tab.element.append(gearSpan);
+            tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
+                tab.titleElement[0].value = title;
+                tab.titleElement[0].style.padding = degree ? 0 : 2;
+                tab.titleElement[0].style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
+            }, { fireImmediately: true });
+        }
+        tab.closeElement.off('click') //unbind the current click handler
+            .click(function () {
+                Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+                Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true);
+                SelectionManager.DeselectAll();
+                tab.contentItem.remove();
+            });
     }
     /**
      * Adds a document to the presentation view
@@ -695,7 +606,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         if (unpin) DockedFrameRenderer.UnpinDoc(doc);
         else {
             //add this new doc to props.Document
-            const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+            const curPres = CurrentUserUtils.ActivePresentation;
             if (curPres) {
                 const pinDoc = Doc.MakeAlias(doc);
                 pinDoc.presentationTargetDoc = doc;
@@ -717,7 +628,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @action
     public static UnpinDoc(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        const curPres = CurrentUserUtils.ActivePresentation;
         if (curPres) {
             const ind = DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc));
             ind !== -1 && Doc.RemoveDocFromList(curPres, "data", DocListCast(curPres.data)[ind]);
@@ -727,7 +638,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     componentDidMount() {
         const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white");
         const selected = () => SelectionManager.SelectedDocuments().some(v => v.props.Document === this._document);
-        const updateTabColor = () => this._tab && (this._tab.style.backgroundColor = selected() ? color() : "");
+        const updateTabColor = () => this.tabTitle && (this.tabTitle.style.backgroundColor = selected() ? color() : "");
         const observer = new _global.ResizeObserver(action((entries: any) => {
             for (const entry of entries) {
                 this._panelWidth = entry.contentRect.width;
@@ -750,10 +661,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     private onActiveContentItemChanged() {
         if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) {
             this._isActive = this.props.glContainer.tab.isActive;
-            this._isActive && setTimeout(() => {
-                const dv = this._document && DocumentManager.Instance.getFirstDocumentView(this._document);
-                dv && SelectionManager.SelectDoc(dv, false);
-            });
+            this._isActive && setTimeout(() => this.view && SelectionManager.SelectDoc(this.view, false), 0);
             (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.
         }
@@ -764,7 +672,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) :
         (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth)
     panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
-
     nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0;
     nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0;
 
@@ -778,17 +685,9 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth))) {
             scaling = this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
         } else if (nativeW && nativeH) {
-            // if (this.layoutDoc!.type === DocumentType.PDF || this.layoutDoc!.type === DocumentType.WEB) {
-            //     if ((this.layoutDoc?._fitWidth) ||
-            //         this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) {
-            //         return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth);
-            //     } else {
-            //         return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight);
-            //     }
-            // }
             const wscale = this.panelWidth() / nativeW;
             scaling = wscale * nativeH > this._panelHeight ? this._panelHeight / nativeH : wscale;
-        } else scaling = 1;
+        }
         return scaling;
     }
 
@@ -805,17 +704,14 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => {
         SelectionManager.DeselectAll();
-        if (doc._viewType === CollectionViewType.Docking) {
-            return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
-        } else if (location === "onRight") {
-            return CollectionDockingView.AddRightSplit(doc);
-        } else if (location === "close") {
-            return CollectionDockingView.CloseRightSplit(doc);
-        } else if (location === "replace") {
-            CollectionDockingView.UseRightSplit(doc);
-            return true;
-        } else {// if (location === "inPlace") {
-            return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath);
+        if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        switch (location) {
+            case "onRight": return CollectionDockingView.AddRightSplit(doc);
+            case "close": return CollectionDockingView.CloseRightSplit(doc);
+            case "replace": return CollectionDockingView.UseRightSplit(doc);
+            case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
+            case "inPlace":
+            default: return CollectionDockingView.AddTab(this.stack, doc);
         }
     }
 
@@ -833,7 +729,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null);
     returnMiniSize = () => NumCast(this._document?._miniMapSize, 150);
     miniDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
+        this._document && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
             this._document!._panX = clamp(NumCast(this._document!._panX) + delta[0] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.l, this.renderContentBounds.l + this.renderContentBounds.dim);
             this._document!._panY = clamp(NumCast(this._document!._panY) + delta[1] / this.returnMiniSize() * this.renderContentBounds.dim, this.renderContentBounds.t, this.renderContentBounds.t + this.renderContentBounds.dim);
             return false;
@@ -899,12 +795,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
     @computed get docView() {
         TraceMobx();
         if (!this._document) return (null);
-        const document = this._document;
         const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined;
         return <>
-            <DocumentView key={document[Id]}
+            <DocumentView key={this._document[Id]}
                 LibraryPath={emptyPath}
-                Document={document}
+                Document={this._document}
                 DataDoc={resolvedDataDoc}
                 bringToFront={emptyFunction}
                 rootSelected={returnTrue}
@@ -927,7 +822,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
                 searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined} />
-            {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
+            {this._document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
         </>;
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index cdfa67769..fffbe65a3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -738,7 +738,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myDashboards) {
+        if (!e.isPropagationStopped() && this.doc === CurrentUserUtils.MyDashboards) {
             ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" });
             e.stopPropagation();
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 11a905fb6..8e391f995 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -379,6 +379,14 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
                         onPointerDown={() => this.changeFollowBehavior("onRight")}>
                         Always open in a new pane
                         </div>
+                    <div className="linkEditor-followingDropdown-option"
+                        onPointerDown={() => this.changeFollowBehavior("replace")}>
+                        Always replace right tab
+                        </div>
+                    <div className="linkEditor-followingDropdown-option"
+                        onPointerDown={() => this.changeFollowBehavior("fullScreen")}>
+                        Always open full screen
+                        </div>
                     <div className="linkEditor-followingDropdown-option"
                         onPointerDown={() => this.changeFollowBehavior("inTab")}>
                         Always open in a new tab
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index a24761195..d7a8ed404 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -492,7 +492,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
     }
     render() {
-        const myDashboards = DocListCast(Cast(Doc.UserDoc().myDashboards, Doc, null).data);
+        const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
         return (
             <div style={{ pointerEvents: "all" }} className="searchBox-container">
                 <div style={{ position: "absolute", left: 15, height: 32, alignItems: "center", display: "flex" }}>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index cb4ca9a25..1401dd6a1 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -939,19 +939,16 @@ export namespace Doc {
     }
 
     export function IsBrushed(doc: Doc) {
-        return false;
         return computedFn(function IsBrushed(doc: Doc) {
             return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
         })(doc);
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
-        return 0;
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0;
         return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0;
     }
     export function IsBrushedDegree(doc: Doc) {
-        return 0;
         return computedFn(function IsBrushDegree(doc: Doc) {
             return Doc.IsBrushedDegreeUnmemoized(doc);
         })(doc);
@@ -980,7 +977,6 @@ export namespace Doc {
 
     let _lastDate = 0;
     export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) {
-        return;
         linkFollowUnhighlight();
         Doc.HighlightDoc(destDoc, dataAndDisplayDocs);
         document.removeEventListener("pointerdown", linkFollowUnhighlight);
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 841862f49..d4eb76ecd 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -324,7 +324,7 @@ export class MobileInterface extends React.Component {
             );
         }
         // stores dashboards documents as 'dashboards' variable
-        let dashboards = Cast(Doc.UserDoc().myDashboards, Doc) as Doc;
+        let dashboards = CurrentUserUtils.MyDashboards;
         if (this.dashboards) {
             dashboards = this.dashboards;
         }
@@ -394,7 +394,7 @@ export class MobileInterface extends React.Component {
      */
     @action
     createNewDashboard = async (id?: string) => {
-        const scens = Cast(Doc.UserDoc().myDashboards, Doc) as Doc;
+        const scens = CurrentUserUtils.MyDashboards;
         const dashboardCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
-- 
cgit v1.2.3-70-g09d2


From 442572a66aaa6b50d8d01d46d79b7d6cf2f247a8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 29 Aug 2020 17:42:48 -0400
Subject: fixed maximize docdecoration to toggle target w/o making an alias.
 use ctrlKey to make an alias.  removed dashboardBrush stuff.  cleaned up
 DocDecoartions css slightly

---
 src/client/util/CurrentUserUtils.ts                |  9 ++-
 src/client/views/DocumentDecorations.scss          | 84 ++++++++++++----------
 src/client/views/DocumentDecorations.tsx           | 82 +++++++++------------
 src/client/views/MainView.tsx                      | 17 +++--
 .../views/collections/CollectionDockingView.tsx    | 13 ++--
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 .../CollectionFreeFormLayoutEngines.tsx            |  3 +-
 .../collectionGrid/CollectionGridView.scss         | 11 ---
 src/client/views/nodes/DocumentView.tsx            |  7 +-
 .../views/nodes/formattedText/DashDocView.tsx      |  5 +-
 .../views/nodes/formattedText/RichTextSchema.tsx   |  5 +-
 src/client/views/search/SearchBox.tsx              |  2 +-
 13 files changed, 117 insertions(+), 125 deletions(-)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 20f8fe9c1..2ca395164 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1020,7 +1020,7 @@ export class CurrentUserUtils {
     }
 
     public static snapshotDashboard = (userDoc: Doc) => {
-        const copy = CollectionDockingView.Copy(Cast(userDoc.activeDashboard, Doc, null));
+        const copy = CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
         Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
         CurrentUserUtils.openDashboard(userDoc, copy);
     }
@@ -1031,7 +1031,11 @@ export class CurrentUserUtils {
         const dashboards = Cast(userDoc.myDashboards, Doc) as Doc;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
         const emptyPane = Cast(userDoc.emptyPane, Doc, null);
-        emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+        try {
+            emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+        } catch (e) {
+            console.log(e)
+        }
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
@@ -1054,6 +1058,7 @@ export class CurrentUserUtils {
         CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
     }
 
+    public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, 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.scss b/src/client/views/DocumentDecorations.scss
index b5c32ca1d..8291d7212 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -16,21 +16,11 @@ $linkGap : 3px;
     grid-template-columns: 8px 16px 1fr 8px 8px;
     pointer-events: none;
 
-    #documentDecorations-centerCont {
+    .documentDecorations-centerCont {
         grid-column: 3;
         background: none;
     }
 
-    .documentDecorations-resizer {
-        pointer-events: auto;
-        background: $alt-accent;
-        opacity: 0.1;
-    }
-
-    .documentDecorations-resizer:hover {
-        opacity: 1;
-    }
-
     .documentDecorations-selector {
         pointer-events: auto;
         height: 15px;
@@ -49,27 +39,43 @@ $linkGap : 3px;
         grid-row: 4;
     }
 
-    #documentDecorations-topLeftResizer,
-    #documentDecorations-leftResizer,
-    #documentDecorations-bottomLeftResizer {
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-topRightResizer,
+    .documentDecorations-bottomLeftResizer,
+    .documentDecorations-bottomRightResizer,
+    .documentDecorations-leftResizer,
+    .documentDecorations-topResizer,
+    .documentDecorations-bottomResizer,
+    .documentDecorations-rightResizer {
+        pointer-events: auto;
+        background: $alt-accent;
+        opacity: 0.1;
+        &:hover {
+            opacity: 1;
+        }
+    }
+
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-leftResizer,
+    .documentDecorations-bottomLeftResizer {
         grid-column: 1
     }
 
-    #documentDecorations-topResizer,
-    #documentDecorations-bottomResizer {
+    .documentDecorations-topResizer,
+    .documentDecorations-bottomResizer {
         grid-column-start: 2;
         grid-column-end: 5;
     }
 
-    #documentDecorations-bottomRightResizer,
-    #documentDecorations-topRightResizer,
-    #documentDecorations-rightResizer {
+    .documentDecorations-bottomRightResizer,
+    .documentDecorations-topRightResizer,
+    .documentDecorations-rightResizer {
         grid-column-start: 5;
         grid-column-end: 7;
     }
 
-    #documentDecorations-rotation,
-    #documentDecorations-borderRadius {
+    .documentDecorations-rotation,
+    .documentDecorations-borderRadius {
         grid-column: 5;
         grid-row: 4;
         border-radius: 100%;
@@ -87,69 +93,69 @@ $linkGap : 3px;
             position: absolute;
         }
     }
-    #documentDecorations-rotation {
+    .documentDecorations-rotation {
         background: transparent;
         right: -15;
     }
 
 
-    #documentDecorations-topLeftResizer,
-    #documentDecorations-bottomRightResizer {
+    .documentDecorations-topLeftResizer,
+    .documentDecorations-bottomRightResizer {
         cursor: nwse-resize;
         background: unset;
         opacity: 1;
     }
 
-    #documentDecorations-topLeftResizer {
+    .documentDecorations-topLeftResizer {
         border-left: 2px solid;
         border-top: solid 2px;
     }
 
-    #documentDecorations-bottomRightResizer {
+    .documentDecorations-bottomRightResizer {
         border-right: 2px solid;
         border-bottom: solid 2px;
     }
 
-    #documentDecorations-topLeftResizer:hover,
-    #documentDecorations-bottomRightResizer:hover {
+    .documentDecorations-topLeftResizer:hover,
+    .documentDecorations-bottomRightResizer:hover {
         opacity: 1;
     }
 
-    #documentDecorations-bottomRightResizer {
+    .documentDecorations-bottomRightResizer {
         grid-row: 4;
     }
 
-    #documentDecorations-topRightResizer,
-    #documentDecorations-bottomLeftResizer {
+    .documentDecorations-topRightResizer,
+    .documentDecorations-bottomLeftResizer {
         cursor: nesw-resize;
         background: unset;
         opacity: 1;
     }
 
-    #documentDecorations-topRightResizer {
+    .documentDecorations-topRightResizer {
         border-right: 2px solid;
         border-top: 2px solid;
     }
 
-    #documentDecorations-bottomLeftResizer {
+    .documentDecorations-bottomLeftResizer {
         border-left: 2px solid;
         border-bottom: 2px solid;
     }
 
-    #documentDecorations-topRightResizer:hover,
-    #documentDecorations-bottomLeftResizer:hover {
+    .documentDecorations-topRightResizer:hover,
+    .documentDecorations-bottomLeftResizer:hover {
         cursor: nesw-resize;
         background: dimGray;
         opacity: 1;
     }
 
-    #documentDecorations-topResizer,
-    #documentDecorations-bottomResizer {
+    .documentDecorations-topResizer,
+    .documentDecorations-bottomResizer {
         cursor: ns-resize;
     }
 
-    #documentDecorations-leftResizer,
-    #documentDecorations-rightResizer {
+    .documentDecorations-leftResizer,
+    .documentDecorations-rightResizer {
         cursor: ew-resize;
     }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c7a24133c..6db5186ba 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,6 +24,7 @@ import { FormatShapePane } from './FormatShapePane';
 import { DocumentView } from "./nodes/DocumentView";
 import React = require("react");
 import e = require('express');
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
 
 @observer
 export class DocumentDecorations extends React.Component<{}, { value: string }> {
@@ -177,7 +178,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
     }
     @action
     onMaximizeDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick);
+        if (e.ctrlKey) {
+            const selectedDocs = SelectionManager.SelectedDocuments();
+            const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
+            alias.context = undefined;
+            //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
+            CollectionDockingView.AddSplit(alias, "right");
+            e.stopPropagation();
+            e.preventDefault();
+        } else {
+            setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick);
+        }
     }
     @undoBatch
     @action
@@ -185,10 +196,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (e.button === 0) {
             const selectedDocs = SelectionManager.SelectedDocuments();
             if (selectedDocs.length) {
-                const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
-                alias.context = undefined;
-                //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]);
-                CollectionDockingView.AddSplit(alias, "right");
+                CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
             }
         }
         SelectionManager.DeselectAll();
@@ -333,7 +341,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { });
         if (e.button === 0) {
-            this._resizeHdlId = e.currentTarget.id;
+            this._resizeHdlId = e.currentTarget.className;
             const bounds = e.currentTarget.getBoundingClientRect();
             this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
             this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
@@ -575,7 +583,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
     }
     render() {
-        const darkScheme = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimgray" : undefined;
+        const darkScheme = CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : undefined;
         const bounds = this.Bounds;
         const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
@@ -587,35 +595,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         });
         const minimal = bounds.r - bounds.x < 100 ? true : false;
         const maximizeIcon = minimal ? (
-            <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top">
+            <Tooltip title={<div className="dash-tooltip">Show context menu</div>} placement="top">
                 <div className="documentDecorations-contextMenu" onPointerDown={this.onSettingsDown}>
                     <FontAwesomeIcon size="lg" icon="bars" />
                 </div></Tooltip>) : canDelete ? (
-                    <Tooltip title={<><div className="dash-tooltip">Close</div></>} placement="top">
+                    <Tooltip title={<div className="dash-tooltip">Close</div>} placement="top">
                         <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
                             {/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
                             <FontAwesomeIcon className="documentdecorations-times" icon={"times"} size="lg" />
                         </div></Tooltip>) : (null);
 
         const titleArea = this._edtingTitle ?
+            <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
+                onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> :
             <>
-                <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
-                    onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} />
-                {minimal ? (null) : <div className="publishBox" // title="make document referenceable by its title"
-                // onPointerDown={action(e => {
-                //     if (!seldoc.props.Document["title-custom"]) {
-                //         seldoc.props.Document["title-custom"] = true;
-                //         StrCast(Doc.GetProto(seldoc.props.Document).title).startsWith("-") && (Doc.GetProto(seldoc.props.Document).title = StrCast(seldoc.props.Document.title).substring(1));
-                //         this._accumulatedTitle = StrCast(seldoc.props.Document.title);
-                //     }
-                //     DocUtils.Publish(seldoc.props.Document, this._accumulatedTitle, seldoc.props.addDocument, seldoc.props.removeDocument);
-                // })}
-                >
-                    {/* <FontAwesomeIcon size="lg" color={SelectionManager.SelectedDocuments()[0].props.Document.title === SelectionManager.SelectedDocuments()[0].props.Document[Id] ? "green" : undefined} icon="sticky-note"></FontAwesomeIcon> */}
-                </div>}
-            </> :
-            <>
-                {minimal ? (null) : <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
+                {minimal ? (null) : <Tooltip title={<div className="dash-tooltip">Show context menu</div>} placement="top"><div className="documentDecorations-contextMenu" key="menu" onPointerDown={this.onSettingsDown}>
                     <FontAwesomeIcon size="lg" icon="bars" />
                 </div></Tooltip>}
                 <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
@@ -656,37 +650,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     {maximizeIcon}
                     {titleArea}
                     {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
-                        <Tooltip title={<><div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div></>} placement="top">
+                        <Tooltip title={<div className="dash-tooltip">{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}</div>} placement="top">
                             <div className="documentDecorations-iconifyButton" onPointerDown={this.onIconifyDown}>
                                 <FontAwesomeIcon icon={seldoc.finalLayoutKey.includes("icon") ? "window-restore" : "window-minimize"} className="documentView-minimizedIcon" />
                             </div></Tooltip>}
-                    <Tooltip title={<><div className="dash-tooltip">Open In a New Pane</div></>} placement="top"><div className="documentDecorations-openInTab" onPointerDown={this.onMaximizeDown}>
+                    <Tooltip title={<div className="dash-tooltip">Open In a New Pane</div>} placement="top"><div className="documentDecorations-openInTab" onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}>
                         {SelectionManager.SelectedDocuments().length === 1 ? <FontAwesomeIcon icon="external-link-alt" className="documentView-minimizedIcon" /> : "..."}
                     </div></Tooltip>
-                    <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-topResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-leftResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-centerCont"></div>
-                    <div id="documentDecorations-rightResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                    <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
-                        onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-leftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-centerCont"></div>
+                    <div className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                    <div className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
                     {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
-                        <Tooltip title={<><div className="dash-tooltip">tap to select containing document</div></>} placement="top">
-                            <div id="documentDecorations-levelSelector" className="documentDecorations-selector"
+                        <Tooltip title={<div className="dash-tooltip">tap to select containing document</div>} placement="top">
+                            <div className="documentDecorations-levelSelector"
                                 onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
                                 <FontAwesomeIcon className="documentdecorations-times" icon={"arrow-alt-circle-up"} size="lg" />
                             </div></Tooltip>}
-                    <div id={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
+                    <div className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
                         onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}>{useRotation && "⟲"}</div>
 
                 </div >
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7b0c6fa9f..83d9a3498 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -13,7 +13,7 @@ import { Doc, DocListCast, Field, Opt } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { listSpec } from '../../fields/Schema';
-import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
 import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
@@ -75,10 +75,10 @@ export class MainView extends React.Component {
     @observable private _panelHeight: number = 0;
     @observable private _flyoutTranslate: boolean = false;
     @observable public flyoutWidth: number = 0;
-    private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeDashboard, Doc, null)?.darkScheme); }
+    private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
 
     @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeDashboard, Doc)) : CurrentUserUtils.GuestDashboard; }
+    @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
     @computed public get searchDoc() { return Cast(this.userDoc.mySearchPanelDoc, Doc) as Doc; }
 
@@ -226,8 +226,7 @@ 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,
+            reaction(() => CurrentUserUtils.GuestTarget,
                 target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()),
                 { fireImmediately: true }
             );
@@ -241,11 +240,11 @@ export class MainView extends React.Component {
                     }),
                 );
             }
-            const doc = this.userDoc && await Cast(this.userDoc.activeDashboard, Doc);
-            if (doc) {
-                CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            const activeDash = PromiseValue(this.userDoc.activeDashboard);
+            if (activeDash) {
+                activeDash.then(dash => dash instanceof Doc && CurrentUserUtils.openDashboard(this.userDoc, dash));
             } else {
-                CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+                CurrentUserUtils.createNewDashboard(this.userDoc);
             }
         }
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 62c865436..93e77e0a2 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -140,6 +140,13 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
         return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
     }
 
+
+    @undoBatch
+    public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) {
+        return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ?
+            CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName);
+    }
+
     //
     //  Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side
     //
@@ -262,8 +269,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 config => {
                     if (!this._goldenLayout || this._ignoreStateChange !== config) {
                         this.setupGoldenLayout();
-                        DocListCast(CurrentUserUtils.MyDashboards.data).map(d => d.dashboardBrush = false);
-                        this.props.Document.dashboardBrush = true;
                     }
                     this._ignoreStateChange = "";
                 });
@@ -274,7 +279,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
 
     componentWillUnmount: () => void = () => {
         try {
-            this.props.Document.dashboardBrush = false;
             this._goldenLayout.unbind('stackCreated', this.stackCreated);
             this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
         } catch (e) { }
@@ -619,8 +623,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
             case "inPlace":
             case "add":
-            default: return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ?
-                CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, locationParams, this.stack);
+            default: return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
         }
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 80a6afba7..84fd4cbe8 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -483,7 +483,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 style={{
                     fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
                     textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
-                    outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined,
+                    outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
                     pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
                 }} >
                 {view}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 64ad3abbf..81403de46 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -583,7 +583,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
             ChildLayoutString: this.childLayoutString,
         };
         const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document._isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined :
-            `${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
+            `${CurrentUserUtils.ActiveDashboard?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`;
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
             style={{ pointerEvents: this.props.Document._isBackground ? "none" : undefined, boxShadow }}>
             {this.showIsTagged()}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 646ae94fb..bc2cb2d20 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -10,6 +10,7 @@ import { Id, ToString } from "../../../../fields/FieldSymbols";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RefField } from "../../../../fields/RefField";
 import { listSpec } from "../../../../fields/Schema";
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 
 export interface ViewDefBounds {
     type: string;
@@ -359,7 +360,7 @@ export function computeTimelineLayout(
         groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
-    const divider = { type: "div", color: Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+    const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
     return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index 4d8473be9..60ec02f47 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -134,17 +134,6 @@
 
 }
 
-// .documentDecorations-container .documentDecorations-resizer {
-//     pointer-events: none;
-// }
-
-// #documentDecorations-bottomRightResizer,
-// #documentDecorations-bottomLeftResizer,
-// #documentDecorations-topRightResizer,
-// #documentDecorations-topLeftResizer {
-//     visibility: collapse;
-// }
-
 
 /* Chrome, Safari, Edge, Opera */
 input::-webkit-outer-spin-button,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 48a1688a6..f4eb71145 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -39,6 +39,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup';
 import { RadialMenu } from './RadialMenu';
 import { TaskCompletionBox } from './TaskCompletedBox';
 import React = require("react");
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 
 export type DocFocusFunc = () => boolean;
 
@@ -588,7 +589,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     deleteClicked = (): void => {
-        if (Doc.UserDoc().activeDashboard === this.props.Document) {
+        if (CurrentUserUtils.ActiveDashboard === this.props.Document) {
             alert("Can't delete the active dashboard");
         } else {
             const selected = SelectionManager.SelectedDocuments().slice();
@@ -625,7 +626,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     @undoBatch @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
-        if (this.props.Document === Doc.UserDoc().activeDashboard) {
+        if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
             alert("linking to document tabs not yet supported.  Drop link on document content.");
             return;
         }
@@ -1041,7 +1042,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         const borderRounding = this.layoutDoc.borderRounding;
         const localScale = fullDegree;
-        const highlightColors = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ?
+        const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
             ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
         const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 08bab8d12..3b77735a7 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -12,6 +12,7 @@ import { DocumentView } from "../DocumentView";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { Transform } from "../../../util/Transform";
 import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 
 interface IDashDocView {
     node: any;
@@ -175,7 +176,7 @@ export class DashDocView extends React.Component<IDashDocView> {
         const outerStyle = {
             position: "relative" as "relative",
             textIndent: "0",
-            border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")),
+            border: "1px solid " + StrCast(this._textBox.Document.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")),
             width: this.props.node.props.width,
             height: this.props.node.props.height,
             display: this.props.node.props.hidden ? "none" : "inline-block",
@@ -202,7 +203,7 @@ export class DashDocView extends React.Component<IDashDocView> {
                 ({ dim, color }) => {
                     spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px";
                     spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px";
-                    outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+                    outerStyle.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
                 }, { fireImmediately: true });
 
             if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index b76e520b7..f0bacb735 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -13,6 +13,7 @@ import { Transform } from "../../../util/Transform";
 import { DocumentView } from "../DocumentView";
 import { FormattedTextBox } from "./FormattedTextBox";
 import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 
 
 export class DashDocView {
@@ -43,7 +44,7 @@ export class DashDocView {
         this._outer = document.createElement("span");
         this._outer.style.position = "relative";
         this._outer.style.textIndent = "0";
-        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+        this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
         this._outer.style.width = node.attrs.width;
         this._outer.style.height = node.attrs.height;
         this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
@@ -126,7 +127,7 @@ export class DashDocView {
             this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => {
                 this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
                 this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
-                this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+                this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
             }, { fireImmediately: true });
 
             const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index d7a8ed404..3926208ae 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -507,7 +507,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                     </div>
                     <div className="searchBox-lozenge-dashboard"  >
                         <select className="searchBox-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])}
-                            value={myDashboards.indexOf(Cast(Doc.UserDoc().activeDashboard, Doc, null))}>
+                            value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}>
                             {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
                         </select>
                         <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}>
-- 
cgit v1.2.3-70-g09d2

