From 1bba63b1d15cfe76393424a768d2dbc0f0b8cffb Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 2 Nov 2023 10:55:43 -0400
Subject: cleaned up brushView to only apply to freeformviews that aren't
 overlays (wasn't being used properly. before anyway).  cleaned up marquee
 view divs.

---
 src/client/views/collections/CollectionView.tsx | 1 -
 1 file changed, 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ce19b3f9b..493f40d77 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -41,7 +41,6 @@ interface CollectionViewProps_ extends FieldViewProps {
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
     layoutEngine?: () => string;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => void;
-    setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => void;
     ignoreUnrendered?: boolean;
 
     // property overrides for child documents
-- 
cgit v1.2.3-70-g09d2


From eec81f7e0b53395e3e2ea25663a9ea06ec83085d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 2 Nov 2023 19:30:19 -0400
Subject: performance fixes - don't invalidate as much by using reactions in
 place of computd values;  don't make things active when things are dragged
 unless CanEmbed; fix for linkBox to use reaction.

---
 src/client/util/DragManager.ts                     |   7 +-
 src/client/util/SnappingManager.ts                 |   7 ++
 src/client/views/collections/CollectionSubView.tsx |  18 ++--
 src/client/views/collections/CollectionView.tsx    |  21 +++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 107 +++++++++++++--------
 src/client/views/nodes/DocumentLinksButton.tsx     |   2 +
 src/client/views/nodes/DocumentView.tsx            |  62 ++++++++----
 src/client/views/nodes/LinkBox.tsx                 |  90 ++++++++++-------
 src/client/views/nodes/WebBox.tsx                  |  77 ++++++++-------
 src/client/views/pdf/PDFViewer.tsx                 |   4 +-
 10 files changed, 248 insertions(+), 147 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ea13eaa5b..9d6bd4f60 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -329,7 +329,7 @@ export namespace DragManager {
         DocDragData = dragData as DocumentDragData;
         const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag');
         eles = eles.filter(e => e);
-        CanEmbed = dragData.canEmbed || false;
+        SnappingManager.SetCanEmbed(dragData.canEmbed || false);
         if (!dragDiv) {
             dragDiv = document.createElement('div');
             dragDiv.className = 'dragManager-dragDiv';
@@ -455,7 +455,7 @@ export namespace DragManager {
         runInAction(() => docsBeingDragged.push(...docsToDrag));
 
         const hideDragShowOriginalElements = (hide: boolean) => {
-            dragLabel.style.display = hide && !CanEmbed ? '' : 'none';
+            dragLabel.style.display = hide && !SnappingManager.GetCanEmbed() ? '' : 'none';
             !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
             setTimeout(() => eles.forEach(ele => (ele.hidden = hide)));
         };
@@ -482,6 +482,7 @@ export namespace DragManager {
             SnappingManager.SetIsDragging(false);
             if (batch.end() && undo) UndoManager.Undo();
             docsBeingDragged.length = 0;
+            SnappingManager.SetCanEmbed(false);
         });
         var startWindowDragTimer: any;
         const moveHandler = (e: PointerEvent) => {
@@ -588,7 +589,7 @@ export namespace DragManager {
                 altKey: e.altKey,
                 metaKey: e.metaKey,
                 ctrlKey: e.ctrlKey,
-                embedKey: CanEmbed,
+                embedKey: SnappingManager.GetCanEmbed(),
             },
         };
         target.dispatchEvent(new CustomEvent<DropEvent>('dashPreDrop', dropArgs));
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 3cb41ab4d..c0cd94067 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -5,6 +5,7 @@ export namespace SnappingManager {
     class Manager {
         @observable IsDragging: boolean = false;
         @observable IsResizing: Doc | undefined;
+        @observable CanEmbed: boolean = false;
         @observable public horizSnapLines: number[] = [];
         @observable public vertSnapLines: number[] = [];
         @action public clearSnapLines() {
@@ -38,10 +39,16 @@ export namespace SnappingManager {
     export function SetIsResizing(doc: Doc | undefined) {
         runInAction(() => (manager.IsResizing = doc));
     }
+    export function SetCanEmbed(canEmbed: boolean) {
+        runInAction(() => (manager.CanEmbed = canEmbed));
+    }
     export function GetIsDragging() {
         return manager.IsDragging;
     }
     export function GetIsResizing() {
         return manager.IsResizing;
     }
+    export function GetCanEmbed() {
+        return manager.CanEmbed;
+    }
 }
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 09e7cdb32..8a1ba0df1 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -8,14 +8,22 @@ import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
+import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
 import { returnFalse, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
+import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { InteractionUtils } from '../../util/InteractionUtils';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { DocComponent } from '../DocComponent';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { CollectionView, CollectionViewProps } from './CollectionView';
 import React = require('react');
 
 export interface SubCollectionViewProps extends CollectionViewProps {
@@ -118,7 +126,7 @@ export function CollectionSubView<X>(moreProps?: X) {
             childDocs.forEach(d => {
                 // dragging facets
                 const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragsDocFilter));
-                if (dragged && DragManager.docsBeingDragged.includes(d)) return false;
+                if (dragged && SnappingManager.GetCanEmbed() && DragManager.docsBeingDragged.includes(d)) return false;
                 let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.props.Document).length > 0;
                 if (notFiltered) {
                     notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.props.Document).length > 0;
@@ -486,11 +494,3 @@ export function CollectionSubView<X>(moreProps?: X) {
 
     return CollectionSubView;
 }
-
-import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { DragManager, dropActionType } from '../../util/DragManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { CollectionView, CollectionViewProps } from './CollectionView';
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 493f40d77..6e4b9ec07 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import { computed, observable, runInAction } from 'mobx';
+import { computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
@@ -78,6 +78,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     public static SetSafeMode(safeMode: boolean) {
         this._safeMode = safeMode;
     }
+    private reactionDisposer: IReactionDisposer | undefined;
+    @observable _isContentActive: boolean | undefined;
 
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
@@ -86,6 +88,21 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         runInAction(() => (this._annotationKeySuffix = returnEmptyString));
     }
 
+    componentDidMount() {
+        // we use a reaction/observable instead of a computed value to reduce invalidations.
+        // There are many variables that aggregate into this boolean output - a change in any of them
+        // will cause downstream invalidations even if the computed value doesn't change.  By making
+        // this a reaction, downstream invalidations only occur when the reaction value actually changes.
+        this.reactionDisposer = reaction(
+            () => (this.isAnyChildContentActive() ? true : this.props.isContentActive()),
+            active => (this._isContentActive = active),
+            { fireImmediately: true }
+        );
+    }
+    componentWillUnmount() {
+        this.reactionDisposer?.();
+    }
+
     get collectionViewType(): CollectionViewType | undefined {
         const viewField = StrCast(this.layoutDoc._type_collection);
         if (CollectionView._safeMode) {
@@ -220,7 +237,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
     childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
-    isContentActive = (outsideReaction?: boolean) => (this.isAnyChildContentActive() ? true : this.props.isContentActive());
+    isContentActive = (outsideReaction?: boolean) => this._isContentActive;
 
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d80ea2cb2..2897cac0e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1280,12 +1280,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
     @computed get childPointerEvents() {
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
-        const pointerEvents = DocumentView.Interacting
+        const pointerevents = DocumentView.Interacting
             ? 'none'
-            : this.props.childPointerEvents?.() ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
-        return pointerEvents;
+            : this.props.childPointerEvents?.() ??
+              (this.props.viewDefDivClick || //
+              (engine === computePassLayout.name && !this.props.isSelected(true)) ||
+              this.isContentActive() === false
+                  ? 'none'
+                  : this.props.pointerEvents?.());
+        console.log(`${this.rootDoc.title} pe = ` + pointerevents);
+        return pointerevents;
     }
-    childPointerEventsFunc = () => this.childPointerEvents;
+
+    @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined;
+    childPointerEventsFunc = () => this._childPointerEvents;
     childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
     getChildDocView(entry: PoolData) {
         const childLayout = entry.pair.layout;
@@ -1372,9 +1380,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     });
     @observable _lightboxDoc: Opt<Doc>;
 
-    getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
+    getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
         const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
-        const childDoc = params.pair.layout;
+        const childDoc = pair.layout;
         const childDocLayout = Doc.Layout(childDoc);
         const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values
         const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
@@ -1400,7 +1408,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             width: _width,
             height: _height,
             transition: StrCast(childDocLayout.dataTransition),
-            pair: params.pair,
+            pair,
             pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
             replica: '',
         };
@@ -1480,7 +1488,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     doFreeformLayout(poolData: Map<string, PoolData>) {
-        this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document })));
+        this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions(pair)));
         return [] as ViewDefResult[];
     }
 
@@ -1502,38 +1510,39 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @observable _numLoaded = 1;
     _lastPoolSize = 0;
-    get doLayoutComputation() {
-        const { newPool, computedElementData } = this.doInternalLayoutComputation;
+    @action
+    doLayoutComputation = (newPool: Map<string, PoolData>, computedElementData: ViewDefResult[]) => {
         const array = Array.from(newPool.entries());
-        let somethingChanged = array.length !== this._lastPoolSize;
         this._lastPoolSize = array.length;
-        runInAction(() => {
-            for (const entry of array) {
-                const lastPos = this._cachedPool.get(entry[0]); // last computed pos
-                const newPos = entry[1];
-                if (
-                    !lastPos ||
-                    newPos.color !== lastPos.color ||
-                    newPos.backgroundColor !== lastPos.backgroundColor ||
-                    newPos.opacity !== lastPos.opacity ||
-                    newPos.x !== lastPos.x ||
-                    newPos.y !== lastPos.y ||
-                    newPos.z !== lastPos.z ||
-                    newPos.rotation !== lastPos.rotation ||
-                    newPos.zIndex !== lastPos.zIndex ||
-                    newPos.transition !== lastPos.transition ||
-                    newPos.pointerEvents !== lastPos.pointerEvents
-                ) {
-                    this._layoutPoolData.set(entry[0], newPos);
-                    somethingChanged = true;
-                }
-                if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
-                    this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
-                    somethingChanged = true;
-                }
+        for (const entry of array) {
+            const lastPos = this._cachedPool.get(entry[0]); // last computed pos
+            const newPos = entry[1];
+            if (
+                !lastPos ||
+                newPos.color !== lastPos.color ||
+                newPos.backgroundColor !== lastPos.backgroundColor ||
+                newPos.opacity !== lastPos.opacity ||
+                newPos.x !== lastPos.x ||
+                newPos.y !== lastPos.y ||
+                newPos.z !== lastPos.z ||
+                newPos.rotation !== lastPos.rotation ||
+                newPos.zIndex !== lastPos.zIndex ||
+                newPos.transition !== lastPos.transition ||
+                newPos.pointerEvents !== lastPos.pointerEvents
+            ) {
+                this._layoutPoolData.set(entry[0], newPos);
             }
-        });
-        if (!somethingChanged) return undefined;
+            if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
+                this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
+            }
+        }
+        // by returning undefined, we prevent an edit being made to layoutElements when nothing has happened
+        // this short circuit, prevents lots of downstream mobx invalidations which would have no effect but cause
+        // a distinct lag at the start of dragging.
+        // The reason we're here in the first place without a change is that when dragging a document,
+        // filters are changed on the annotation layers (eg. WebBox) which invalidate the childDoc list
+        // for the overlay views -- however, in many cases, this filter change doesn't actually affect anything
+        // (e.g, no annotations, or only opaque annotations).
         this._cachedPool.clear();
         Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
         const elements = computedElementData.slice();
@@ -1551,7 +1560,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
         return elements;
-    }
+    };
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         // create an anchor that saves information about the current state of the freeform view (pan, zoom, view type)
@@ -1605,10 +1614,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     { fireImmediately: true }
                 );
 
+                this._disposers.pointerevents = reaction(
+                    () => {
+                        const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
+                        return DocumentView.Interacting
+                            ? 'none'
+                            : this.props.childPointerEvents?.() ??
+                                  (this.props.viewDefDivClick || //
+                                  (engine === computePassLayout.name && !this.props.isSelected(true)) ||
+                                  this.isContentActive() === false
+                                      ? 'none'
+                                      : this.props.pointerEvents?.());
+                    },
+                    pointerevents => (this._childPointerEvents = pointerevents as any),
+                    { fireImmediately: true }
+                );
+
                 this._disposers.layoutComputation = reaction(
-                    () => this.doLayoutComputation,
-                    elements => elements !== undefined && (this._layoutElements = elements || []),
-                    { fireImmediately: true, name: 'doLayout' }
+                    () => this.doInternalLayoutComputation,
+                    ({ newPool, computedElementData }) => (this._layoutElements = this.doLayoutComputation(newPool, computedElementData)),
+                    { fireImmediately: true, name: 'layoutComputationReaction' }
                 );
 
                 this._disposers.active = reaction(
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 4db0bf5fa..a26d2e9f3 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -27,6 +27,7 @@ interface DocumentLinksButtonProps {
     StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
     ShowCount?: boolean;
     scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
+    hideCount?: () => boolean;
 }
 @observer
 export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
@@ -239,6 +240,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     }
 
     render() {
+        if (this.props.hideCount?.()) return null;
         const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
         const buttonTitle = 'Tap to view links; double tap to open link collection';
         const title = this.props.ShowCount ? buttonTitle : menuTitle;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3d6b53ccc..30d5a5184 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,6 +1,6 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { Dropdown, DropdownType, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
@@ -136,7 +136,7 @@ export interface DocComponentView {
     componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
     incrementalRendering?: () => void;
     layout_fitWidth?: () => boolean; // whether the component always fits width (eg, KeyValueBox)
-    overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document
+    overridePointerEvents?: () => 'all' | 'none' | undefined; // if the conmponent overrides the pointer events for the document (e.g, KeyValueBox always allows pointer events)
     fieldKey?: string;
     annotationKey?: string;
     getTitle?: () => string;
@@ -272,8 +272,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this._animateScaleTime ?? 100;
     }
     public get displayName() {
-        return 'DocumentView(' + this.props.Document.title + ')';
+        return 'DocumentViewInternal(' + this.props.Document.title + ')';
     } // this makes mobx trace() statements more descriptive
+
     public get ContentDiv() {
         return this._mainCont.current;
     }
@@ -317,8 +318,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get titleHeight() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
     }
+    @observable _pointerEvents: 'none' | 'all' | 'visiblePainted' | undefined;
     @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
-        return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents);
+        TraceMobx();
+        return this._pointerEvents;
     }
     @computed get finalLayoutKey() {
         return StrCast(this.Document.layout_fieldKey, 'layout');
@@ -330,6 +333,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this.props.NativeHeight();
     }
     @computed get disableClickScriptFunc() {
+        TraceMobx();
         const onScriptDisable = this.props.onClickScriptDisable ?? this._componentView?.onClickScriptDisable?.() ?? this.layoutDoc.onClickScriptDisable;
         // prettier-ignore
         return (
@@ -356,6 +360,25 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     componentDidMount() {
         this.setupHandlers();
+        this._disposers.contentActive = reaction(
+            () => {
+                //  true  - if the document has been activated directly or indirectly (by having its children selected)
+                //  false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive
+                // undefined - it is not active, but it should be responsive to actions that might activate it or its contents (eg clicking)
+                return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none'
+                    ? false
+                    : Doc.ActiveTool !== InkTool.None || SnappingManager.GetCanEmbed() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()
+                    ? true
+                    : undefined;
+            },
+            active => (this._isContentActive = active),
+            { fireImmediately: true }
+        );
+        this._disposers.pointerevents = reaction(
+            () => this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents),
+            pointerevents => (this._pointerEvents = pointerevents),
+            { fireImmediately: true }
+        );
     }
     preDropFunc = (e: Event, de: DragManager.DropEvent) => {
         const dropAction = this.layoutDoc.dropAction as dropActionType;
@@ -883,23 +906,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
     setHeight = (height: number) => (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
-    @computed get _isContentActive() {
-        //  true  - if the document has been activated directly or indirectly (by having its children selected)
-        //  false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive
-        // undefined - it is not active, but it should be responsive to actions that might active it or its contents (eg clicking)
-        return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none'
-            ? false
-            : Doc.ActiveTool !== InkTool.None || SnappingManager.GetIsDragging() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive()
-            ? true
-            : undefined;
-    }
+    @observable _isContentActive: boolean | undefined;
+
     isContentActive = (): boolean | undefined => this._isContentActive;
     childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
 
     /// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive
     @computed get _contentPointerEvents() {
-        if (this.props.contentPointerEvents) return this.props.contentPointerEvents;
-        return (!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.() && this.isContentActive() !== true) || this.isContentActive() === false ? 'none' : this.pointerEvents;
+        TraceMobx();
+        return this.props.contentPointerEvents ??
+            ((!this.disableClickScriptFunc && //
+                this.onClickHandler &&
+                !this.props.onBrowseClick?.() &&
+                this.isContentActive() !== true) ||
+                this.isContentActive() === false)
+            ? 'none'
+            : this.pointerEvents;
     }
     contentPointerEvents = () => this._contentPointerEvents;
     @computed get contents() {
@@ -1304,8 +1326,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 onContextMenu={this.onContextMenu}
                 onPointerDown={this.onPointerDown}
                 onClick={this.onClick}
-                onPointerEnter={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)}
-                onPointerOver={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)}
+                onPointerEnter={e => (!SnappingManager.GetIsDragging() || SnappingManager.GetCanEmbed()) && Doc.BrushDoc(this.rootDoc)}
+                onPointerOver={e => (!SnappingManager.GetIsDragging() || SnappingManager.GetCanEmbed()) && Doc.BrushDoc(this.rootDoc)}
                 onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.rootDoc)}
                 style={{
                     borderRadius: this.borderRounding,
@@ -1426,9 +1448,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get hideLinkButton() {
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkBtn + (this.isSelected() ? ':selected' : ''));
     }
+    hideLinkCount = () => this.props.renderDepth === -1 || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton;
     @computed get linkCountView() {
-        const hideCount = this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (this.isSelected() && this.props.renderDepth) || !this._isHovering || this.hideLinkButton;
-        return hideCount ? null : <DocumentLinksButton View={this} scaling={this.scaleToScreenSpace} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
+        return <DocumentLinksButton hideCount={this.hideLinkCount} View={this} scaling={this.scaleToScreenSpace} OnHover={true} Bottom={this.topMost} ShowCount={true} />;
     }
     @computed get docViewPath(): DocumentView[] {
         return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this];
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 38ff21209..e66fed84b 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,19 +1,19 @@
 import React = require('react');
 import { Bezier } from 'bezier-js';
-import { computed, action } from 'mobx';
+import { computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils';
 import { DocumentManager } from '../../util/DocumentManager';
+import { Transform } from '../../util/Transform';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
 import { ComparisonBox } from './ComparisonBox';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LinkBox.scss';
-import { CollectionFreeFormView } from '../collections/collectionFreeForm';
-import { Transform } from '../../util/Transform';
 
 @observer
 export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -22,9 +22,6 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
 
     onClickScriptDisable = returnAlways;
-    componentDidMount() {
-        this.props.setContentView?.(this);
-    }
     @computed get anchor1() {
         const anchor1 = DocCast(this.rootDoc.link_anchor_1);
         const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1;
@@ -56,45 +53,70 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         return { left: 0, top: 0, right: 0, bottom: 0, center: undefined };
     };
-    render() {
-        if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
-            const a = (this.anchor1 ?? this.anchor2)!;
-            const b = (this.anchor2 ?? this.anchor1)!;
-
-            const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView;
-            const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform();
-            const a_invXf = a.props.ScreenToLocalTransform().inverse();
-            const b_invXf = b.props.ScreenToLocalTransform().inverse();
-            const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) };
-            const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) };
-            const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) };
-            const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) };
+    disposer: IReactionDisposer | undefined;
+    componentDidMount() {
+        this.props.setContentView?.(this);
+        this.disposer = reaction(
+            () => {
+                if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
+                    const a = (this.anchor1 ?? this.anchor2)!;
+                    const b = (this.anchor2 ?? this.anchor1)!;
 
-            const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2];
-            const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2);
-            const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2);
-            const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2];
+                    const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView;
+                    const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform();
+                    const a_invXf = a.props.ScreenToLocalTransform().inverse();
+                    const b_invXf = b.props.ScreenToLocalTransform().inverse();
+                    const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) };
+                    const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) };
+                    const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) };
+                    const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) };
 
-            const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]);
-            const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])];
-            setTimeout(
-                action(() => {
-                    this.layoutDoc.x = lx;
-                    this.layoutDoc.y = ty;
-                    this.layoutDoc._width = rx - lx;
-                    this.layoutDoc._height = by - ty;
-                })
-            );
+                    const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2];
+                    const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2);
+                    const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2);
+                    const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2];
 
+                    const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]);
+                    const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])];
+                    return { pts, lx, rx, ty, by };
+                }
+                return undefined;
+            },
+            params => {
+                this.renderProps = params;
+                if (params) {
+                    if (
+                        Math.abs(params.lx - NumCast(this.layoutDoc.x)) > 1e-5 ||
+                        Math.abs(params.ty - NumCast(this.layoutDoc.y)) > 1e-5 ||
+                        Math.abs(params.rx - params.lx - NumCast(this.layoutDoc._width)) > 1e-5 ||
+                        Math.abs(params.by - params.ty - NumCast(this.layoutDoc._height)) > 1e-5
+                    ) {
+                        this.layoutDoc.x = params?.lx;
+                        this.layoutDoc.y = params?.ty;
+                        this.layoutDoc._width = params.rx - params?.lx;
+                        this.layoutDoc._height = params?.by - params?.ty;
+                    }
+                }
+            },
+            { fireImmediately: true }
+        );
+    }
+    componentWillUnmount(): void {
+        this.disposer?.();
+    }
+    @observable renderProps: { lx: number; rx: number; ty: number; by: number; pts: number[][] } | undefined;
+    render() {
+        if (this.renderProps) {
             const highlight = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting);
             const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined;
 
-            const bez = new Bezier(pts.map(p => ({ x: p[0], y: p[1] })));
+            const bez = new Bezier(this.renderProps.pts.map(p => ({ x: p[0], y: p[1] })));
             const text = bez.get(0.5);
             const linkDesc = StrCast(this.rootDoc.link_description) || 'description';
             const strokeWidth = NumCast(this.rootDoc.stroke_width, 4);
             const dash = StrCast(this.rootDoc.stroke_dash);
             const strokeDasharray = dash && Number(dash) ? String(strokeWidth * Number(dash)) : undefined;
+            const { pts, lx, ty, rx, by } = this.renderProps;
             return (
                 <div style={{ transition: 'inherit', pointerEvents: 'none', position: 'absolute', width: '100%', height: '100%' }}>
                     <svg width={Math.max(100, rx - lx)} height={Math.max(100, by - ty)} style={{ transition: 'inherit', overflow: 'visible' }}>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 66e0ed21f..a94279e88 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -861,7 +861,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 key="sidebar"
                 title="Toggle Sidebar"
                 style={{
-                    display: !this.props.isContentActive() ? 'none' : undefined,
                     top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
                     backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                 }}
@@ -950,42 +949,48 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get SidebarShown() {
         return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
+    renderAnnotations = (childFilters: () => string[]) => (
+        <CollectionFreeFormView
+            {...this.props}
+            setContentView={this.setInnerContent}
+            NativeWidth={returnZero}
+            NativeHeight={returnZero}
+            originTopLeft={false}
+            isAnnotationOverlayScrollable={true}
+            renderDepth={this.props.renderDepth + 1}
+            isAnnotationOverlay={true}
+            fieldKey={this.annotationKey}
+            setPreviewCursor={this.setPreviewCursor}
+            PanelWidth={this.panelWidth}
+            PanelHeight={this.panelHeight}
+            ScreenToLocalTransform={this.scrollXf}
+            NativeDimScaling={returnOne}
+            focus={this.focus}
+            childFilters={childFilters}
+            select={emptyFunction}
+            isAnyChildContentActive={returnFalse}
+            bringToFront={emptyFunction}
+            styleProvider={this.childStyleProvider}
+            whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+            removeDocument={this.removeDocument}
+            moveDocument={this.moveDocument}
+            addDocument={this.addDocumentWrapper}
+            childPointerEvents={this.childPointerEvents}
+            pointerEvents={this.annotationPointerEvents}
+        />
+    );
 
+    @computed get renderOpaqueAnnotations() {
+        return this.renderAnnotations(this.opaqueFilter);
+    }
+    @computed get renderTransparentAnnotations() {
+        return this.renderAnnotations(this.transparentFilter);
+    }
     childPointerEvents = () => (this.props.isContentActive() ? 'all' : undefined);
     @computed get webpage() {
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const scale = previewScale * (this.props.NativeDimScaling?.() || 1);
-        const renderAnnotations = (childFilters: () => string[]) => (
-            <CollectionFreeFormView
-                {...this.props}
-                setContentView={this.setInnerContent}
-                NativeWidth={returnZero}
-                NativeHeight={returnZero}
-                originTopLeft={false}
-                isAnnotationOverlayScrollable={true}
-                renderDepth={this.props.renderDepth + 1}
-                isAnnotationOverlay={true}
-                fieldKey={this.annotationKey}
-                setPreviewCursor={this.setPreviewCursor}
-                PanelWidth={this.panelWidth}
-                PanelHeight={this.panelHeight}
-                ScreenToLocalTransform={this.scrollXf}
-                NativeDimScaling={returnOne}
-                focus={this.focus}
-                childFilters={childFilters}
-                select={emptyFunction}
-                isAnyChildContentActive={returnFalse}
-                bringToFront={emptyFunction}
-                styleProvider={this.childStyleProvider}
-                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                removeDocument={this.removeDocument}
-                moveDocument={this.moveDocument}
-                addDocument={this.addDocumentWrapper}
-                childPointerEvents={this.childPointerEvents}
-                pointerEvents={this.annotationPointerEvents}
-            />
-        );
         return (
             <div
                 className="webBox-outerContent"
@@ -1000,8 +1005,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 onPointerDown={this.onMarqueeDown}>
                 <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight) || '100%', pointerEvents }}>
                     {this.content}
-                    {<div style={{ display: DragManager.docsBeingDragged.length ? 'none' : undefined, mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>}
-                    {renderAnnotations(this.opaqueFilter)}
+                    <div style={{ display: SnappingManager.GetCanEmbed() ? 'none' : undefined, mixBlendMode: 'multiply' }}>{this.renderTransparentAnnotations}</div>
+                    {this.renderOpaqueAnnotations}
                     {this.annotationLayer}
                 </div>
             </div>
@@ -1050,7 +1055,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
+    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.IsOpaqueFilter()])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (this.inlineTextAnnotations.includes(doc)) return 'none';
@@ -1131,8 +1136,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         removeDocument={this.removeDocument}
                     />
                 </div>
-                {this.sidebarHandle}
-                {!this.props.isContentActive() ? null : this.searchUI}
+                {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.sidebarHandle}
+                {!this.props.isContentActive() || SnappingManager.GetIsDragging() ? null : this.searchUI}
             </div>
         );
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 2e494aa45..712a5e92f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -512,7 +512,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])];
+    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
             if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none';
@@ -557,7 +557,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     );
     @computed get overlayTransparentAnnotations() {
         const transparentChildren = DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.transparentFilter(), []);
-        return !transparentChildren.length ? null : this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length && this.props.isContentActive() ? 'none' : undefined);
+        return !transparentChildren.length ? null : this.renderAnnotations(this.transparentFilter, 'multiply', SnappingManager.GetCanEmbed() && this.props.isContentActive() ? 'none' : undefined);
     }
     @computed get overlayOpaqueAnnotations() {
         return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined);
-- 
cgit v1.2.3-70-g09d2


From a4e3b645317c4589cf49f8007f6e6b57cf2c12d3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 4 Nov 2023 23:29:56 -0400
Subject: cleanup

---
 src/client/util/DragManager.ts                           |  2 +-
 src/client/views/collections/CollectionMenu.tsx          | 16 ++++++++--------
 src/client/views/collections/CollectionView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx        |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx       |  2 +-
 src/client/views/nodes/DocumentLinksButton.tsx           |  6 +++---
 src/client/views/nodes/DocumentView.tsx                  |  6 +++---
 src/client/views/nodes/KeyValueBox.tsx                   |  2 +-
 src/client/views/nodes/WebBox.tsx                        |  3 +--
 src/client/views/nodes/generativeFill/GenerativeFill.tsx |  4 ++--
 src/client/views/pdf/PDFViewer.tsx                       |  6 ++----
 11 files changed, 24 insertions(+), 27 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9d6bd4f60..8d8975763 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,7 +4,7 @@ import { Doc, Field, Opt, StrListCast } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, ScriptCast, StrCast } from '../../fields/Types';
+import { ScriptCast, StrCast } from '../../fields/Types';
 import { emptyFunction, Utils } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import * as globalCssVariables from '../views/global/globalCssVariables.scss';
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index ec9d86c1a..52cf40635 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -3,11 +3,11 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { Toggle, ToggleType, Type } from 'browndash-components';
-import { Lambda, action, computed, observable, reaction, runInAction } from 'mobx';
+import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState } from 'react-color';
-import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Document } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
@@ -15,10 +15,10 @@ import { ObjectField } from '../../../fields/ObjectField';
 import { RichTextField } from '../../../fields/RichTextField';
 import { listSpec } from '../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { Document } from '../../../fields/documentSchemas';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -31,17 +31,17 @@ import { GestureOverlay } from '../GestureOverlay';
 import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
 import { LightboxView } from '../LightboxView';
 import { MainView } from '../MainView';
-import { DefaultStyleProvider } from '../StyleProvider';
+import { media_state } from '../nodes/AudioBox';
 import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
 import { DocumentView, DocumentViewInternal, OpenWhereMod } from '../nodes/DocumentView';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { DefaultStyleProvider } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
+import { CollectionFreeFormView } from './collectionFreeForm';
+import { CollectionLinearView } from './collectionLinear';
 import './CollectionMenu.scss';
 import { COLLECTION_BORDER_WIDTH } from './CollectionView';
 import { TabDocView } from './TabDocView';
-import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionLinearView } from './collectionLinear';
-import { media_state } from '../nodes/AudioBox';
 
 interface CollectionMenuProps {
     panelHeight: () => number;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 6e4b9ec07..c2062e8ab 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,4 @@
-import { computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2897cac0e..da0f7c893 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -241,7 +241,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.layoutDoc._freeform_scale = vals.scale;
     };
     freeformData = (force?: boolean) => (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined);
-    reverseNativeScaling = () => (this.fitContentsToBox ? true : false);
+    ignoreNativeDimScaling = () => (this.fitContentsToBox ? true : false);
     // freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
     // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index f9afe4d53..085f9f023 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -6,7 +6,7 @@ import { listSpec } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { numberRange, OmitKeys } from '../../../Utils';
+import { numberRange } from '../../../Utils';
 import { DocumentManager } from '../../util/DocumentManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { Transform } from '../../util/Transform';
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index a26d2e9f3..50a7f5d7b 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -2,21 +2,21 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc } from '../../../fields/Doc';
 import { StrCast } from '../../../fields/Types';
 import { emptyFunction, returnFalse, setupMoveUpEvents, StopEvent } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { Hypothesis } from '../../util/HypothesisUtils';
 import { LinkManager } from '../../util/LinkManager';
-import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
 import './DocumentLinksButton.scss';
 import { DocumentView } from './DocumentView';
 import { LinkDescriptionPopup } from './LinkDescriptionPopup';
 import { TaskCompletionBox } from './TaskCompletedBox';
+import { PinProps } from './trails';
 import React = require('react');
 import _ = require('lodash');
-import { PinProps } from './trails';
 
 interface DocumentLinksButtonProps {
     View: DocumentView;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 30d5a5184..98b13f90f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -119,7 +119,7 @@ export interface DocComponentView {
     getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
     addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
     addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
-    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
+    ignoreNativeDimScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     select?: (ctrlKey: boolean, shiftKey: boolean) => void;
     focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt<number>;
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
@@ -1459,10 +1459,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return Doc.Layout(this.Document, this.props.LayoutTemplate?.());
     }
     @computed get nativeWidth() {
-        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
+        return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
     }
     @computed get nativeHeight() {
-        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
+        return this.docView?._componentView?.ignoreNativeDimScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.layout_fitWidth));
     }
     @computed get shouldNotScale() {
         return this.props.shouldNotScale?.() || (this.layout_fitWidth && !this.nativeWidth) || [CollectionViewType.Docking].includes(this.Document._type_collection as any);
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 673f711be..95aeb5331 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -41,7 +41,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     componentDidMount() {
         this.props.setContentView?.(this);
     }
-    reverseNativeScaling = returnTrue;
+    ignoreNativeDimScaling = returnTrue;
     able = returnAlways;
     layout_fitWidth = returnTrue;
     overridePointerEvents = returnAll;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index a94279e88..2aca314da 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -10,13 +10,12 @@ import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { RefField } from '../../../fields/RefField';
 import { listSpec } from '../../../fields/Schema';
-import { Cast, ImageCast, NumCast, StrCast, WebCast } from '../../../fields/Types';
+import { Cast, NumCast, StrCast, WebCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager } from '../../util/DragManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index 3093287e9..2b0927148 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -9,7 +9,9 @@ import { NumCast } from '../../../../fields/Types';
 import { Utils } from '../../../../Utils';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { Networking } from '../../../Network';
+import { DocumentManager } from '../../../util/DocumentManager';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
 import { OpenWhereMod } from '../DocumentView';
 import { ImageBox } from '../ImageBox';
 import './GenerativeFill.scss';
@@ -20,8 +22,6 @@ import { CursorData, ImageDimensions, Point } from './generativeFillUtils/genera
 import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler';
 import { PointerHandler } from './generativeFillUtils/PointerHandler';
 import React = require('react');
-import { DocumentManager } from '../../../util/DocumentManager';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
 
 enum BrushStyle {
     ADD,
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 712a5e92f..43b662f0f 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -8,9 +8,8 @@ import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnAll, returnFalse, returnNone, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnAll, returnFalse, returnNone, returnZero, smoothScroll, Utils } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
-import { DragManager } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -23,10 +22,9 @@ import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import { StyleProp } from '../StyleProvider';
 import { AnchorMenu } from './AnchorMenu';
 import { Annotation } from './Annotation';
+import { GPTPopup } from './GPTPopup/GPTPopup';
 import './PDFViewer.scss';
 import React = require('react');
-import { GPTPopup } from './GPTPopup/GPTPopup';
-import { InkingStroke } from '../InkingStroke';
 const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer');
 const pdfjsLib = require('pdfjs-dist');
 const _global = (window /* browser */ || global) /* node */ as any;
-- 
cgit v1.2.3-70-g09d2

