From daa1e3ddf585f5fe237c100504130a3eae204252 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 26 Feb 2021 15:12:28 -0500
Subject: fixed serialization error handling to not kill Dash - bad list items
 prevented tabs from being created.  cleaned up document fields a little more
 - switched layers to _layerTags, got rid of a couple unused fields.

---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx  | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4040362d8..02f148eb0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -264,7 +264,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            !StrListCast(d.layers).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            !StrListCast(d._layerTags).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         this.updateGroupBounds();
@@ -429,8 +429,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 styleProp = colors[cluster % colors.length];
                 const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
                 // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
-                set?.filter(s => !StrListCast(s.layers).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
-                set?.filter(s => StrListCast(s.layers).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
+                set?.filter(s => !StrListCast(s._layerTags).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
+                set?.filter(s => StrListCast(s._layerTags).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
             }
         } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
         return styleProp;
@@ -869,7 +869,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @action
     bringToFront = (doc: Doc, sendToBack?: boolean) => {
-        if (sendToBack || StrListCast(doc.layers).includes(StyleLayers.Background)) {
+        if (sendToBack || StrListCast(doc._layerTags).includes(StyleLayers.Background)) {
             doc.zIndex = 0;
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
@@ -1363,7 +1363,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => intersectRect(docDims(doc), rect);
 
         const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
-        let snappableDocs = activeDocs.filter(doc => !StrListCast(doc.layers).includes(StyleLayers.Background) && doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
+        let snappableDocs = activeDocs.filter(doc => !StrListCast(doc._layerTags).includes(StyleLayers.Background) && doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs
 
-- 
cgit v1.2.3-70-g09d2


From bed03be10f9289e36f7e1621bbcf2579b0ca3f2d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 1 Mar 2021 13:00:23 -0500
Subject: final cleanups for simplified drag and drop of annotations.  fixed
 some issues rtf focus when creating notes in masonry view.  fixed text
 sidebar.

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/CurrentUserUtils.ts                |  2 +-
 src/client/util/DragManager.ts                     | 31 +++++++--------
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/MarqueeAnnotator.tsx              | 30 +++++++--------
 src/client/views/StyleProvider.tsx                 |  5 ++-
 .../collections/CollectionMasonryViewFieldRow.tsx  |  6 ++-
 src/client/views/collections/CollectionSubView.tsx | 13 ++++---
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 23 +++++++----
 src/client/views/nodes/DocumentLinksButton.tsx     |  8 +---
 src/client/views/nodes/DocumentView.tsx            | 19 +++++----
 src/client/views/nodes/ImageBox.tsx                | 11 ++----
 src/client/views/nodes/VideoBox.tsx                | 11 ++----
 src/client/views/nodes/WebBox.tsx                  | 11 ++----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 45 +++++++---------------
 src/client/views/pdf/AnchorMenu.tsx                |  4 +-
 src/fields/Doc.ts                                  |  6 +--
 18 files changed, 106 insertions(+), 127 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 3791dbc5c..3f03d39da 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -658,7 +658,7 @@ export namespace Docs {
             viewDoc["acl-Public"] = dataDoc["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
             viewDoc["acl-Override"] = dataDoc["acl-Override"] = "None";
 
-            !Doc.IsSystem(dataDoc) && proto.type !== DocumentType.PDFANNO && proto.type !== DocumentType.LINK && proto.type !== DocumentType.LINKANCHOR &&
+            !Doc.IsSystem(dataDoc) && ![DocumentType.PDFANNO, DocumentType.LINK, DocumentType.LINKANCHOR, DocumentType.TEXTANCHOR].includes(proto.type as any) &&
                 !protoProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc);
 
             return Doc.assign(viewDoc, delegateProps, true);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 3304de9a1..cd9905be6 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -428,7 +428,7 @@ export class CurrentUserUtils {
             ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyNote === undefined) {
-            doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyImage === undefined) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9baefd247..437fea0ea 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -138,13 +138,13 @@ export namespace DragManager {
         isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged.  this will suppress onDragStart scripts
     }
     export class LinkDragData {
-        constructor(linkSourceDoc: Doc) {
-            this.linkSourceDocument = linkSourceDoc;
+        constructor(dragDoc: Doc, linkSourceGetAnchor: () => Doc) {
+            this.dragDocument = dragDoc;
+            this.linkSourceGetAnchor = linkSourceGetAnchor;
         }
-        droppedDocuments: Doc[] = [];
-        linkSourceDocument: Doc;
-        dontClearTextBox?: boolean;
-        linkDropCallback?: (data: { linkDocument: Doc }) => void;
+        dragDocument: Doc;
+        linkSourceGetAnchor: () => Doc;
+        linkSourceDoc?: Doc;
     }
     export class ColumnDragData {
         constructor(colKey: SchemaHeaderField) {
@@ -154,23 +154,18 @@ export namespace DragManager {
     }
     // used by PDFs,Text,Image,Video,Web to conditionally (if the drop completes) create a text annotation when dragging the annotate button from the AnchorMenu when a text/region selection has been made.
     // this is pretty clunky and should be rethought out using linkDrag or DocumentDrag
-    export class AnchorAnnoDragData {
-        constructor(dragDoc: Doc, annotationDocCreator: () => Doc, dropDocCreator: (annotationOn?: Doc) => Doc) {
-            this.dragDocument = dragDoc;
+    export class AnchorAnnoDragData extends LinkDragData {
+        constructor(dragDoc: Doc, linkSourceGetAnchor: () => Doc, dropDocCreator: (annotationOn: Doc | undefined) => Doc) {
+            super(dragDoc, linkSourceGetAnchor);
             this.dropDocCreator = dropDocCreator;
-            this.annotationDocCreator = annotationDocCreator;
             this.offset = [0, 0];
         }
-        targetContext: Doc | undefined;
-        dragDocument: Doc;
-        annotationDocCreator: () => Doc;
-        dropDocCreator: (asAnnotation?: Doc) => Doc;
+        linkSourceDoc?: Doc;
+        dropDocCreator: (annotationOn: Doc | undefined) => Doc;
         dropDocument?: Doc;
-        annotationDocument?: Doc;
         offset: number[];
         dropAction: dropActionType;
         userDropAction: dropActionType;
-        linkDropCallback?: (data: { linkDocument: Doc }) => void;
     }
 
     export function MakeDropTarget(
@@ -258,8 +253,8 @@ export namespace DragManager {
     }
 
     // drags a linker button and creates a link on drop
-    export function StartLinkDrag(ele: HTMLElement, sourceDoc: Doc, downX: number, downY: number, options?: DragOptions) {
-        StartDrag([ele], new DragManager.LinkDragData(sourceDoc), downX, downY, options);
+    export function StartLinkDrag(ele: HTMLElement, sourceDoc: Doc, sourceDocGetAnchor: undefined | (() => Doc), downX: number, downY: number, options?: DragOptions) {
+        StartDrag([ele], new DragManager.LinkDragData(sourceDoc, () => sourceDocGetAnchor?.() ?? sourceDoc), downX, downY, options);
     }
 
     // drags a column from a schema view
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 92f5b79bd..357bff36d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -414,8 +414,8 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
             if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions();
             if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
                 const doc = Document(docView.rootDoc);
-                let nwidth = docView.nativeWidth;
-                let nheight = docView.nativeHeight;
+                const nwidth = docView.nativeWidth;
+                const nheight = docView.nativeHeight;
                 const width = (doc._width || 0);
                 let height = (doc._height || (nheight / nwidth * width));
                 height = !height || isNaN(height) ? 20 : height;
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 1fb87f522..f03e1dd9d 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -64,27 +64,24 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
          * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.  
          * It also initiates a Drag/Drop interaction to place the text annotation.
          */
-        AnchorMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => {
+        AnchorMenu.Instance.StartDrag = action((e: PointerEvent, ele: HTMLElement) => {
             e.preventDefault();
             e.stopPropagation();
-            const targetCreator = (asAnnotation?: Doc) => {
-                const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100, undefined, asAnnotation);
-                FormattedTextBox.SelectOnLoad = target[Id];
-                return target;
-            };
-            const anchorCreator = () => {
-                const annoDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color
-                annoDoc.isLinkButton = true; // prevents link button from showing up --- maybe not a good thing?
+            const sourceAnchorCreator = () => {
+                const annoDoc = this.highlight("rgba(173, 216, 230, 0.75)", true); // hyperlink color
                 this.props.addDocument(annoDoc);
                 return annoDoc;
             };
-            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.rootDoc, anchorCreator, targetCreator), e.pageX, e.pageY, {
+            const targetCreator = (annotationOn: Doc | undefined) => {
+                const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn);
+                FormattedTextBox.SelectOnLoad = target[Id];
+                return target;
+            };
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.rootDoc, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                 dragComplete: e => {
-                    if (!e.aborted && e.annoDragData && e.annoDragData.annotationDocument && e.annoDragData.dropDocument && !e.linkDocument) {
-                        e.linkDocument = DocUtils.MakeLink({ doc: e.annoDragData.annotationDocument }, { doc: e.annoDragData.dropDocument }, "Annotation");
-                        e.annoDragData.annotationDocument.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.rootDoc;
+                    if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
+                        e.annoDragData.linkSourceDoc.isPushpin = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
                     }
-                    e.linkDocument && e.annoDragData?.linkDropCallback?.(e as { linkDocument: Doc });// bcz: typescript can't figure out that this is valid even though we tested e.linkDocument
                 }
             });
         });
@@ -141,11 +138,12 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         }
     }
     @action
-    highlight = (color: string) => {
+    highlight = (color: string, isLinkButton: boolean) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
         const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color);
         annotationDoc && this.props.addDocument(annotationDoc);
+        annotationDoc && (annotationDoc.isLinkButton = isLinkButton);
         return annotationDoc as Doc ?? undefined;
     }
 
@@ -197,7 +195,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
 
             if (AnchorMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
-                this.highlight("rgba(245, 230, 95, 0.75)");  // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
+                this.highlight("rgba(245, 230, 95, 0.75)", false);  // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
             }
         } else {
             runInAction(() => this._width = this._height = 0);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index e5b2a332a..5a2fb285b 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -65,13 +65,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
     const selected = property.includes(":selected");
     const isCaption = property.includes(":caption");
     const isAnchor = property.includes(":anchor");
+    const isAnnotated = property.includes(":annotated");
     const isBackground = () => StrListCast(doc?._layerTags).includes(StyleLayers.Background);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
     const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
 
     switch (property.split(":")[0]) {
         case StyleProp.DocContents: return undefined;
-        case StyleProp.WidgetColor: return darkScheme() ? "lightgrey" : "dimgrey";
+        case StyleProp.WidgetColor: return isAnnotated ? "lightBlue" : darkScheme() ? "lightgrey" : "dimgrey";
         case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
         case StyleProp.HideLinkButton: return isAnchor || props?.dontRegisterView || (!selected && (doc?.isLinkButton || doc?.hideLinkButton));
         case StyleProp.ShowTitle: return doc && !doc.presentationTargetDoc && StrCast(doc._showTitle,
@@ -157,7 +158,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
             if (isAnchor && docProps) return "none";
             if (props?.pointerEvents === "none") return "none";
             const layer = doc && props?.layerProvider?.(doc);
-            if (opacity() === 0 || doc?.type === DocumentType.INK || doc?.isInkMask) return "none";
+            if (opacity() === 0 || (doc?.type === DocumentType.INK && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
             if (layer === false && !selected && !SnappingManager.GetIsDragging()) return "none";
             if (doc?.type !== DocumentType.INK && layer === true) return "all";
             return undefined;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 4bdd39194..077e50dd2 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -16,6 +16,8 @@ import { EditableView } from "../EditableView";
 import { CollectionStackingView } from "./CollectionStackingView";
 import "./CollectionStackingView.scss";
 import { SnappingManager } from "../../util/SnappingManager";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
+import { Id } from "../../../fields/FieldSymbols";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -142,8 +144,10 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
         if (!value && !forceEmptyNote) return false;
         this._createAliasSelected = false;
         const key = StrCast(this.props.parent.props.Document._pivotField);
-        const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _width: 200, title: value });
+        const newDoc = Docs.Create.TextDocument("", { _autoHeight: true, _width: 200, title: value });
         const onLayoutDoc = this.onLayoutDoc(key);
+        FormattedTextBox.SelectOnLoad = newDoc[Id];
+        FormattedTextBox.SelectOnLoadChar = value;
         (onLayoutDoc ? newDoc : newDoc[DataSym])[key] = this.getValue(this.props.heading);
         const docs = this.props.parent.childDocList;
         return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this.props.parent.props.addDocument?.(newDoc) || false;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b0c665aa8..639650b94 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -224,11 +224,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 e.stopPropagation();
                 return added;
             }
-            else if (de.complete.annoDragData && (!this.props.isAnnotationOverlay || de.complete.annoDragData.dragDocument === this.props.Document)) {
-                e.stopPropagation();
-                de.complete.annoDragData.annotationDocument = de.complete.annoDragData.annotationDocCreator();
-                de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(this.props.isAnnotationOverlay ? this.props.Document : undefined);
-                return de.complete.annoDragData.dropDocument && this.addDocument(de.complete.annoDragData.dropDocument);
+            else if (de.complete.annoDragData) {
+                const dropCreator = de.complete.annoDragData.dropDocCreator;
+                de.complete.annoDragData.dropDocCreator = () => {
+                    const dropped = dropCreator(this.props.isAnnotationOverlay ? this.rootDoc : undefined);
+                    this.addDocument(dropped);
+                    return dropped;
+                }
+                return true;
             }
             return false;
         }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6975c24d1..18d515552 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -254,7 +254,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const before = pt[1] < rect.top + rect.height / 2;
         const inside = this.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
         if (de.complete.linkDragData) {
-            const sourceDoc = de.complete.linkDragData.linkSourceDocument;
+            const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
             const destDoc = this.doc;
             DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", "");
             e.stopPropagation();
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 02f148eb0..af8ccb9c5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -275,16 +275,23 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @undoBatch
     internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData, xp: number, yp: number) {
-        annoDragData.dropDocument!.x = xp - annoDragData.offset[0];
-        annoDragData.dropDocument!.y = yp - annoDragData.offset[1];
-        this.bringToFront(annoDragData.dropDocument!);
+        const dropCreator = annoDragData.dropDocCreator;
+        annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => {
+            const dropDoc = dropCreator(annotationOn);
+            if (dropDoc) {
+                dropDoc.x = xp - annoDragData.offset[0];
+                dropDoc.y = yp - annoDragData.offset[1];
+                this.bringToFront(dropDoc);
+            }
+            return dropDoc || this.rootDoc;
+        }
         return true;
     }
 
     @undoBatch
     internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
-        if (linkDragData.linkSourceDocument === this.props.Document || this.props.Document.annotationOn) return false;
-        if (!linkDragData.linkSourceDocument.context || StrCast(Cast(linkDragData.linkSourceDocument.context, Doc, null)?.type) === DocumentType.COL) {
+        if (linkDragData.dragDocument === this.props.Document || this.props.Document.annotationOn) return false;
+        if (!linkDragData.dragDocument.context || StrCast(Cast(linkDragData.dragDocument.context, Doc, null)?.type) === DocumentType.COL) {
             // const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
             // this.props.addDocument(source);
             // linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation"); // TODODO this is where in text links get passed
@@ -292,7 +299,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         } else {
             const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation", ""); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed
             e.stopPropagation();
             return true;
         }
@@ -300,9 +307,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         const [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
-        if (this.isAnnotationOverlay !== true && de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
         if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData, xp, yp);
-        if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp);
+        else if (this.isAnnotationOverlay !== true && de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
+        else if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp);
         return false;
     }
 
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 96b6bea8c..085ffae26 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -51,16 +51,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         if (this.props.InMenu && this.props.StartLink) {
             if (this._linkButton.current !== null) {
                 const linkDrag = UndoManager.StartBatch("Drag Link");
-                this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View.props.Document, e.pageX, e.pageY, {
+                this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View.props.Document, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
                     dragComplete: dropEv => {
                         if (this.props.View && dropEv.linkDocument) {// dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
                             !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = "hyperlink");
-
-                            // we want to allow specific views to handle the link creation in their own way (e.g., rich text makes text hyperlinks)
-                            // the dragged view can regiser a linkDropCallback to be notified that the link was made and to update their data structures
-                            // however, the dropped document isn't so accessible.  What we do is set the newly created link document on the documentView
-                            // The documentView passes a function prop returning this link doc to its descendants who can react to changes to it.
-                            dropEv.linkDragData?.linkDropCallback?.(dropEv as { linkDocument: Doc }); // bcz: typescript can't figure out that this is valid even though we tested dropEv.linkDocument above
                         }
                         linkDrag?.end();
                     },
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 848287fcc..e5bd14809 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -605,13 +605,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 "linking to document tabs not yet supported.  Drop link on document content.");
             return;
         }
-        if (de.complete.annoDragData) de.complete.annoDragData.annotationDocument = de.complete.annoDragData.annotationDocCreator();
-        const linkSource = de.complete.annoDragData ? de.complete.annoDragData.annotationDocument : de.complete.linkDragData ? de.complete.linkDragData.linkSourceDocument : undefined;
-        if (linkSource && linkSource !== this.props.Document) {
+        const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
+        if (linkdrag) linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor();
+        if (linkdrag?.linkSourceDoc) {
             e.stopPropagation();
-            const dropDoc = this._componentView?.getAnchor?.() || this.rootDoc;
-            if (de.complete.annoDragData) de.complete.annoDragData.dropDocument = dropDoc;
-            de.complete.linkDocument = DocUtils.MakeLink({ doc: linkSource }, { doc: dropDoc }, "link", undefined, undefined, undefined, [de.x, de.y]);
+            if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
+                de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
+            }
+            if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
+                const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
+                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, "link", undefined, undefined, undefined, [de.x, de.y]);
+            }
         }
     }
 
@@ -947,7 +951,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
             ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"])[highlightIndex];
         const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex];
-        let highlighting = !this.props.cantBrush && highlightIndex && ![DocumentType.FONTICON, DocumentType.INK].includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
+        const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
+        let highlighting = !this.props.cantBrush && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
         highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]";  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
 
         const boxShadow = highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4c3031ae2..240aa1659 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -62,13 +62,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
 
     componentDidMount() {
         this._disposers.selection = reaction(() => this.props.isSelected(),
-            selected => {
-                if (!selected) {
-                    this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
-                    this._savedAnnotations.clear();
-                }
-            },
-            { fireImmediately: true });
+            selected => !selected && setTimeout(() => {
+                this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+                this._savedAnnotations.clear();
+            }));
         this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
             action(({ nativeSize, width }) => {
                 if (!this.layoutDoc._height) {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ac67949f9..589a6dd24 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -204,13 +204,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
     componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         this._disposers.selection = reaction(() => this.props.isSelected(),
-            selected => {
-                if (!selected) {
-                    this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
-                    this._savedAnnotations.clear();
-                }
-            },
-            { fireImmediately: true });
+            selected => !selected && setTimeout(() => {
+                this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+                this._savedAnnotations.clear();
+            }));
         this._disposers.triggerVideo = reaction(
             () => !LinkDocPreview.LinkInfo && this.props.renderDepth !== -1 ? NumCast(this.Document._triggerVideo, null) : undefined,
             time => time !== undefined && setTimeout(() => {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2cd6f5f33..0cf052501 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -140,13 +140,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
         runInAction(() => this._url = urlField?.url.toString() || "");
 
         this._disposers.selection = reaction(() => this.props.isSelected(),
-            selected => {
-                if (!selected) {
-                    this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
-                    this._savedAnnotations.clear();
-                }
-            },
-            { fireImmediately: true });
+            selected => !selected && setTimeout(() => {
+                this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+                this._savedAnnotations.clear();
+            }));
 
         document.addEventListener("pointerup", this.onLongPressUp);
         document.addEventListener("pointermove", this.onLongPressMove);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 59147061a..288cd14c9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -185,7 +185,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     @action
     setupAnchorMenu = () => {
         AnchorMenu.Instance.Status = "marquee";
-        AnchorMenu.Instance.Highlight = action((color: string) => {
+        AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
             this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch);
             return undefined;
         });
@@ -202,16 +202,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 return target;
             };
 
-            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.rootDoc, () => this.rootDoc, targetCreator), e.pageX, e.pageY, {
-                dragComplete: e => {
-                    const anchor = this.makeLinkAnchor(undefined, "add:right", undefined, "a link");
-                    if (!e.aborted && e.annoDragData && e.annoDragData.annotationDocument && e.annoDragData.dropDocument && !e.linkDocument) {
-                        e.linkDocument = DocUtils.MakeLink({ doc: anchor }, { doc: e.annoDragData.dropDocument }, "hyperlink", "link to note");
-                        e.annoDragData.annotationDocument.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.rootDoc;
-                    }
-                    e.linkDocument && e.annoDragData?.linkDropCallback?.(e as { linkDocument: Doc });// bcz: typescript can't figure out that this is valid even though we tested e.linkDocument
-                }
-            });
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.rootDoc, this.getAnchor, targetCreator), e.pageX, e.pageY);
         });
         const coordsT = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
         const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
@@ -401,7 +392,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
     @undoBatch
     @action
-    drop = async (e: Event, de: DragManager.DropEvent) => {
+    drop = (e: Event, de: DragManager.DropEvent) => {
+        if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = this.getAnchor;
         const dragData = de.complete.docDragData;
         if (dragData) {
             const draggedDoc = dragData.draggedDocuments.length && dragData.draggedDocuments[0];
@@ -427,18 +419,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 e.stopPropagation();
                 // }
             } // otherwise, fall through to outer collection to handle drop
-        } else if (de.complete.linkDragData) {
-            de.complete.linkDragData.linkDropCallback = this.linkDrop;
-        }
-        else if (de.complete.annoDragData) {
-            de.complete.annoDragData.linkDropCallback = this.linkDrop;
         }
     }
-    linkDrop = (data: { linkDocument: Doc }) => {
-        const anchor1Title = data.linkDocument.anchor1 instanceof Doc ? StrCast(data.linkDocument.anchor1.title) : "-untitled-";
-        const anchor1 = data.linkDocument.anchor1 instanceof Doc ? data.linkDocument.anchor1 : undefined;
-        this.makeLinkAnchor(anchor1, "add:right", undefined, anchor1Title);
-    }
 
     getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null {
         let offset = 0;
@@ -875,6 +857,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }, { fireImmediately: true }
         );
         quickScroll = undefined;
+        this.tryUpdateScrollHeight();
     }
 
     pushToGoogleDoc = async () => {
@@ -1457,11 +1440,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
     @computed get sidebarHandle() {
         const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
-        return this.props.noSidebar || (!this.props.isSelected() && !(annotated && !this.sidebarWidth())) ? (null) :
-            <div className="formattedTextBox-sidebar-handle"
-                style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightblue" : this.props.styleProvider?.(this.props.Document, this.props, StyleProp.WidgetColor) }}
-                onPointerDown={this.sidebarDown}
-            />;
+        return <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
+            style={{
+                left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`,
+                background: this.props.styleProvider?.(this.props.Document, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : ""))
+            }} />;
     }
     @computed get sidebarCollection() {
         const renderComponent = (tag: string) => {
@@ -1490,8 +1473,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 ScreenToLocalTransform={this.sidebarScreenToLocal}
                 renderDepth={this.props.renderDepth + 1}
                 noSidebar={true}
-                fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : this.SidebarKey} />
-        }
+                fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : this.SidebarKey} />;
+        };
         return <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
             style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
             {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
@@ -1557,8 +1540,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                             }}
                         />
                     </div>
-                    {this.sidebarCollection}
-                    {this.props.noSidebar || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarHandle}
+                    {this.props.noSidebar || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection}
+                    {this.sidebarHandle}
                     {!this.layoutDoc._showAudio ? (null) : this.audioHandle}
                 </div>
             </div>
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index d1fdc6c44..e3d14d620 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -43,7 +43,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable public Status: "marquee" | "annotation" | "" = "";
 
     public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
-    public Highlight: (color: string) => Opt<Doc> = (color: string) => undefined;
+    public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined;
     public Delete: () => void = unimplementedFunction;
     public AddTag: (key: string, value: string) => boolean = returnFalse;
     public PinToPres: () => void = unimplementedFunction;
@@ -76,7 +76,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     @action
     highlightClicked = (e: React.MouseEvent) => {
-        if (!this.Highlight(this.highlightColor) && this.Pinned) {
+        if (!this.Highlight(this.highlightColor, false) && this.Pinned) {
             this.Highlighting = !this.Highlighting;
         }
     }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index bc50d70de..e24821116 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1119,14 +1119,12 @@ export namespace Doc {
     export function isDocPinned(doc: Doc) {
         //add this new doc to props.Document
         const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
-        if (curPres) {
-            return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
-        }
-        return false;
+        return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
     }
 
     export function copyDragFactory(dragFactory: Doc) {
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
+        ndoc && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(ndoc));
         if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
             dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
             Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
-- 
cgit v1.2.3-70-g09d2

