From e6802ea58257710ba262076f8ff57d61b8fd9ec5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 19 Jun 2023 14:55:54 -0400
Subject: cleanup of unused fields.  fix to allow key value pane for
 collectionDocking view.

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

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e9cc2c894..1173f522e 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -174,9 +174,7 @@ export class CollectionDockingView extends CollectionSubView() {
 
     @undoBatch
     public static ToggleSplit(doc: Doc, location: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
-        return CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex(tab => tab.DashDoc === doc) !== -1
-            ? CollectionDockingView.CloseSplit(doc)
-            : CollectionDockingView.AddSplit(doc, location, stack, panelName, keyValue);
+        return Array.from(CollectionDockingView.Instance?.tabMap.keys() ?? []).findIndex(tab => tab.DashDoc === doc) !== -1 ? CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName, keyValue);
     }
 
     //
@@ -184,7 +182,7 @@ export class CollectionDockingView extends CollectionSubView() {
     //
     @action
     public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
-        if (document?._type_collection === CollectionViewType.Docking) return DashboardView.openDashboard(document);
+        if (document?._type_collection === CollectionViewType.Docking && !keyValue) return DashboardView.openDashboard(document);
         if (!CollectionDockingView.Instance) return false;
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !keyValue);
         if (tab) {
-- 
cgit v1.2.3-70-g09d2


From 1bc15f9b408d35639cea37ea1c29e7cdbd326301 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 19 Jun 2023 20:30:12 -0400
Subject: fixed not adding key value panes to header bar

---
 src/client/views/collections/CollectionDockingView.tsx | 2 +-
 src/client/views/nodes/trails/PresBox.tsx              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1173f522e..a5d7e7864 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -471,7 +471,7 @@ export class CollectionDockingView extends CollectionSubView() {
 
     tabDestroyed = (tab: any) => {
         this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
-        if (tab.DashDoc && ![DocumentType.KVP, DocumentType.PRES].includes(tab.DashDoc?.type)) {
+        if (tab.DashDoc && ![DocumentType.PRES].includes(tab.DashDoc?.type) && !tab.contentItem.config.props.keyValue) {
             Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
             Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
         }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 1e622e9ff..4f7d2e1c1 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -303,7 +303,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             targetView.docView._animateScalingTo = 1.2;
                             targetView.docView._animateScaleTime = 400;
                             Doc.AddUnHighlightWatcher(() => {
-                                targetView.docView._animateScaleTime = undefined;
+                                targetView.docView!._animateScaleTime = undefined;
                                 targetView!.docView!._animateScalingTo = 0;
                             });
                         }
-- 
cgit v1.2.3-70-g09d2


From c0d45c06df10d29cae5f46eeecb220a11588c3a1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 30 Jun 2023 23:43:59 -0400
Subject: fixed onBrowseClick for notetaking. fixed panning when in onBrowse
 explore mode.   fixed switching dashboards to not display an empty stack.

---
 src/client/util/DocumentManager.ts                 | 23 +++++++++++------
 src/client/views/DashboardView.tsx                 |  3 ++-
 .../views/collections/CollectionDockingView.tsx    |  1 +
 .../views/collections/CollectionNoteTakingView.tsx |  3 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 30 ++++++++++------------
 src/client/views/nodes/DocumentView.tsx            |  5 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 7 files changed, 38 insertions(+), 29 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7e3302067..351f9ef7c 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -228,7 +228,7 @@ export class DocumentManager {
     public showDocumentView = async (targetDocView: DocumentView, options: DocFocusOptions) => {
         const docViewPath = targetDocView.docViewPath.slice();
         let rootContextView = docViewPath.shift();
-        await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined })));
+        await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined, focused: false })));
         if (options.toggleTarget && (!options.didMove || targetDocView.rootDoc.hidden)) targetDocView.rootDoc.hidden = !targetDocView.rootDoc.hidden;
         else if (options.openLocation?.startsWith(OpenWhere.toggle) && !options.didMove && rootContextView) DocumentViewInternal.addDocTabFunc(rootContextView.rootDoc, options.openLocation);
     };
@@ -242,7 +242,7 @@ export class DocumentManager {
     public showDocument = async (
         targetDoc: Doc, // document to display
         options: DocFocusOptions, // options for how to navigate to target
-        finished?: () => void
+        finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
     ) => {
         const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
         if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
@@ -269,22 +269,29 @@ export class DocumentManager {
         docContextPath.shift();
         const childViewIterator = async (docView: DocumentView) => {
             const innerDoc = docContextPath.shift();
-            return { viewSpec: innerDoc, childDocView: innerDoc && !innerDoc.layout_unrendered ? (await docView.ComponentView?.getView?.(innerDoc)) ?? this.getDocumentView(innerDoc) : undefined };
+            return { focused: false, viewSpec: innerDoc, childDocView: innerDoc && !innerDoc.layout_unrendered ? (await docView.ComponentView?.getView?.(innerDoc)) ?? this.getDocumentView(innerDoc) : undefined };
         };
+
         if (rootContextView) {
             const target = await this.focusViewsInPath(rootContextView, options, childViewIterator);
             this.restoreDocView(target.viewSpec, target.docView, options, target.contextView ?? target.docView, targetDoc);
-        }
-        finished?.();
+            finished?.(target.focused);
+        } else finished?.(false);
     };
 
-    focusViewsInPath = async (docView: DocumentView, options: DocFocusOptions, iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt<Doc>; childDocView: Opt<DocumentView> }>) => {
+    focusViewsInPath = async (
+        docView: DocumentView, //
+        options: DocFocusOptions,
+        iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt<Doc>; childDocView: Opt<DocumentView>; focused: boolean }>
+    ) => {
         let contextView: DocumentView | undefined; // view containing context that contains target
+        let focused = false;
         while (true) {
             docView.rootDoc.layout_fieldKey === 'layout_icon' ? await new Promise<void>(res => docView.iconify(res)) : undefined;
-            docView.props.focus(docView.rootDoc, options); // focus the view within its container
+            const nextFocus = docView.props.focus(docView.rootDoc, options); // focus the view within its container
+            focused = focused || (nextFocus === undefined ? false : true);
             const { childDocView, viewSpec } = await iterator(docView);
-            if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.rootDoc, docView, contextView };
+            if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.rootDoc, docView, contextView, focused };
             contextView = docView;
             docView = childDocView;
         }
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 6aae302ac..f6d843201 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -347,7 +347,8 @@ export class DashboardView extends React.Component {
                 },
             ],
         };
-        Doc.SetInPlace(dashboard, 'dockingConfig', JSON.stringify(reset), true);
+        if (dashboard.dockingConfig && dashboard.dockingConfig !== Doc.GetProto(dashboard).dockingConfig) dashboard.dockingConfig = JSON.stringify(reset);
+        else Doc.SetInPlace(dashboard, 'dockingConfig', JSON.stringify(reset), true);
         return reset;
     };
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index a5d7e7864..3bf3f1a74 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -359,6 +359,7 @@ export class CollectionDockingView extends CollectionSubView() {
         } catch (e) {}
         this._goldenLayout?.destroy();
         window.removeEventListener('resize', this.onResize);
+        window.removeEventListener('mouseup', this.onPointerUp);
 
         this._reactionDisposer?.();
         this._lightboxReactionDisposer?.();
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index f1c4c2cdf..fb995301a 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -192,7 +192,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         if (found) {
             const top = found.getBoundingClientRect().top;
             const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
-            if (Math.floor(localTop[1]) !== 0) {
+            if (Math.floor(localTop[1]) !== 0 && Math.ceil(this.props.PanelHeight()) < (this._mainCont?.scrollHeight || 0)) {
                 let focusSpeed = options.zoomTime ?? 500;
                 smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
                 return focusSpeed;
@@ -252,6 +252,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 layout_showTitle={this.props.childlayout_showTitle}
                 dragAction={StrCast(this.layoutDoc.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
+                onBrowseClick={this.props.onBrowseClick}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={noteTakingDocTransform}
                 focus={this.focusDocument}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ef396c6b3..ba31916a7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -2256,22 +2256,20 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
 export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY: number) {
     const browseTransitionTime = 500;
     SelectionManager.DeselectAll();
-    if (
-        dv.props.focus(dv.props.Document, {
-            willZoomCentered: true,
-            zoomScale: 0.8,
-            zoomTime: browseTransitionTime,
-        }) === undefined
-    ) {
-        const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
-        let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
-        while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
-        const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it.  Otherwise, zoom out on the parent ffview
-        ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime);
-        Doc.linkFollowHighlight(dv?.props.Document, false);
-    } else {
-        DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true });
-    }
+    DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
+        if (!focused) {
+            const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
+            let containers = dv.props.docViewPath();
+            let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+            for (var cont of containers) {
+                parFfview = parFfview ?? cont.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+            }
+            while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+            const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it.  Otherwise, zoom out on the parent ffview
+            ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime);
+            Doc.linkFollowHighlight(dv?.props.Document, false);
+        }
+    });
 }
 ScriptingGlobals.add(CollectionBrowseClick);
 ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b6f1626f8..733ee1ed1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -900,8 +900,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     };
     childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
 
-    /// disable pointer events on content when there's an enabled onClick script, or if contents are marked inactive
-    contentPointerEvents = () => ((!this.disableClickScriptFunc && this.onClickHandler) || this.isContentActive() === false ? 'none' : this.pointerEvents);
+    /// disable pointer events on content when there's an enabled onClick script (but not the browse script), or if contents are marked inactive
+    contentPointerEvents = () => ((!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.()) || this.isContentActive() === false ? 'none' : this.pointerEvents);
+
     @computed get contents() {
         TraceMobx();
         const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index da0fc9ffb..026864ddd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2079,7 +2079,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     style={{
                         overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
                         height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
-                        pointerEvents: Doc.ActiveTool === InkTool.None ? undefined : 'none',
+                        pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick ? undefined : 'none',
                     }}
                     onContextMenu={this.specificContextMenu}
                     onKeyDown={this.onKeyDown}
-- 
cgit v1.2.3-70-g09d2


From 446ab35c19ae18a609acf26babbc4e6dc1c46876 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 2 Jul 2023 19:06:56 -0400
Subject: improved highlighting of tabs to show focus/active. added. menu item
 to open a tab in lightbox

---
 .../views/collections/CollectionDockingView.scss   |   6 +
 .../views/collections/CollectionDockingView.tsx    |  10 +-
 src/client/views/collections/TabDocView.tsx        | 144 +++++++++++----------
 src/client/views/nodes/DocumentView.tsx            |   4 +
 .../views/nodes/formattedText/FormattedTextBox.tsx |  11 +-
 5 files changed, 104 insertions(+), 71 deletions(-)

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

diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 78e44dfa2..4c15d5eed 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -15,6 +15,12 @@
     cursor: grab;
     color: $black;
 }
+.collectiondockingview-container .lm_splitter {
+    opacity: 0.2;
+    &:hover {
+        opacity: 1;
+    }
+}
 
 .lm_title.focus-visible {
     -webkit-appearance: none;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 3bf3f1a74..2ed55b3ca 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -29,6 +29,7 @@ import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { TabDocView } from './TabDocView';
 import React = require('react');
+import { DocumentManager } from '../../util/DocumentManager';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
@@ -410,7 +411,14 @@ export class CollectionDockingView extends CollectionSubView() {
             if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) {
                 const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : '';
                 if (className.includes('lm_maximise')) this._flush = UndoManager.StartBatch('tab maximize');
-                else if (!className.includes('lm_close')) DocServer.UPDATE_SERVER_CACHE();
+                else {
+                    const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement);
+                    const map = Array.from(this.tabMap).find(tab => tab.element[0] === tabTarget);
+                    if (map?.DashDoc && DocumentManager.Instance.getFirstDocumentView(map.DashDoc)) {
+                        SelectionManager.SelectView(DocumentManager.Instance.getFirstDocumentView(map.DashDoc), false);
+                    }
+                    if (!className.includes('lm_close')) DocServer.UPDATE_SERVER_CACHE();
+                }
             }
         }
         if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 118b216bf..83edc60df 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -43,12 +43,17 @@ interface TabDocViewProps {
 }
 @observer
 export class TabDocView extends React.Component<TabDocViewProps> {
+    static _firstTab = true;
     _mainCont: HTMLDivElement | null = null;
     _tabReaction: IReactionDisposer | undefined;
     @observable _activated: boolean = false;
     @observable _panelWidth = 0;
     @observable _panelHeight = 0;
     @observable _isActive: boolean = false;
+    @observable _isAnyChildContentActive = false;
+    @computed get _isContentActive() {
+        return SelectionManager.Views().some(view => view.rootDoc === this._document) || this._isAnyChildContentActive;
+    }
     @observable _document: Doc | undefined;
     @observable _view: DocumentView | undefined;
 
@@ -61,9 +66,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         return 'transparent';
     }
     @computed get tabColor() {
-        let tabColor = StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor)));
-        if (tabColor === 'transparent') return 'black';
-        return tabColor;
+        return this._isContentActive ? Colors.WHITE : Colors.MEDIUM_GRAY;
     }
     @computed get tabTextColor() {
         return this._document?.type === DocumentType.PRES ? 'black' : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color)));
@@ -191,7 +194,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 }
             });
             tab._disposers.selectionDisposer = reaction(
-                () => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc),
+                () => SelectionManager.Views().some(view => view.rootDoc === this._document),
                 action(selected => {
                     if (selected) this._activated = true;
                     const toggle = tab.element[0].children[2].children[0] as HTMLInputElement;
@@ -322,11 +325,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     }
     componentDidUpdate() {
         this._view && DocumentManager.Instance.AddView(this._view);
+        if (TabDocView._firstTab) SelectionManager.SelectView(this._view, false);
     }
 
     componentWillUnmount() {
         this._tabReaction?.();
         this._view && DocumentManager.Instance.RemoveView(this._view);
+        TabDocView._firstTab = false;
 
         this.props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged);
     }
@@ -405,11 +410,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     };
     PanelWidth = () => this._panelWidth;
     PanelHeight = () => this._panelHeight;
-    miniMapColor = () => this.tabColor;
+    miniMapColor = () => Colors.MEDIUM_GRAY;
     tabView = () => this._view;
     disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._type_collection !== CollectionViewType.Freeform;
-    hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.layout_hideMinimap);
-
+    whenChildContentActiveChanges = (isActive: boolean) => (this._isAnyChildContentActive = isActive);
+    tabs = () => Array.from(CollectionDockingView.Instance?.tabMap ?? new Set());
+    isContentActive = () =>
+        SelectionManager.Views().some(dv => dv.rootDoc === this._document) ||
+        this._isAnyChildContentActive ||
+        (this.tabs().findIndex(tab => tab.DashDoc === this._document) === 0 && !this.tabs().some(tab => SelectionManager.Views().some(dv => dv.rootDoc === tab.DashDoc)));
     @computed get docView() {
         return !this._activated || !this._document ? null : (
             <>
@@ -427,7 +436,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     DataDoc={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined}
                     onBrowseClick={MainView.Instance.exploreMode}
                     waitForDoubleClickToClick={MainView.Instance.waitForDoubleClick}
-                    isContentActive={returnTrue}
+                    isContentActive={this.isContentActive}
                     isDocumentActive={returnFalse}
                     PanelWidth={this.PanelWidth}
                     PanelHeight={this.PanelHeight}
@@ -441,30 +450,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     ScreenToLocalTransform={this.ScreenToLocalTransform}
                     dontCenter={'y'}
                     rootSelected={returnTrue}
-                    whenChildContentsActiveChanged={emptyFunction}
+                    whenChildContentsActiveChanged={this.whenChildContentActiveChanges}
                     focus={this.focusFunc}
                     docViewPath={returnEmptyDoclist}
                     bringToFront={emptyFunction}
                     pinToPres={TabDocView.PinDoc}
                 />
-                <TabMinimapView key="minimap" hideMinimap={this.hideMinimap} addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
-                <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.layout_hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}>
-                    <div
-                        className="miniMap-hidden"
-                        style={{
-                            display: this.disableMinimap() || this._document._type_collection !== 'freeform' ? 'none' : undefined,
-                            color: this._document.layout_hideMinimap ? Colors.BLACK : Colors.WHITE,
-                            backgroundColor: this._document.layout_hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
-                            boxShadow: this._document.layout_hideMinimap ? Shadows.STANDARD_SHADOW : undefined,
-                        }}
-                        onPointerDown={e => e.stopPropagation()}
-                        onClick={action(e => {
-                            e.stopPropagation();
-                            this._document!.layout_hideMinimap = !this._document!.layout_hideMinimap;
-                        })}>
-                        <FontAwesomeIcon icon={'globe-asia'} size="lg" />
-                    </div>
-                </Tooltip>
+                {this.disableMinimap() || this._document._type_collection !== CollectionViewType.Freeform ? null : (
+                    <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
+                )}
             </>
         );
     }
@@ -495,7 +489,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
 interface TabMinimapViewProps {
     document: Doc;
-    hideMinimap: () => boolean;
     tabView: () => DocumentView | undefined;
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
     PanelWidth: () => number;
@@ -573,43 +566,64 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
         const miniLeft = 50 + ((NumCast(this.props.document._freeform_panX) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2;
         const miniTop = 50 + ((NumCast(this.props.document._freeform_panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2;
         const miniSize = this.returnMiniSize();
-        return this.props.hideMinimap() ? null : (
-            <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
-                <CollectionFreeFormView
-                    Document={this.props.document}
-                    docViewPath={returnEmptyDoclist}
-                    childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
-                    noOverlay={true} // don't render overlay Docs since they won't scale
-                    setHeight={returnFalse}
-                    isContentActive={emptyFunction}
-                    isAnyChildContentActive={returnFalse}
-                    select={emptyFunction}
-                    isSelected={returnFalse}
-                    dontRegisterView={true}
-                    fieldKey={Doc.LayoutFieldKey(this.props.document)}
-                    bringToFront={emptyFunction}
-                    rootSelected={returnTrue}
-                    addDocument={returnFalse}
-                    moveDocument={returnFalse}
-                    removeDocument={returnFalse}
-                    PanelWidth={this.returnMiniSize}
-                    PanelHeight={this.returnMiniSize}
-                    ScreenToLocalTransform={Transform.Identity}
-                    renderDepth={0}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    focus={emptyFunction}
-                    styleProvider={TabMinimapView.miniStyleProvider}
-                    addDocTab={this.props.addDocTab}
-                    pinToPres={TabDocView.PinDoc}
-                    childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
-                    childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
-                    searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
-                    fitContentsToBox={returnTrue}
-                />
-                <div className="miniOverlay" onPointerDown={this.miniDown}>
-                    <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} />
-                </div>
-            </div>
+        return (
+            <>
+                {' '}
+                {this.props.document?.layout_hideMinimap ? null : (
+                    <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
+                        <CollectionFreeFormView
+                            Document={this.props.document}
+                            docViewPath={returnEmptyDoclist}
+                            childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
+                            noOverlay={true} // don't render overlay Docs since they won't scale
+                            setHeight={returnFalse}
+                            isContentActive={emptyFunction}
+                            isAnyChildContentActive={returnFalse}
+                            select={emptyFunction}
+                            isSelected={returnFalse}
+                            dontRegisterView={true}
+                            fieldKey={Doc.LayoutFieldKey(this.props.document)}
+                            bringToFront={emptyFunction}
+                            rootSelected={returnTrue}
+                            addDocument={returnFalse}
+                            moveDocument={returnFalse}
+                            removeDocument={returnFalse}
+                            PanelWidth={this.returnMiniSize}
+                            PanelHeight={this.returnMiniSize}
+                            ScreenToLocalTransform={Transform.Identity}
+                            renderDepth={0}
+                            whenChildContentsActiveChanged={emptyFunction}
+                            focus={emptyFunction}
+                            styleProvider={TabMinimapView.miniStyleProvider}
+                            addDocTab={this.props.addDocTab}
+                            pinToPres={TabDocView.PinDoc}
+                            childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
+                            childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+                            searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
+                            fitContentsToBox={returnTrue}
+                        />
+                        <div className="miniOverlay" onPointerDown={this.miniDown}>
+                            <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} />
+                        </div>
+                    </div>
+                )}
+                <Tooltip key="ttip" title={<div className="dash-tooltip">{this.props.document.layout_hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}>
+                    <div
+                        className="miniMap-hidden"
+                        style={{
+                            color: this.props.document.layout_hideMinimap ? Colors.BLACK : Colors.WHITE,
+                            backgroundColor: this.props.document.layout_hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
+                            boxShadow: this.props.document.layout_hideMinimap ? Shadows.STANDARD_SHADOW : undefined,
+                        }}
+                        onPointerDown={e => e.stopPropagation()}
+                        onClick={action(e => {
+                            e.stopPropagation();
+                            this.props.document!.layout_hideMinimap = !this.props.document!.layout_hideMinimap;
+                        })}>
+                        <FontAwesomeIcon icon="globe-asia" size="lg" />
+                    </div>
+                </Tooltip>
+            </>
         );
     }
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index c10c0005d..19f9f15a4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -729,6 +729,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layout_fieldKey)], Doc, null);
             const appearance = cm.findByDescription('UI Controls...');
             const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
+
+            if (this.props.renderDepth === 0) {
+                appearanceItems.push({ description: 'Open in Lightbox', event: () => LightboxView.SetLightboxDoc(this.rootDoc), icon: 'hand-point-right' });
+            }
             !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
             !appearance && appearanceItems.length && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 62b3443d4..f1305b64b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -879,11 +879,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         const optionItems = options && 'subitems' in options ? options.subitems : [];
         optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' });
         optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' });
-        optionItems.push({
-            description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
-            event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR),
-            icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars',
-        });
+        this.props.renderDepth &&
+            optionItems.push({
+                description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
+                event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR),
+                icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars',
+            });
         !Doc.noviceMode &&
             optionItems.push({
                 description: `${this.Document._layout_autoHeight ? 'Lock' : 'Auto'} Height`,
-- 
cgit v1.2.3-70-g09d2

