From c6d1059e24f362a167b9ac24e6f13d1e45361da9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 29 Nov 2022 12:05:29 -0500
Subject: changes to streamline link editing UI (got rid of LinkEditor). 
 cleaned up link (un)highlighting.

---
 .../collections/collectionFreeForm/CollectionFreeFormLinksView.tsx      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index b8344dc0c..9e360f557 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -2,10 +2,10 @@ import { computed } from 'mobx';
 import { observer } from 'mobx-react';
 import { Id } from '../../../../fields/FieldSymbols';
 import { DocumentManager } from '../../../util/DocumentManager';
+import { LightboxView } from '../../LightboxView';
 import './CollectionFreeFormLinksView.scss';
 import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
 import React = require('react');
-import { LightboxView } from '../../LightboxView';
 
 @observer
 export class CollectionFreeFormLinksView extends React.Component<React.PropsWithChildren<{}>> {
-- 
cgit v1.2.3-70-g09d2


From f61092c162378f8884335b0988058420b7982f7d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 29 Nov 2022 15:31:04 -0500
Subject: fixed docDecoration appearance after following link and clicking
 before highlight times out.  fixed selection of links to allow setting
 background color of link line and link text box.  added background for link
 description text and  highlighting of link when clicked/selected.

---
 src/client/util/SelectionManager.ts                |   6 +-
 src/client/views/MainView.tsx                      |   6 +-
 src/client/views/PropertiesView.tsx                | 199 +++++++++++----------
 .../CollectionFreeFormLinkView.scss                |   9 +-
 .../CollectionFreeFormLinkView.tsx                 |  56 ++++--
 .../CollectionFreeFormLinksView.tsx                |   3 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   3 +-
 src/client/views/nodes/button/FontIconBox.tsx      |   7 +-
 src/fields/Doc.ts                                  |   2 +-
 9 files changed, 159 insertions(+), 132 deletions(-)

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index a3d6f5227..646942569 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,9 +1,10 @@
 import { action, observable, ObservableMap } from 'mobx';
 import { computedFn } from 'mobx-utils';
-import { Doc, Opt } from '../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { DocCast } from '../../fields/Types';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocumentView } from '../views/nodes/DocumentView';
+import { LinkManager } from './LinkManager';
 import { ScriptingGlobals } from './ScriptingGlobals';
 
 export namespace SelectionManager {
@@ -21,6 +22,9 @@ export namespace SelectionManager {
             // if doc is not in SelectedDocuments, add it
             if (!manager.SelectedViews.get(docView) && docView.props.Document.type !== DocumentType.MARKER) {
                 if (!ctrlPressed) {
+                    if (LinkManager.currentLink && !DocListCast(docView.rootDoc.links).includes(LinkManager.currentLink) && docView.rootDoc !== LinkManager.currentLink) {
+                        LinkManager.currentLink = undefined;
+                    }
                     this.DeselectAll();
                 }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 09063901d..165bb69bb 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -3,15 +3,15 @@ import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons';
 import * as far from '@fortawesome/free-regular-svg-icons';
 import * as fa from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import 'browndash-components/dist/styles/global.min.css';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import 'normalize.css';
 import * as React from 'react';
-import * as ReactDOM from 'react-dom/client';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { ScriptField } from '../../fields/ScriptField';
 import { DocCast, StrCast } from '../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocUtils } from '../documents/Documents';
@@ -58,14 +58,12 @@ import { LinkDocPreview } from './nodes/LinkDocPreview';
 import { RadialMenu } from './nodes/RadialMenu';
 import { TaskCompletionBox } from './nodes/TaskCompletedBox';
 import { PresBox } from './nodes/trails';
-import { WebBox } from './nodes/WebBox';
 import { OverlayView } from './OverlayView';
 import { AnchorMenu } from './pdf/AnchorMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { PropertiesView } from './PropertiesView';
 import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
-import 'browndash-components/dist/styles/global.min.css';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index e8fd540a8..05dd376f2 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1549,6 +1549,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const zoom = Number((NumCast(this.sourceAnchor?.presZoom, 1) * 100).toPrecision(3));
         const targZoom = this.sourceAnchor?.followLinkZoom;
         const indent = 30;
+        const hasSelectedAnchor = SelectionManager.Views().some(dv => DocListCast(this.sourceAnchor?.links).includes(LinkManager.currentLink!));
         if (!this.selectedDoc && !this.isPres) {
             return (
                 <div className="propertiesView" style={{ width: this.props.width }}>
@@ -1575,7 +1576,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         {this.contextsSubMenu}
 
                         {this.linksSubMenu}
-                        {!this.selectedDoc || !LinkManager.currentLink || !SelectionManager.Views().some(dv => DocListCast(this.sourceAnchor?.links).includes(LinkManager.currentLink!)) ? null : (
+                        {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : (
                             <>
                                 <div className="propertiesView-section" style={{ background: 'darkgray' }}>
                                     <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
@@ -1617,108 +1618,110 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                         </button>
                                     </div>
                                 </div>
-                                <div className="propertiesView-section">
-                                    <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
-                                        <p>Follow by</p>
-                                        <select onChange={e => this.changeFollowBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkLocation, 'default')}>
-                                            <option value="default">Default</option>
-                                            <option value="add:left">Opening in new left pane</option>
-                                            <option value="add:right">Opening in new right pane</option>
-                                            <option value="replace:left">Replacing left tab</option>
-                                            <option value="replace:right">Replacing right tab</option>
-                                            <option value="fullScreen">Opening full screen</option>
-                                            <option value="add">Opening in new tab</option>
-                                            <option value="replace">Replacing current tab</option>
-                                            <option value="inPlace">Opening in place</option>
-                                            {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
-                                        </select>
-                                    </div>
-                                    <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
-                                        <p>Animation</p>
-                                        <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.presEffect, 'default')}>
-                                            <option value="default">Default</option>
-                                            {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
-                                                <option value={effect.toString()}>{effect.toString()}</option>
-                                            ))}
-                                        </select>
-                                        <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
-                                            {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
-                                            {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
-                                            {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
-                                            {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
-                                            {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+                                {!hasSelectedAnchor ? null : (
+                                    <div className="propertiesView-section">
+                                        <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
+                                            <p>Follow by</p>
+                                            <select onChange={e => this.changeFollowBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkLocation, 'default')}>
+                                                <option value="default">Default</option>
+                                                <option value="add:left">Opening in new left pane</option>
+                                                <option value="add:right">Opening in new right pane</option>
+                                                <option value="replace:left">Replacing left tab</option>
+                                                <option value="replace:right">Replacing right tab</option>
+                                                <option value="fullScreen">Opening full screen</option>
+                                                <option value="add">Opening in new tab</option>
+                                                <option value="replace">Replacing current tab</option>
+                                                <option value="inPlace">Opening in place</option>
+                                                {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
+                                            </select>
                                         </div>
-                                    </div>
-                                    {PresBox.inputter(
-                                        '0.1',
-                                        '0.1',
-                                        '10',
-                                        NumCast(this.sourceAnchor?.presTransition) / 1000,
-                                        true,
-                                        (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.presTransition = timeInMS)),
-                                        indent
-                                    )}{' '}
-                                    <div
-                                        className={'slider-headers'}
-                                        style={{
-                                            display: 'grid',
-                                            justifyContent: 'space-between',
-                                            width: `calc(100% - ${indent * 2}px)`,
-                                            marginLeft: indent,
-                                            marginRight: indent,
-                                            gridTemplateColumns: 'auto auto',
-                                            borderTop: 'solid',
-                                        }}>
-                                        <div className="slider-text">Fast</div>
-                                        <div className="slider-text">Slow</div>
-                                    </div>{' '}
-                                    <div className="propertiesView-input inline">
-                                        <p>Play Target Audio</p>
-                                        <button
-                                            style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
-                                            onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
-                                            onClick={e => e.stopPropagation()}
-                                            className="propertiesButton">
-                                            <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                                        </button>
-                                    </div>
-                                    <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
-                                        <p>Zoom %</p>
-                                        <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
-                                            <input className="presBox-input" style={{ width: '100%' }} type="number" value={zoom} />
-                                            <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
-                                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
-                                                    <FontAwesomeIcon icon={'caret-up'} />
-                                                </div>
-                                                <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
-                                                    <FontAwesomeIcon icon={'caret-down'} />
+                                        <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
+                                            <p>Animation</p>
+                                            <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.presEffect, 'default')}>
+                                                <option value="default">Default</option>
+                                                {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
+                                                    <option value={effect.toString()}>{effect.toString()}</option>
+                                                ))}
+                                            </select>
+                                            <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
+                                                {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+                                                {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+                                                {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+                                                {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+                                                {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+                                            </div>
+                                        </div>
+                                        {PresBox.inputter(
+                                            '0.1',
+                                            '0.1',
+                                            '10',
+                                            NumCast(this.sourceAnchor?.presTransition) / 1000,
+                                            true,
+                                            (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.presTransition = timeInMS)),
+                                            indent
+                                        )}{' '}
+                                        <div
+                                            className={'slider-headers'}
+                                            style={{
+                                                display: 'grid',
+                                                justifyContent: 'space-between',
+                                                width: `calc(100% - ${indent * 2}px)`,
+                                                marginLeft: indent,
+                                                marginRight: indent,
+                                                gridTemplateColumns: 'auto auto',
+                                                borderTop: 'solid',
+                                            }}>
+                                            <div className="slider-text">Fast</div>
+                                            <div className="slider-text">Slow</div>
+                                        </div>{' '}
+                                        <div className="propertiesView-input inline">
+                                            <p>Play Target Audio</p>
+                                            <button
+                                                style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
+                                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
+                                                onClick={e => e.stopPropagation()}
+                                                className="propertiesButton">
+                                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                            </button>
+                                        </div>
+                                        <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
+                                            <p>Zoom %</p>
+                                            <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
+                                                <input className="presBox-input" style={{ width: '100%' }} type="number" value={zoom} />
+                                                <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
+                                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
+                                                        <FontAwesomeIcon icon={'caret-up'} />
+                                                    </div>
+                                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
+                                                        <FontAwesomeIcon icon={'caret-down'} />
+                                                    </div>
                                                 </div>
                                             </div>
+                                            <button
+                                                style={{ background: !targZoom ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
+                                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+                                                onClick={e => e.stopPropagation()}
+                                                className="propertiesButton">
+                                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                            </button>
                                         </div>
-                                        <button
-                                            style={{ background: !targZoom ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
-                                            onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
-                                            onClick={e => e.stopPropagation()}
-                                            className="propertiesButton">
-                                            <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                                        </button>
+                                        {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
+                                        <div
+                                            className={'slider-headers'}
+                                            style={{
+                                                display: !targZoom ? 'none' : 'grid',
+                                                justifyContent: 'space-between',
+                                                width: `calc(100% - ${indent * 2}px)`,
+                                                marginLeft: indent,
+                                                marginRight: indent,
+                                                gridTemplateColumns: 'auto auto',
+                                                borderTop: 'solid',
+                                            }}>
+                                            <div className="slider-text">0%</div>
+                                            <div className="slider-text">100%</div>
+                                        </div>{' '}
                                     </div>
-                                    {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
-                                    <div
-                                        className={'slider-headers'}
-                                        style={{
-                                            display: !targZoom ? 'none' : 'grid',
-                                            justifyContent: 'space-between',
-                                            width: `calc(100% - ${indent * 2}px)`,
-                                            marginLeft: indent,
-                                            marginRight: indent,
-                                            gridTemplateColumns: 'auto auto',
-                                            borderTop: 'solid',
-                                        }}>
-                                        <div className="slider-text">0%</div>
-                                        <div className="slider-text">100%</div>
-                                    </div>{' '}
-                                </div>
+                                )}
                             </>
                         )}
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index 858719a08..b44acfce8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -5,15 +5,8 @@
     transition: opacity 0.5s ease-in;
     fill: transparent;
 }
-.collectionfreeformlinkview-linkCircle {
-    stroke: rgb(0,0,0);
-    opacity: 0.5;
-    pointer-events: all;
-    cursor: pointer;
-}
 .collectionfreeformlinkview-linkText {
-    stroke: rgb(0,0,0);
-    opacity: 0.5;
+    stroke: rgb(0, 0, 0);
     pointer-events: all;
     cursor: move;
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index b39fcbd48..be20bf207 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
 import { Doc, Field } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { List } from '../../../../fields/List';
-import { Cast, NumCast } from '../../../../fields/Types';
+import { Cast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { LinkManager } from '../../../util/LinkManager';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -117,12 +117,16 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
                 return false;
             },
             emptyFunction,
-            () => {
+            action(() => {
+                SelectionManager.DeselectAll();
+                SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
+                LinkManager.currentLink = this.props.LinkDocs[0];
+                this.toggleProperties();
                 // OverlayView.Instance.addElement(
                 //     <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
                 //         showLinks={action(() => { })}
                 //     />, { x: 300, y: 300 });
-            }
+            })
         );
     };
 
@@ -168,8 +172,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         }
     };
 
+    @action
     onClickLine = () => {
+        SelectionManager.DeselectAll();
         SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
+        LinkManager.currentLink = this.props.LinkDocs[0];
         this.toggleProperties();
     };
 
@@ -232,44 +239,65 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     render() {
         if (!this.renderData) return null;
 
+        const link = this.props.LinkDocs[0];
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
         const linkRelationship = Field.toString(LinkManager.currentLink?.linkRelationship as any as Field); //get string representing relationship
         const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List<string>;
         const linkColorList = Doc.UserDoc().linkColorList as List<string>;
         const linkRelationshipSizes = Doc.UserDoc().linkRelationshipSizes as List<number>;
         const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship);
+        const linkDescription = Field.toString(link.description as any as Field);
 
         const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
 
         //access stroke color using index of the relationship in the color list (default black)
-        const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? 'black' : linkColorList[currRelationshipIndex];
+        const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? StrCast(link._backgroundColor, 'black') : linkColorList[currRelationshipIndex];
         // const hexStroke = this.rgbToHex(stroke)
 
         //calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has)
         //thickness varies linearly from 3px to 12px for increasing link count
         const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
 
-        if (this.props.LinkDocs[0].linkDisplayArrow === undefined) {
-            this.props.LinkDocs[0].linkDisplayArrow = false;
+        if (link.linkDisplayArrow === undefined) {
+            link.linkDisplayArrow = false;
         }
 
-        return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || (!this.props.LinkDocs[0].linkDisplay && !aActive && !bActive) ? null : (
+        return link.opacity === 0 || !a.width || !b.width || (!link.linkDisplay && !aActive && !bActive) ? null : (
             <>
                 <defs>
-                    <marker id="arrowhead" markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
-                        <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
+                    <marker id={`${link[Id] + 'arrowhead'}`} markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
+                        <polygon points="0 0, 3 1.5, 0 3" fill={stroke} />
                     </marker>
+                    <filter id="outline">
+                        <feMorphology in="SourceAlpha" result="expanded" operator="dilate" radius="1" />
+                        <feFlood flood-color={`${Colors.DARK_GRAY}`} />
+                        <feComposite in2="expanded" operator="in" />
+                        <feComposite in="SourceGraphic" />
+                    </filter>
+                    <filter x="0" y="0" width="1" height="1" id={`${link[Id] + 'background'}`}>
+                        <feFlood flood-color={`${StrCast(link._backgroundColor, 'white')}`} result="bg" />
+                        <feMerge>
+                            <feMergeNode in="bg" />
+                            <feMergeNode in="SourceGraphic" />
+                        </feMerge>
+                    </filter>
                 </defs>
                 <path
+                    filter={LinkManager.currentLink === link ? 'url(#outline)' : ''}
+                    fill="pink"
+                    stroke="antiquewhite"
+                    stroke-width="4"
                     className="collectionfreeformlinkview-linkLine"
-                    style={{ pointerEvents: 'all', opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
+                    style={{ pointerEvents: 'all', opacity: this._opacity, stroke, strokeWidth }}
                     onClick={this.onClickLine}
                     d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
-                    markerEnd={this.props.LinkDocs[0].linkDisplayArrow ? 'url(#arrowhead)' : ''}
+                    markerEnd={link.linkDisplayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
                 />
-                {textX === undefined ? null : (
-                    <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
-                        {Field.toString(this.props.LinkDocs[0].description as any as Field)}
+                {textX === undefined || !linkDescription ? null : (
+                    <text filter={`url(#${link[Id] + 'background'})`} className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
+                        <tspan>&nbsp;</tspan>
+                        <tspan dy="2">{linkDescription}</tspan>
+                        <tspan dy="2">&nbsp;</tspan>
                     </text>
                 )}
             </>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 9e360f557..420e6a318 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -8,7 +8,7 @@ import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
 import React = require('react');
 
 @observer
-export class CollectionFreeFormLinksView extends React.Component<React.PropsWithChildren<{}>> {
+export class CollectionFreeFormLinksView extends React.Component {
     @computed get uniqueConnections() {
         return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews))
             .filter(c => !LightboxView.LightboxDoc || (LightboxView.IsLightboxDocView(c.a.docViewPath) && LightboxView.IsLightboxDocView(c.b.docViewPath)))
@@ -19,7 +19,6 @@ export class CollectionFreeFormLinksView extends React.Component<React.PropsWith
         return (
             <div className="collectionfreeformlinksview-container">
                 <svg className="collectionfreeformlinksview-svgCanvas">{this.uniqueConnections}</svg>
-                {this.props.children}
             </div>
         );
     }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a020b67cd..3fd6ca803 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -17,6 +17,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
+import { OpenWhere } from '../../nodes/DocumentView';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
 import { PresBox } from '../../nodes/trails/PresBox';
 import { VideoBox } from '../../nodes/VideoBox';
@@ -120,7 +121,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const cm = ContextMenu.Instance;
         const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
         if (e.key === '?') {
-            cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), 'add:right'));
+            cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), OpenWhere.addRight));
 
             cm.displayMenu(this._downX, this._downY, undefined, true);
             e.stopPropagation();
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index c71552a6f..fe8c85e5e 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -13,6 +13,7 @@ import { WebField } from '../../../../fields/URLField';
 import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
 import { aggregateBounds, StopEvent, Utils } from '../../../../Utils';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { LinkManager } from '../../../util/LinkManager';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
@@ -547,11 +548,11 @@ ScriptingGlobals.add(function setView(view: string) {
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement();
+    const selected = SelectionManager.Views().lastElement()?.props.Document ?? LinkManager.currentLink;
     if (checkResult) {
-        return selected?.props.Document._backgroundColor ?? 'transparent';
+        return selected?._backgroundColor ?? 'transparent';
     }
-    if (selected) selected.props.Document._backgroundColor = color;
+    if (selected) selected._backgroundColor = color;
 });
 
 // toggle: Set overlay status of selected document
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index c6cabe269..40152551e 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1265,6 +1265,7 @@ export namespace Doc {
     }
 
     export function linkFollowUnhighlight() {
+        UnhighlightWatchers.forEach(watcher => watcher());
         UnhighlightWatchers.length = 0;
         highlightedDocs.forEach(doc => Doc.UnHighlightDoc(doc));
         document.removeEventListener('pointerdown', linkFollowUnhighlight);
@@ -1284,7 +1285,6 @@ export namespace Doc {
         document.addEventListener('pointerdown', linkFollowUnhighlight);
         if (UnhighlightTimer) clearTimeout(UnhighlightTimer);
         UnhighlightTimer = window.setTimeout(() => {
-            UnhighlightWatchers.forEach(watcher => watcher());
             linkFollowUnhighlight();
             UnhighlightTimer = 0;
         }, 5000);
-- 
cgit v1.2.3-70-g09d2

