From dc942e6228e003caa3754a72c0e126d64332a004 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 1 Nov 2022 18:29:11 -0400
Subject: fixes for goldenlayout to initialize more cleanly.  updated all old
 ReactDOM.render() to ReactDom.createRoot().  fixes for PDF/Web sidebar
 sizing.  added text from pdf selection anchors to sidebar notes.   fixed  PDF
 text selection to align properly.

---
 src/client/views/SidebarAnnos.tsx | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

(limited to 'src/client/views/SidebarAnnos.tsx')

diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 12f41394d..df1eb72ce 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -15,6 +15,7 @@ import { SearchBox } from './search/SearchBox';
 import './SidebarAnnos.scss';
 import { StyleProp } from './StyleProvider';
 import React = require('react');
+import { RichTextField } from '../../fields/RichTextField';
 
 interface ExtraProps {
     fieldKey: string;
@@ -72,6 +73,27 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         FormattedTextBox.DontSelectInitialText = true;
         this.allMetadata.map(tag => (target[tag] = tag));
         DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on');
+
+        Doc.GetProto(target).text = new RichTextField(
+            JSON.stringify({
+                doc: {
+                    type: 'doc',
+                    content: [
+                        {
+                            type: 'paragraph',
+                            attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+                            content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false } }],
+                        },
+                        {
+                            type: 'paragraph',
+                            attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+                        },
+                    ],
+                },
+                selection: { type: 'text', anchor: 4, head: 4 },
+            }),
+            ''
+        );
         this.addDocument(target);
         this._stackRef.current?.focusDocument(target, {});
         return target;
-- 
cgit v1.2.3-70-g09d2


From 1a8024dce302acc1637cd1f7d74b0238c5979c15 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 2 Nov 2022 10:42:25 -0400
Subject: select docs when title header is edited. fixed doc decoration top
 border.  sort sidebar annos by y coord

---
 src/client/views/DocumentDecorations.scss               |  4 +++-
 src/client/views/SidebarAnnos.tsx                       |  9 ++++++++-
 src/client/views/collections/CollectionStackingView.tsx | 12 ++++++++----
 src/client/views/nodes/DocumentView.tsx                 |  5 ++++-
 4 files changed, 23 insertions(+), 7 deletions(-)

(limited to 'src/client/views/SidebarAnnos.tsx')

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index b41962c73..ccac5ffe4 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,7 +1,7 @@
 @import 'global/globalCssVariables';
 
 $linkGap: 3px;
-$headerHeight: 25px;
+$headerHeight: 20px;
 $resizeHandler: 8px;
 
 .documentDecorations-Dark,
@@ -424,7 +424,9 @@ $resizeHandler: 8px;
     background: $light-gray;
     opacity: 0.2;
     pointer-events: all;
+    transition: opacity 1s;
     &:hover {
+        transition: unset;
         opacity: 1;
     }
 }
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index df1eb72ce..869caabd1 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
 import { Doc, DocListCast, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
-import { NumCast, StrCast } from '../../fields/Types';
+import { DocCast, NumCast, StrCast } from '../../fields/Types';
 import { emptyFunction, OmitKeys, returnAll, returnOne, returnTrue, returnZero } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
@@ -66,6 +66,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             _height: 50,
             _fitWidth: true,
             _autoHeight: true,
+            _isLinkButton: true,
             _fontSize: StrCast(Doc.UserDoc().fontSize),
             _fontFamily: StrCast(Doc.UserDoc().fontFamily),
         });
@@ -133,6 +134,11 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
     showTitle = () => 'title';
     setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
+    sortByLinkAnchorY = (a: Doc, b: Doc) => {
+        const ay = DocListCast(a.links).length && DocCast(DocListCast(a.links)[0].anchor1).y;
+        const by = DocListCast(b.links).length && DocCast(DocListCast(b.links)[0].anchor1).y;
+        return NumCast(ay) - NumCast(by);
+    };
     render() {
         const renderTag = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`);
@@ -184,6 +190,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         PanelWidth={this.panelWidth}
                         docFilters={this.docFilters}
                         scaleField={this.sidebarKey + '-scale'}
+                        sortFunc={this.sortByLinkAnchorY}
                         setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
                         select={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 77b47ed82..175051d5c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,12 +3,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { CursorProperty } from 'csstype';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -33,6 +33,7 @@ import { CollectionSubView } from './CollectionSubView';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionStackingViewProps = {
+    sortFunc?: (a: Doc, b: Doc) => number;
     chromeHidden?: boolean;
     // view type is stacking
     viewType?: CollectionViewType;
@@ -73,7 +74,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     }
     // filteredChildren is what you want to work with. It's the list of things that you're currently displaying
     @computed get filteredChildren() {
-        return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
+        const children = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
+        if (this.props.sortFunc) children.sort(this.props.sortFunc);
+        return children;
     }
     // how much margin we give the header
     @computed get headerMargin() {
@@ -305,6 +308,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
 
     isChildContentActive = () =>
         this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
+    isChildButtonContentActive = () => (this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined);
     // this is what renders the document that you see on the screen
     // called in Children: this actually adds a document to our children list
     getDisplayDoc(doc: Doc, width: () => number) {
@@ -326,7 +330,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 styleProvider={this.styleProvider}
                 docViewPath={this.props.docViewPath}
                 fitWidth={this.props.childFitWidth}
-                isContentActive={this.isChildContentActive}
+                isContentActive={doc.isLinkButton ? this.isChildButtonContentActive : this.isChildContentActive}
                 onKey={this.onKeyDown}
                 isDocumentActive={this.isContentActive}
                 LayoutTemplate={this.props.childLayoutTemplate}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 50b76896e..595fc3bca 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1313,7 +1313,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         .join('\\')}
                     display={'block'}
                     fontSize={10}
-                    GetValue={() => (showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle)}
+                    GetValue={() => {
+                        this.props.select(false);
+                        return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle;
+                    }}
                     SetValue={undoBatch((input: string) => {
                         if (input?.startsWith('#')) {
                             if (this.props.showTitle) {
-- 
cgit v1.2.3-70-g09d2


From b171b939743de6126f3fb5ca1e2d34bba7cbe428 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 2 Nov 2022 12:54:18 -0400
Subject: fix sidebar so active filters add proper metadata to newly created
 documents

---
 src/client/views/SidebarAnnos.tsx                  | 72 ++++++++++++++--------
 src/client/views/nodes/DocumentView.tsx            |  1 +
 .../views/nodes/formattedText/FormattedTextBox.tsx |  5 +-
 3 files changed, 52 insertions(+), 26 deletions(-)

(limited to 'src/client/views/SidebarAnnos.tsx')

diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 869caabd1..78480da03 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -1,6 +1,6 @@
 import { computed } from 'mobx';
 import { observer } from 'mobx-react';
-import { Doc, DocListCast, StrListCast } from '../../fields/Doc';
+import { Doc, DocListCast, Field, FieldResult, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { DocCast, NumCast, StrCast } from '../../fields/Types';
@@ -39,12 +39,13 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     }
     _stackRef = React.createRef<CollectionStackingView>();
     @computed get allMetadata() {
-        const keys = new Set<string>();
-        DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key)));
-        return Array.from(keys.keys())
-            .filter(key => key[0])
-            .filter(key => key[0] !== '_' && key[0] === key[0].toUpperCase())
-            .sort();
+        const keys = new Map<string, FieldResult<Field>>();
+        DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc =>
+            SearchBox.documentKeys(doc)
+                .filter(key => key[0] && key[0] !== '_' && key[0] === key[0].toUpperCase())
+                .map(key => keys.set(key, doc[key]))
+        );
+        return keys;
     }
     @computed get allUsers() {
         const keys = new Set<string>();
@@ -72,26 +73,47 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         });
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
-        this.allMetadata.map(tag => (target[tag] = tag));
         DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on');
 
+        const taggedContent = this.docFilters()
+            .filter(data => data.split(':')[0])
+            .map(data => {
+                const key = data.split(':')[0];
+                const val = Field.Copy(this.allMetadata.get(key));
+                Doc.GetProto(target)[key] = val;
+                return {
+                    type: 'dashField',
+                    attrs: { fieldKey: key, docid: '', hideKey: false, editable: true },
+                    marks: [
+                        { type: 'pFontSize', attrs: { fontSize: '12px' } },
+                        { type: 'pFontFamily', attrs: { family: 'Arial' } },
+                        { type: 'pFontColor', attrs: { color: 'black' } },
+                        { type: 'strong' },
+                        { type: 'user_mark', attrs: { userid: Doc.CurrentUserEmail, modified: 0 } },
+                    ],
+                };
+            });
+        const textLines = [
+            {
+                type: 'paragraph',
+                attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+                content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false } }],
+            },
+            { type: 'paragraph', attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null } },
+        ];
+        const metadatatext = {
+            type: 'paragraph',
+            attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+            content: taggedContent,
+        };
+        if (taggedContent.length) textLines.push(metadatatext);
         Doc.GetProto(target).text = new RichTextField(
             JSON.stringify({
                 doc: {
                     type: 'doc',
-                    content: [
-                        {
-                            type: 'paragraph',
-                            attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
-                            content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false } }],
-                        },
-                        {
-                            type: 'paragraph',
-                            attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
-                        },
-                    ],
+                    content: textLines,
                 },
-                selection: { type: 'text', anchor: 4, head: 4 },
+                selection: { type: 'text', anchor: 4, head: 4 }, // set selection to middle paragraph
             }),
             ''
         );
@@ -148,10 +170,10 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 </div>
             );
         };
-        const renderMeta = (tag: string) => {
-            const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:exists`);
+        const renderMeta = (tag: string, dflt: FieldResult<Field>) => {
+            const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${dflt}:exists`);
             return (
-                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, 'exists', true, this.sidebarKey, e.shiftKey)}>
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, dflt, 'exists', true, this.sidebarKey, e.shiftKey)}>
                     {tag}
                 </div>
             );
@@ -178,7 +200,9 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 }}>
                 <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight() }} onWheel={e => e.stopPropagation()}>
                     {this.allUsers.map(renderUsers)}
-                    {this.allMetadata.map(renderMeta)}
+                    {Array.from(this.allMetadata.keys())
+                        .sort()
+                        .map(key => renderMeta(key, this.allMetadata.get(key)))}
                 </div>
                 <div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
                     <CollectionStackingView
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 595fc3bca..a35400e72 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -619,6 +619,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     }, 350);
                 } else clickFunc();
             } else if (this.allLinks && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
+                SelectionManager.DeselectAll();
                 this.allLinks.length && LinkFollower.FollowLink(undefined, this.props.Document, this.props, e.altKey);
             } else {
                 if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0e48dd93a..ee97e5e62 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1398,9 +1398,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             } else if (this._editorView) {
                 this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
             }
-            FormattedTextBox.DontSelectInitialText = false;
         }
-        selectOnLoad && this._editorView!.focus();
+        if (FormattedTextBox.DontSelectInitialText) setTimeout(() => selectOnLoad && this._editorView!.focus(), 0);
+        else selectOnLoad && this._editorView!.focus();
+        FormattedTextBox.DontSelectInitialText = false;
         // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
         if (this._editorView) {
             this._editorView.state.storedMarks = [
-- 
cgit v1.2.3-70-g09d2


From fe98c7d46df1852a74cd84dbe9ad010bfb3d5550 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 4 Nov 2022 12:43:59 -0400
Subject: more fixes to pdf and text to allow dashfieldview nodes to be link
 anchors and make sidebar annotations work better.

---
 src/client/views/SidebarAnnos.tsx                  | 34 +++++++----
 src/client/views/linking/LinkMenuItem.tsx          |  1 +
 src/client/views/nodes/LinkAnchorBox.tsx           |  1 +
 src/client/views/nodes/LinkDocPreview.tsx          |  2 +
 .../views/nodes/formattedText/DashFieldView.scss   |  9 ++-
 .../views/nodes/formattedText/DashFieldView.tsx    | 65 ++++++++--------------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  7 ++-
 .../formattedText/FormattedTextBoxComment.tsx      | 14 +++--
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 src/client/views/nodes/formattedText/marks_rts.ts  |  4 +-
 10 files changed, 71 insertions(+), 68 deletions(-)

(limited to 'src/client/views/SidebarAnnos.tsx')

diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 78480da03..c285b2e34 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -67,7 +67,6 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             _height: 50,
             _fitWidth: true,
             _autoHeight: true,
-            _isLinkButton: true,
             _fontSize: StrCast(Doc.UserDoc().fontSize),
             _fontFamily: StrCast(Doc.UserDoc().fontFamily),
         });
@@ -84,20 +83,33 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 return {
                     type: 'dashField',
                     attrs: { fieldKey: key, docid: '', hideKey: false, editable: true },
-                    marks: [
-                        { type: 'pFontSize', attrs: { fontSize: '12px' } },
-                        { type: 'pFontFamily', attrs: { family: 'Arial' } },
-                        { type: 'pFontColor', attrs: { color: 'black' } },
-                        { type: 'strong' },
-                        { type: 'user_mark', attrs: { userid: Doc.CurrentUserEmail, modified: 0 } },
-                    ],
+                    marks: [{ type: 'pFontSize', attrs: { fontSize: '12px' } }, { type: 'strong' }, { type: 'user_mark', attrs: { userid: Doc.CurrentUserEmail, modified: 0 } }],
                 };
             });
-        const textLines = [
+
+        const textLines: any = [
             {
                 type: 'paragraph',
                 attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
-                content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false } }],
+                content: [
+                    {
+                        type: 'dashField',
+                        marks: [
+                            {
+                                type: 'linkAnchor',
+                                attrs: {
+                                    allAnchors: [{ href: `/doc/${target[Id]}`, title: 'Anchored Selection', noPreview: true, anchorId: `${target[Id]}` }],
+                                    location: 'add:right',
+                                    title: 'Anchored Selection',
+                                    docref: false,
+                                },
+                            },
+                            { type: 'pFontSize', attrs: { fontSize: '8px' } },
+                            { type: 'em' },
+                        ],
+                        attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false },
+                    },
+                ],
             },
             { type: 'paragraph', attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null } },
         ];
@@ -118,7 +130,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             ''
         );
         this.addDocument(target);
-        this._stackRef.current?.focusDocument(target, {});
+        setTimeout(() => this._stackRef.current?.focusDocument(target, {}));
         return target;
     };
     makeDocUnfiltered = (doc: Doc) => {
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 3f9db2612..387e0e3d5 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -155,6 +155,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                                 linkDoc: this.props.linkDoc,
                                 showHeader: false,
                                 location: [this._drag.current?.getBoundingClientRect().right ?? 100, this._drag.current?.getBoundingClientRect().top ?? e.clientY],
+                                noPreview: false,
                             })
                         }
                         onPointerDown={this.onLinkButtonDown}>
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 5102eae51..d6cf79f87 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -144,6 +144,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         linkDoc: this.rootDoc,
                         showHeader: true,
                         location: [e.clientX, e.clientY + 20],
+                        noPreview: false,
                     })
                 }
                 onPointerDown={this.onPointerDown}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 27e79a83b..0e66214d1 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -26,6 +26,7 @@ interface LinkDocPreviewProps {
     location: number[];
     hrefs?: string[];
     showHeader?: boolean;
+    noPreview?: boolean;
 }
 @observer
 export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
@@ -111,6 +112,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                                     this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
                                 }
                                 this._toolTipText = '';
+                                if (LinkDocPreview.LinkInfo?.noPreview) this.followLink();
                             }
                         })
                     );
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index c36e6804b..f17579853 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -1,3 +1,5 @@
+@import '../../global/globalCssVariables';
+
 .dashFieldView {
     position: relative;
     display: inline-flex;
@@ -22,7 +24,7 @@
         position: relative;
         display: inline-block;
         font-weight: normal;
-        background: rgba(0,0,0,0.1);
+        background: rgba(0, 0, 0, 0.1);
     }
     .dashFieldView-fieldSpan {
         min-width: 8px;
@@ -31,11 +33,12 @@
         padding-left: 2px;
         display: inline-block;
         background-color: rgba(155, 155, 155, 0.24);
-        font-weight: bold;
         span {
             min-width: 100%;
             display: inline-block;
         }
     }
 }
-        
\ No newline at end of file
+.ProseMirror-selectedNode {
+    outline: solid 1px $light-blue !important;
+}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index f088b39d1..fbcf25215 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -16,21 +16,20 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 import { Tooltip } from '@material-ui/core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { NodeSelection } from 'prosemirror-state';
+import { FormattedTextBoxComment } from './FormattedTextBoxComment';
 
 export class DashFieldView {
     dom: HTMLDivElement; // container for label and value
     root: any;
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
-        const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey);
-
         this.dom = document.createElement('div');
         this.dom.style.width = node.attrs.width;
         this.dom.style.height = node.attrs.height;
-        this.dom.style.fontWeight = 'bold';
         this.dom.style.position = 'relative';
         this.dom.style.display = 'inline-block';
-        this.dom.textContent = node.attrs.fieldKey.startsWith('#') ? node.attrs.fieldKey : node.attrs.fieldKey + ' ' + strVal;
+        //this.dom.contentEditable = 'true';
         this.dom.onkeypress = function (e: any) {
             e.stopPropagation();
         };
@@ -45,12 +44,20 @@ export class DashFieldView {
         };
 
         this.root = ReactDOM.createRoot(this.dom);
-        this.root.render(<DashFieldViewInternal fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} editable={node.attrs.editable} tbox={tbox} />);
+        this.root.render(
+            <DashFieldViewInternal node={node} getPos={getPos} fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} editable={node.attrs.editable} tbox={tbox} />
+        );
     }
     destroy() {
         //this.root.unmount();
     }
-    selectNode() {}
+    deselectNode() {
+        this.dom.classList.remove('ProseMirror-selectednode');
+    }
+    selectNode() {
+        this.dom.classList.add('ProseMirror-selectednode');
+        console.log('SELECT');
+    }
 }
 
 interface IDashFieldViewInternal {
@@ -61,6 +68,8 @@ interface IDashFieldViewInternal {
     width: number;
     height: number;
     editable: boolean;
+    node: any;
+    getPos: any;
 }
 
 @observer
@@ -127,7 +136,13 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                             r?.addEventListener('blur', e => r && this.updateText(r.textContent!, false));
                             r?.addEventListener(
                                 'pointerdown',
-                                action(e => e.stopPropagation())
+                                action(e => {
+                                    // let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
+                                    // while (target && !target.dataset?.targethrefs) target = target.parentElement;
+                                    this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new NodeSelection(this.props.tbox.EditorView!.state.doc.resolve(this.props.getPos()))));
+                                    // FormattedTextBoxComment.update(this.props.tbox, this.props.tbox.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc);
+                                    // e.stopPropagation();
+                                })
                             );
                         }}>
                         {strVal}
@@ -162,42 +177,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     updateText = (nodeText: string, forceMatch: boolean) => {
         if (nodeText) {
             const newText = nodeText.startsWith(':=') || nodeText.startsWith('=:=') ? ':=-computed-' : nodeText;
-
-            // const json = {
-            //     doc: {
-            //         type: 'doc',
-            //         content: [
-            //             {
-            //                 type: 'paragraph',
-            //                 attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
-            //                 content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: '7e02a420-8add-49b0-ad20-54680567575f', hideKey: true, editable: false }, marks: [{ type: 'strong' }] }],
-            //             },
-            //             {
-            //                 type: 'paragraph',
-            //                 attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
-            //                 content: [{ type: 'text', marks: [{ type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334077 } }] }],
-            //             },
-            //         ],
-            //     },
-            // };
-
-            // const json = {
-            //     doc: {
-            //         type: 'doc',
-            //         content: [
-            //             {
-            //                 type: 'paragraph',
-            //                 attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
-            //                 content: [{ type: 'dashField', attrs: { fieldKey: 'hello', docid: '', hideKey: true, editable: true }, marks: [{ type: 'strong' }, { type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334006 } }] }],
-            //             },
-            //             {
-            //                 type: 'paragraph',
-            //                 attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
-            //                 content: [{ type: 'text', marks: [{ type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334088 } }] }],
-            //             },
-            //         ],
-            //     },
-            // };
             // look for a document whose id === the fieldKey being displayed.  If there's a match, then that document
             // holds the different enumerated values for the field in the titles of its collected documents.
             // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down.
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index e9f59f17d..4378c10f7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -393,13 +393,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         const newAutoLinks = new Set<Doc>();
         const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords);
         if (this._editorView?.state.doc.textContent) {
+            const isNodeSel = this._editorView.state.selection instanceof NodeSelection;
             const f = this._editorView.state.selection.from;
             const t = this._editorView.state.selection.to;
             var tr = this._editorView.state.tr as any;
             const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
             tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
             DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
-            tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
+            tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
         }
         oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
@@ -1485,7 +1486,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         document.removeEventListener('pointermove', this.onSelectMove);
     };
     onPointerUp = (e: React.PointerEvent): void => {
-        if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu();
+        if (!this._editorView?.state.selection.empty && !(this._editorView?.state.selection instanceof NodeSelection) && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu();
         if (!this._downEvent) return;
         this._downEvent = false;
         if (this.props.isContentActive(true) && !(e.nativeEvent as any).dash) {
@@ -1494,7 +1495,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
             let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
             while (target && !target.dataset?.targethrefs) target = target.parentElement;
-            FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc);
+            FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.noPreview);
         }
 
         if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index bdf59863b..e7ca26d5c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -1,5 +1,5 @@
 import { Mark, ResolvedPos } from 'prosemirror-model';
-import { EditorState } from 'prosemirror-state';
+import { EditorState, NodeSelection } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { Doc } from '../../../../fields/Doc';
 import { DocServer } from '../../../DocServer';
@@ -92,7 +92,7 @@ export class FormattedTextBoxComment {
         FormattedTextBoxComment.tooltip.style.display = '';
     }
 
-    static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = '', linkDoc: string = '') {
+    static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = '', linkDoc: string = '', noPreview: boolean = false) {
         FormattedTextBoxComment.textBox = textBox;
         if (hrefs || !lastState?.doc.eq(view.state.doc) || !lastState?.selection.eq(view.state.selection)) {
             FormattedTextBoxComment.setupPreview(
@@ -102,12 +102,13 @@ export class FormattedTextBoxComment {
                     ?.trim()
                     .split(' ')
                     .filter(h => h),
-                linkDoc
+                linkDoc,
+                noPreview
             );
         }
     }
 
-    static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[], linkDoc?: string) {
+    static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[], linkDoc?: string, noPreview?: boolean) {
         const state = view.state;
         // this section checks to see if the insertion point is over text entered by a different user.  If so, it sets ths comment text to indicate the user and the modification date
         if (state.selection.$from) {
@@ -130,8 +131,8 @@ export class FormattedTextBoxComment {
         if (state.selection.$from && hrefs?.length) {
             const nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
             const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef;
-            nbef &&
-                naft &&
+            //nbef &&
+            naft &&
                 LinkDocPreview.SetLinkInfo({
                     docProps: textBox.props,
                     linkSrc: textBox.rootDoc,
@@ -139,6 +140,7 @@ export class FormattedTextBoxComment {
                     location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))),
                     hrefs,
                     showHeader: true,
+                    noPreview,
                 });
         }
     }
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index dc5d8ada8..e3c67ad2e 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -274,7 +274,7 @@ export class RichTextRules {
                     const num = value.match(/^[0-9.]$/);
                     this.Document[DataSym][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
                 }
-                const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid, hideKey: true });
+                const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid, hideKey: false });
                 return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
             }),
 
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 00c41e187..4206a1006 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -75,6 +75,7 @@ export const marks: { [index: string]: MarkSpec } = {
             allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] },
             location: { default: null },
             title: { default: null },
+            noPreview: { default: false },
             docref: { default: false }, // flags whether the linked text comes from a document within Dash.  If so, an attribution label is appended after the text
         },
         inclusive: false,
@@ -85,6 +86,7 @@ export const marks: { [index: string]: MarkSpec } = {
                     return {
                         location: dom.getAttribute('location'),
                         title: dom.getAttribute('title'),
+                        noPreview: dom.getAttribute('noPreview'),
                     };
                 },
             },
@@ -111,7 +113,7 @@ export const marks: { [index: string]: MarkSpec } = {
                       ['br'],
                   ]
                 : //node.attrs.allLinks.length === 1 ?
-                  ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, location: node.attrs.location, style: `text-decoration: underline` }, 0];
+                  ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, noPreview: node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0];
             // ["div", { class: "prosemirror-anchor" },
             //     ["span", { class: "prosemirror-linkBtn" },
             //         ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
-- 
cgit v1.2.3-70-g09d2


From 31a51e9dda07e48c88166bffbc8f1ad7166cd624 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 4 Nov 2022 14:34:46 -0400
Subject: more fixes to sidebar annotations of pdfs to improve link following.

---
 src/client/util/LinkFollower.ts                         | 17 +++++++++++++++--
 src/client/views/DocumentDecorations.tsx                |  2 ++
 src/client/views/SidebarAnnos.tsx                       |  3 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx       |  4 +++-
 .../views/nodes/formattedText/DashDocCommentView.tsx    |  9 +++++----
 src/client/views/nodes/formattedText/DashFieldView.tsx  |  3 ---
 .../views/nodes/formattedText/FormattedTextBox.tsx      |  2 +-
 src/client/views/nodes/formattedText/marks_rts.ts       |  2 +-
 8 files changed, 29 insertions(+), 13 deletions(-)

(limited to 'src/client/views/SidebarAnnos.tsx')

diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index f75ac24f5..ea0531fa2 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,9 +1,10 @@
-import { action, observable, observe } from 'mobx';
+import { action, observable, observe, runInAction } from 'mobx';
 import { computedFn } from 'mobx-utils';
 import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { ProxyField } from '../../fields/Proxy';
 import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { DocumentDecorations } from '../views/DocumentDecorations';
 import { LightboxView } from '../views/LightboxView';
 import { DocumentViewSharedProps, ViewAdjustment } from '../views/nodes/DocumentView';
 import { DocumentManager } from './DocumentManager';
@@ -59,7 +60,19 @@ export class LinkFollower {
                 docViewProps.focus(sourceDoc, { willZoom: BoolCast(sourceDoc.followLinkZoom, true), scale: 1, afterFocus: createTabForTarget });
             }
         };
-        LinkFollower.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, zoom), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
+        runInAction(() => (DocumentDecorations.Instance.overrideBounds = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value
+        LinkFollower.traverseLink(
+            linkDoc,
+            sourceDoc,
+            createViewFunc,
+            BoolCast(sourceDoc.followLinkZoom, zoom),
+            docViewProps.ContainingCollectionDoc,
+            action(() => {
+                batch.end();
+                DocumentDecorations.Instance.overrideBounds = false;
+            }),
+            altKey ? true : undefined
+        );
     };
 
     public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 47347284c..cec03c991 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -70,8 +70,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         );
     }
 
+    @observable overrideBounds = false;
     @computed
     get Bounds() {
+        if (this.overrideBounds) return { x: 0, y: 0, r: 0, b: 0 };
         const views = SelectionManager.Views();
         return views
             .filter(dv => dv.props.renderDepth > 0)
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index c285b2e34..ec68a6b36 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -98,9 +98,10 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                             {
                                 type: 'linkAnchor',
                                 attrs: {
-                                    allAnchors: [{ href: `/doc/${target[Id]}`, title: 'Anchored Selection', noPreview: true, anchorId: `${target[Id]}` }],
+                                    allAnchors: [{ href: `/doc/${target[Id]}`, title: 'Anchored Selection', anchorId: `${target[Id]}` }],
                                     location: 'add:right',
                                     title: 'Anchored Selection',
+                                    noPreview: true,
                                     docref: false,
                                 },
                             },
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2f246e74f..ac3777aa6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1229,6 +1229,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const cx = NumCast(this.layoutDoc._panX) + (this.props.isAnnotationOverlay ? pw / 2 : 0);
         const cy = NumCast(this.layoutDoc._panY) + (this.props.isAnnotationOverlay ? ph / 2 : 0);
         const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
+        const maxYShift = Math.max(0, screen.bot - screen.top - (bounds.bot - bounds.top));
+        const phborder = bounds.top < screen.top || bounds.bot > screen.bot ? Math.min(ph / 10, maxYShift / 2) : 0;
         if (screen.right - screen.left < bounds.right - bounds.left || screen.bot - screen.top < bounds.bot - bounds.top) {
             return {
                 panX: (bounds.left + bounds.right) / 2,
@@ -1238,7 +1240,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
         return {
             panX: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panX) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
-            panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panY) : cy) + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot),
+            panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panY) : cy) + Math.min(0, bounds.top - phborder - screen.top) + Math.max(0, bounds.bot + phborder - screen.bot),
         };
     };
 
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index 85523c176..4bff57842 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -36,11 +36,12 @@ export class DashDocCommentView {
         (this as any).dom = this.dom;
     }
 
-    destroy() {
-        // ReactDOM.unmountComponentAtNode(this.dom);
+    deselectNode() {
+        this.dom.classList.remove('ProseMirror-selectednode');
+    }
+    selectNode() {
+        this.dom.classList.add('ProseMirror-selectednode');
     }
-
-    selectNode() {}
 }
 
 interface IDashDocCommentViewInternal {
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index fbcf25215..afae59733 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -17,7 +17,6 @@ import { Tooltip } from '@material-ui/core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { NodeSelection } from 'prosemirror-state';
-import { FormattedTextBoxComment } from './FormattedTextBoxComment';
 
 export class DashFieldView {
     dom: HTMLDivElement; // container for label and value
@@ -29,7 +28,6 @@ export class DashFieldView {
         this.dom.style.height = node.attrs.height;
         this.dom.style.position = 'relative';
         this.dom.style.display = 'inline-block';
-        //this.dom.contentEditable = 'true';
         this.dom.onkeypress = function (e: any) {
             e.stopPropagation();
         };
@@ -56,7 +54,6 @@ export class DashFieldView {
     }
     selectNode() {
         this.dom.classList.add('ProseMirror-selectednode');
-        console.log('SELECT');
     }
 }
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 4378c10f7..63435eea8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1495,7 +1495,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
             let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
             while (target && !target.dataset?.targethrefs) target = target.parentElement;
-            FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.noPreview);
+            FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview);
         }
 
         if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 4206a1006..97549830c 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -113,7 +113,7 @@ export const marks: { [index: string]: MarkSpec } = {
                       ['br'],
                   ]
                 : //node.attrs.allLinks.length === 1 ?
-                  ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, noPreview: node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0];
+                  ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0];
             // ["div", { class: "prosemirror-anchor" },
             //     ["span", { class: "prosemirror-linkBtn" },
             //         ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
-- 
cgit v1.2.3-70-g09d2

