From dd5cfe5302279d708bd8fbc7b9cad7ea082758c4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 13 Oct 2022 10:39:33 -0400
Subject: some basic error checking.   avoid querying background for non-toggle
 buttons

---
 src/client/views/nodes/formattedText/DashDocView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/nodes/formattedText/DashDocView.tsx')

diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 73a711b9d..5f576be41 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -149,7 +149,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._spanRef.current);
         return new Transform(-translateX, -translateY, 1).scale(1 / scale);
     };
-    outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target
+    outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document, {}); // ideally, this would scroll to show the focus target
 
     onKeyDown = (e: any) => {
         e.stopPropagation();
-- 
cgit v1.2.3-70-g09d2


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/goldenLayout.js                         |  11 +-
 src/client/util/DragManager.ts                     |   2 +-
 src/client/util/HypothesisUtils.ts                 | 160 ++++++++++++---------
 src/client/views/DocumentDecorations.scss          |   5 +-
 src/client/views/DocumentDecorations.tsx           |   9 +-
 src/client/views/GlobalKeyHandler.ts               |   2 +-
 src/client/views/Main.tsx                          |   7 +-
 src/client/views/MainView.scss                     |   9 --
 src/client/views/MainView.tsx                      |  94 +-----------
 src/client/views/MarqueeAnnotator.tsx              |   6 +-
 src/client/views/SidebarAnnos.tsx                  |  22 +++
 src/client/views/StyleProvider.tsx                 |   6 +-
 .../views/collections/CollectionDockingView.scss   |   4 +
 .../views/collections/CollectionDockingView.tsx    |  17 ++-
 src/client/views/collections/TabDocView.tsx        |   6 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |   3 -
 src/client/views/nodes/PDFBox.tsx                  |  13 +-
 src/client/views/nodes/WebBox.tsx                  |  14 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  12 +-
 .../nodes/formattedText/DashDocCommentView.tsx     |   8 +-
 .../views/nodes/formattedText/DashDocView.tsx      |  12 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |  53 +++++--
 .../views/nodes/formattedText/EquationView.tsx     |  10 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   1 +
 .../views/nodes/formattedText/RichTextRules.ts     |   7 +-
 .../views/nodes/formattedText/SummaryView.tsx      |   9 +-
 src/client/views/nodes/formattedText/nodes_rts.ts  |   1 +
 src/client/views/pdf/PDFViewer.scss                |  12 +-
 src/client/views/pdf/PDFViewer.tsx                 |   6 +-
 src/debug/Repl.tsx                                 |  37 +++--
 30 files changed, 278 insertions(+), 280 deletions(-)

(limited to 'src/client/views/nodes/formattedText/DashDocView.tsx')

diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index dd11e6466..bc08b4d0b 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -459,7 +459,7 @@
                     this._bDragging = false;
                     this.emit('dragStop', oEvent, this._nOriginalX + this._nX);
                 } else  { // make title receive pointer events to allow setting insertion position or selecting texst range
-                    const classname = typeof oEvent.target?.className === "string" ? oEvent.target.className : "";
+                    const classname = oEvent.target ? (typeof oEvent.target.className === "string" ? oEvent.target.className : ""): "";
                     if (classname.includes("lm_title_wrap")) {
                         oEvent.target.children[0].style.pointerEvents = "all";
                         oEvent.target.children[0].focus();
@@ -5391,10 +5391,11 @@
          * @returns {void}
          */
         _render: function () {
-            this._reactComponent = ReactDOM.render(this._getReactComponent(), this._container.getElement()[0]);
-            this._originalComponentWillUpdate = this._reactComponent.componentWillUpdate || function () {
+            this._reactComponent = ReactDOM.createRoot(this._container.getElement()[0])
+            this._reactComponent.render(this._getReactComponent());
+            this._originalComponentWillUpdate = this._reactComponent.componentDidUpdate || function () {
             };
-            this._reactComponent.componentWillUpdate = this._onUpdate.bind(this);
+            this._reactComponent.componentDidUpdate = this._onUpdate.bind(this);
             if (this._container.getState()) {
                 this._reactComponent.setState(this._container.getState());
             }
@@ -5407,7 +5408,7 @@
          * @returns {void}
          */
         _destroy: function () {
-            ReactDOM.unmountComponentAtNode(this._container.getElement()[0]);
+          //  ReactDOM.unmountComponentAtNode(this._container.getElement()[0]);
             this._container.off('open', this._render, this);
             this._container.off('destroy', this._destroy, this);
         },
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 36e5a65fb..d0690fa10 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -450,7 +450,7 @@ export namespace DragManager {
         const hideDragShowOriginalElements = (hide: boolean) => {
             dragLabel.style.display = hide ? '' : 'none';
             !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
-            eles.forEach(ele => (ele.hidden = hide));
+            setTimeout(() => eles.forEach(ele => (ele.hidden = hide)));
         };
         options?.hideSource && hideDragShowOriginalElements(true);
 
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index e910a9118..297520d5d 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -1,30 +1,28 @@
-import { StrCast, Cast } from "../../fields/Types";
-import { SearchUtil } from "./SearchUtil";
-import { action, runInAction } from "mobx";
-import { Doc, Opt } from "../../fields/Doc";
-import { DocumentType } from "../documents/DocumentTypes";
-import { Docs } from "../documents/Documents";
-import { SelectionManager } from "./SelectionManager";
-import { WebField } from "../../fields/URLField";
-import { DocumentManager } from "./DocumentManager";
-import { DocumentLinksButton } from "../views/nodes/DocumentLinksButton";
-import { simulateMouseClick, Utils } from "../../Utils";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { Id } from "../../fields/FieldSymbols";
+import { StrCast, Cast } from '../../fields/Types';
+import { SearchUtil } from './SearchUtil';
+import { action, runInAction } from 'mobx';
+import { Doc, Opt } from '../../fields/Doc';
+import { DocumentType } from '../documents/DocumentTypes';
+import { Docs } from '../documents/Documents';
+import { SelectionManager } from './SelectionManager';
+import { WebField } from '../../fields/URLField';
+import { DocumentManager } from './DocumentManager';
+import { DocumentLinksButton } from '../views/nodes/DocumentLinksButton';
+import { simulateMouseClick, Utils } from '../../Utils';
+import { DocumentView } from '../views/nodes/DocumentView';
+import { Id } from '../../fields/FieldSymbols';
 
 export namespace Hypothesis {
-
     /**
-     * Retrieve a WebDocument with the given url, prioritizing results that are on screen.  
+     * Retrieve a WebDocument with the given url, prioritizing results that are on screen.
      * If none exist, create and return a new WebDocument.
      */
     export const getSourceWebDoc = async (uri: string) => {
         const result = await findWebDoc(uri);
-        console.log(result ? "existing doc found" : "existing doc NOT found");
+        console.log(result ? 'existing doc found' : 'existing doc NOT found');
         return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
     };
 
-
     /**
      * Search for a WebDocument whose url field matches the given uri, return undefined if not found
      */
@@ -33,18 +31,18 @@ export namespace Hypothesis {
         if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
 
         const results: Doc[] = [];
-        await SearchUtil.Search("web", true).then(action(async (res: SearchUtil.DocSearchResult) => {
-            const docs = res.docs;
-            const filteredDocs = docs.filter(doc =>
-                doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data
-            );
-            filteredDocs.forEach(doc => {
-                uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history? 
-            });
-        }));
+        await SearchUtil.Search('web', true).then(
+            action(async (res: SearchUtil.DocSearchResult) => {
+                const docs = res.docs;
+                const filteredDocs = docs.filter(doc => doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data);
+                filteredDocs.forEach(doc => {
+                    uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
+                });
+            })
+        );
 
         const onScreenResults = results.filter(doc => DocumentManager.Instance.getFirstDocumentView(doc));
-        return onScreenResults.length ? onScreenResults[0] : (results.length ? results[0] : undefined); // prioritize results that are currently on the screen
+        return onScreenResults.length ? onScreenResults[0] : results.length ? results[0] : undefined; // prioritize results that are currently on the screen
     };
 
     /**
@@ -52,17 +50,19 @@ export namespace Hypothesis {
      */
     export const linkListener = async (e: any) => {
         const annotationId: string = e.detail.id;
-        const annotationUri: string = StrCast(e.detail.uri).split("#annotations:")[0]; // clean hypothes.is URLs that reference a specific annotation 
+        const annotationUri: string = StrCast(e.detail.uri).split('#annotations:')[0]; // clean hypothes.is URLs that reference a specific annotation
         const sourceDoc: Doc = await getSourceWebDoc(annotationUri);
 
-        if (!DocumentLinksButton.StartLink || sourceDoc === DocumentLinksButton.StartLink) { // start new link if there were none already started, or if the old startLink came from the same web document (prevent links to itself)
+        if (!DocumentLinksButton.StartLink || sourceDoc === DocumentLinksButton.StartLink) {
+            // start new link if there were none already started, or if the old startLink came from the same web document (prevent links to itself)
             runInAction(() => {
                 DocumentLinksButton.AnnotationId = annotationId;
                 DocumentLinksButton.AnnotationUri = annotationUri;
                 DocumentLinksButton.StartLink = sourceDoc;
                 DocumentLinksButton.StartLinkView = undefined;
             });
-        } else { // if a link has already been started, complete the link to sourceDoc
+        } else {
+            // if a link has already been started, complete the link to sourceDoc
             runInAction(() => {
                 DocumentLinksButton.AnnotationId = annotationId;
                 DocumentLinksButton.AnnotationUri = annotationUri;
@@ -81,31 +81,41 @@ export namespace Hypothesis {
     export const makeLink = async (title: string, url: string, annotationId: string, annotationSourceDoc: Doc) => {
         // if the annotation's source webpage isn't currently loaded in Dash, we're not able to access and edit the annotation from the client
         // so we're loading the webpage and its annotations invisibly in a WebBox in MainView.tsx, until the editing is done
-        !DocumentManager.Instance.getFirstDocumentView(annotationSourceDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = annotationSourceDoc);
+        //!DocumentManager.Instance.getFirstDocumentView(annotationSourceDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = annotationSourceDoc);
 
         var success = false;
         const onSuccess = action(() => {
-            console.log("Edit success!!");
+            console.log('Edit success!!');
             success = true;
             clearTimeout(interval);
-            DocumentLinksButton.invisibleWebDoc = undefined;
-            document.removeEventListener("editSuccess", onSuccess);
+            //DocumentLinksButton.invisibleWebDoc = undefined;
+            document.removeEventListener('editSuccess', onSuccess);
         });
 
         const newHyperlink = `[${title}\n](${url})`;
-        const interval = setInterval(() => // keep trying to edit until annotations have loaded and editing is successful
-            !success && document.dispatchEvent(new CustomEvent<{ newHyperlink: string, id: string }>("addLink", {
-                detail: { newHyperlink: newHyperlink, id: annotationId },
-                bubbles: true
-            })), 300);
-
-        setTimeout(action(() => {
-            if (!success) {
-                clearInterval(interval);
-                DocumentLinksButton.invisibleWebDoc = undefined;
-            }
-        }), 10000); // give up if no success after 10s
-        document.addEventListener("editSuccess", onSuccess);
+        const interval = setInterval(
+            () =>
+                // keep trying to edit until annotations have loaded and editing is successful
+                !success &&
+                document.dispatchEvent(
+                    new CustomEvent<{ newHyperlink: string; id: string }>('addLink', {
+                        detail: { newHyperlink: newHyperlink, id: annotationId },
+                        bubbles: true,
+                    })
+                ),
+            300
+        );
+
+        setTimeout(
+            action(() => {
+                if (!success) {
+                    clearInterval(interval);
+                    //DocumentLinksButton.invisibleWebDoc = undefined;
+                }
+            }),
+            10000
+        ); // give up if no success after 10s
+        document.addEventListener('editSuccess', onSuccess);
     };
 
     /**
@@ -114,33 +124,40 @@ export namespace Hypothesis {
     export const deleteLink = async (linkDoc: Doc, sourceDoc: Doc, destinationDoc: Doc) => {
         if (Cast(destinationDoc.data, WebField)?.url.href !== StrCast(linkDoc.annotationUri)) return; // check that the destinationDoc is a WebDocument containing the target annotation
 
-        !DocumentManager.Instance.getFirstDocumentView(destinationDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = destinationDoc); // see note in makeLink
+        //!DocumentManager.Instance.getFirstDocumentView(destinationDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = destinationDoc); // see note in makeLink
 
         var success = false;
         const onSuccess = action(() => {
-            console.log("Edit success!");
+            console.log('Edit success!');
             success = true;
             clearTimeout(interval);
-            DocumentLinksButton.invisibleWebDoc = undefined;
-            document.removeEventListener("editSuccess", onSuccess);
+            // DocumentLinksButton.invisibleWebDoc = undefined;
+            document.removeEventListener('editSuccess', onSuccess);
         });
 
         const annotationId = StrCast(linkDoc.annotationId);
         const linkUrl = Doc.globalServerPath(sourceDoc);
-        const interval = setInterval(() => {// keep trying to edit until annotations have loaded and editing is successful
-            !success && document.dispatchEvent(new CustomEvent<{ targetUrl: string, id: string }>("deleteLink", {
-                detail: { targetUrl: linkUrl, id: annotationId },
-                bubbles: true
-            }));
+        const interval = setInterval(() => {
+            // keep trying to edit until annotations have loaded and editing is successful
+            !success &&
+                document.dispatchEvent(
+                    new CustomEvent<{ targetUrl: string; id: string }>('deleteLink', {
+                        detail: { targetUrl: linkUrl, id: annotationId },
+                        bubbles: true,
+                    })
+                );
         }, 300);
 
-        setTimeout(action(() => {
-            if (!success) {
-                clearInterval(interval);
-                DocumentLinksButton.invisibleWebDoc = undefined;
-            }
-        }), 10000); // give up if no success after 10s
-        document.addEventListener("editSuccess", onSuccess);
+        setTimeout(
+            action(() => {
+                if (!success) {
+                    clearInterval(interval);
+                    //DocumentLinksButton.invisibleWebDoc = undefined;
+                }
+            }),
+            10000
+        ); // give up if no success after 10s
+        document.addEventListener('editSuccess', onSuccess);
     };
 
     /**
@@ -149,17 +166,20 @@ export namespace Hypothesis {
     export const scrollToAnnotation = (annotationId: string, target: Doc) => {
         var success = false;
         const onSuccess = () => {
-            console.log("Scroll success!!");
+            console.log('Scroll success!!');
             document.removeEventListener('scrollSuccess', onSuccess);
             clearInterval(interval);
             success = true;
         };
 
-        const interval = setInterval(() => { // keep trying to scroll every 250ms until annotations have loaded and scrolling is successful
-            document.dispatchEvent(new CustomEvent('scrollToAnnotation', {
-                detail: annotationId,
-                bubbles: true
-            }));
+        const interval = setInterval(() => {
+            // keep trying to scroll every 250ms until annotations have loaded and scrolling is successful
+            document.dispatchEvent(
+                new CustomEvent('scrollToAnnotation', {
+                    detail: annotationId,
+                    bubbles: true,
+                })
+            );
             const targetView: Opt<DocumentView> = DocumentManager.Instance.getFirstDocumentView(target);
             const position = targetView?.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
             targetView && position && simulateMouseClick(targetView.ContentDiv!, position[0], position[1], position[0], position[1], false);
@@ -168,4 +188,4 @@ export namespace Hypothesis {
         document.addEventListener('scrollSuccess', onSuccess); // listen for success message from client
         setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
     };
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 4e0b061a6..fe1190e27 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -250,8 +250,8 @@ $resizeHandler: 8px;
     .documentDecorations-lock {
         position: absolute;
         background: black;
-        right: 11;
-        top: 30px;
+        right: 23px;
+        top: 3px;
         color: gray;
         height: 14;
         width: 14;
@@ -260,6 +260,7 @@ $resizeHandler: 8px;
         display: flex;
         align-items: center;
         flex-direction: column;
+        border-radius: 15%;
     }
 
     .documentDecorations-rotationPath {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 06eb6c6d7..8e0686038 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -782,6 +782,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
         bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
 
+        const useLock = bounds.r - bounds.x > 120;
         const useRotation = seldocview.rootDoc.type !== DocumentType.EQUATION; // when do we want an object to not rotate?
         const rotation = NumCast(seldocview.rootDoc._rotation);
 
@@ -856,9 +857,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                     onContextMenu={e => e.preventDefault()}
                                 />
                             )}
-                            <div key="lock" className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}>
-                                <FontAwesomeIcon size="sm" icon="lock" />
-                            </div>
+                            {!useLock ? null : (
+                                <div key="lock" className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}>
+                                    <FontAwesomeIcon size="sm" icon="lock" />
+                                </div>
+                            )}
 
                             {hideDocumentButtonBar ? null : (
                                 <div
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index c19e6831f..5a6caf995 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -139,7 +139,7 @@ export class KeyManager {
                 document.body.focus();
                 break;
             case 'enter': {
-                DocumentDecorations.Instance.onCloseClick(false);
+                //DocumentDecorations.Instance.onCloseClick(false);
                 break;
             }
             case 'delete':
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 3e6539c5e..f327f3184 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -3,9 +3,8 @@
 // }
 
 import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import { AssignAllExtensions } from '../../extensions/General/Extensions';
-import { Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
 import { Docs } from '../documents/Documents';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
@@ -14,7 +13,7 @@ import { ReplayMovements } from '../util/ReplayMovements';
 import { TrackMovements } from '../util/TrackMovements';
 import { CollectionView } from './collections/CollectionView';
 import { MainView } from './MainView';
-import * as dotenv from 'dotenv' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
+import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
 dotenv.config();
 
 AssignAllExtensions();
@@ -45,6 +44,6 @@ AssignAllExtensions();
         new LinkManager();
         new TrackMovements();
         new ReplayMovements();
-        ReactDOM.render(<MainView />, document.getElementById('root'));
+        ReactDOM.createRoot(document.getElementById('root')!).render(<MainView />);
     }, 0);
 })();
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index c5ac1cf52..069206126 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -371,12 +371,3 @@ h1,
     width: 200px;
     height: 800px;
 }
-
-.mainVew-invisibleWebRef {
-    position: absolute;
-    left: 50;
-    top: 50;
-    display: block;
-    width: 500px;
-    height: 1000px;
-}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 052846e71..9648a7807 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -7,7 +7,7 @@ import { action, computed, configure, observable, reaction, runInAction } from '
 import { observer } from 'mobx-react';
 import 'normalize.css';
 import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+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';
@@ -948,40 +948,6 @@ export class MainView extends React.Component {
         );
     }
 
-    @computed get invisibleWebBox() {
-        // see note under the makeLink method in HypothesisUtils.ts
-        return !DocumentLinksButton.invisibleWebDoc ? null : (
-            <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
-                <WebBox
-                    fieldKey={'data'}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
-                    Document={DocumentLinksButton.invisibleWebDoc}
-                    dropAction={'move'}
-                    styleProvider={undefined}
-                    isSelected={returnFalse}
-                    select={returnFalse}
-                    setHeight={returnFalse}
-                    rootSelected={returnFalse}
-                    renderDepth={0}
-                    addDocTab={returnFalse}
-                    pinToPres={returnFalse}
-                    ScreenToLocalTransform={Transform.Identity}
-                    bringToFront={returnFalse}
-                    isContentActive={emptyFunction}
-                    whenChildContentsActiveChanged={returnFalse}
-                    focus={returnFalse}
-                    docViewPath={returnEmptyDoclist}
-                    PanelWidth={() => 500}
-                    PanelHeight={() => 800}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                />
-            </div>
-        );
-    }
-
     render() {
         return (
             <div
@@ -1037,68 +1003,10 @@ export class MainView extends React.Component {
                 <RichTextMenu />
                 <InkTranscription />
                 {this.snapLines}
-                <div className="mainView-webRef" ref={this.makeWebRef} />
                 <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
             </div>
         );
     }
-
-    makeWebRef = (ele: HTMLDivElement) => {
-        reaction(
-            () => DocumentLinksButton.invisibleWebDoc,
-            invisibleDoc => {
-                ReactDOM.unmountComponentAtNode(ele);
-                invisibleDoc &&
-                    ReactDOM.render(
-                        <span title="Drag as document" className="invisible-webbox">
-                            <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
-                                <WebBox
-                                    fieldKey={'data'}
-                                    ContainingCollectionView={undefined}
-                                    ContainingCollectionDoc={undefined}
-                                    Document={invisibleDoc}
-                                    dropAction={'move'}
-                                    isSelected={returnFalse}
-                                    docViewPath={returnEmptyDoclist}
-                                    select={returnFalse}
-                                    rootSelected={returnFalse}
-                                    renderDepth={0}
-                                    setHeight={returnFalse}
-                                    styleProvider={undefined}
-                                    addDocTab={returnFalse}
-                                    pinToPres={returnFalse}
-                                    ScreenToLocalTransform={Transform.Identity}
-                                    bringToFront={returnFalse}
-                                    isContentActive={emptyFunction}
-                                    whenChildContentsActiveChanged={returnFalse}
-                                    focus={returnFalse}
-                                    PanelWidth={() => 500}
-                                    PanelHeight={() => 800}
-                                    docFilters={returnEmptyFilter}
-                                    docRangeFilters={returnEmptyFilter}
-                                    searchFilterDocs={returnEmptyDoclist}
-                                />
-                            </div>
-                            ;
-                        </span>,
-                        ele
-                    );
-
-                let success = false;
-                const onSuccess = () => {
-                    success = true;
-                    clearTimeout(interval);
-                    document.removeEventListener('editSuccess', onSuccess);
-                };
-
-                // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
-                // so we keep simulating clicks until annotations have loaded and editing is successful
-                const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500);
-                setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
-                document.addEventListener('editSuccess', onSuccess);
-            }
-        );
-    };
 }
 
 ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) {
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index d9a989309..ce0e58d90 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -27,6 +27,7 @@ export interface MarqueeAnnotatorProps {
     mainCont: HTMLDivElement;
     docView: DocumentView;
     savedAnnotations: () => ObservableMap<number, HTMLDivElement[]>;
+    selectionText: () => string;
     annotationLayer: HTMLDivElement;
     addDocument: (doc: Doc) => boolean;
     getPageFromScroll?: (top: number) => number;
@@ -147,7 +148,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             return marqueeAnno;
         }
 
-        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, backgroundColor: 'transparent', title: 'Selection on ' + this.props.rootDoc.title });
+        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, text: this.props.selectionText(), backgroundColor: 'transparent', title: 'Selection on ' + this.props.rootDoc.title });
         let minX = Number.MAX_VALUE;
         let maxX = -Number.MAX_VALUE;
         let minY = Number.MAX_VALUE;
@@ -271,7 +272,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                     width: `${this._width}px`,
                     height: `${this._height}px`,
                     border: `${this._width === 0 ? '' : '2px dashed black'}`,
-                }}/>
+                }}
+            />
         );
     }
 }
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;
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 869570a17..5abf4bde2 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -288,11 +288,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             return undefined;
         case StyleProp.Decorations:
             if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) {
-                return doc &&
-                    isBackground() &&
-                    !Doc.IsSystem(doc) &&
-                    (props?.renderDepth || 0) > 0 &&
-                    ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ? (
+                return doc && isBackground() && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 ? (
                     <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                         <FontAwesomeIcon icon={isBackground() ? 'lock' : 'unlock'} style={{ color: isBackground() ? 'red' : undefined }} size="lg" />
                     </div>
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index ac3541f2c..78e44dfa2 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -89,6 +89,10 @@
     position: relative;
 }
 
+.lm_stack {
+    position: relative;
+}
+
 .lm_drag_tab {
     padding: 0;
     width: 15px !important;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e9b41de25..92319d080 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,6 +1,6 @@
 import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import * as GoldenLayout from '../../../client/goldenLayout';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -318,7 +318,7 @@ export class CollectionDockingView extends CollectionSubView() {
         }
     };
 
-    componentDidMount: () => void = () => {
+    componentDidMount: () => void = async () => {
         if (this._containerRef.current) {
             this._lightboxReactionDisposer = reaction(
                 () => LightboxView.LightboxDoc,
@@ -335,8 +335,11 @@ export class CollectionDockingView extends CollectionSubView() {
                     this._ignoreStateChange = '';
                 }
             );
-            setTimeout(this.setupGoldenLayout);
-            //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
+            reaction(
+                () => this.props.PanelWidth(),
+                width => !this._goldenLayout && width > 20 && setTimeout(() => this.setupGoldenLayout()), // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
+                { fireImmediately: true }
+            );
         }
     };
 
@@ -460,7 +463,7 @@ export class CollectionDockingView extends CollectionSubView() {
     };
 
     tabDestroyed = (tab: any) => {
-        if (![DocumentType.KVP, DocumentType.PRES].includes(tab.DashDoc?.type)) {
+        if (tab.DashDoc && ![DocumentType.KVP, DocumentType.PRES].includes(tab.DashDoc?.type)) {
             Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
             Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
         }
@@ -470,8 +473,8 @@ export class CollectionDockingView extends CollectionSubView() {
             Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
             this.tabMap.delete(tab);
             tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
-            tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
-            setTimeout(this.stateChanged);
+            //tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
+            this.stateChanged();
         }
     };
     tabCreated = (tab: any) => {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index cde5132a3..31ed5a83b 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -4,7 +4,7 @@ import { Tooltip } from '@material-ui/core';
 import { clamp } from 'lodash';
 import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
@@ -143,8 +143,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
                 const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
                 const closeIcon = <FontAwesomeIcon icon={'eye'} />;
-                ReactDOM.render(docIcon, iconWrap);
-                ReactDOM.render(closeIcon, closeWrap);
+                ReactDOM.createRoot(iconWrap).render(docIcon);
+                ReactDOM.createRoot(closeWrap).render(closeIcon);
                 tab.reactComponents = [iconWrap, closeWrap];
                 tab.element[0].prepend(iconWrap);
                 tab._disposers.layerDisposer = reaction(
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 1d455ad08..627487a9e 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -40,9 +40,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     @observable public static AnnotationUri: string | undefined;
     @observable public static LinkEditorDocView: DocumentView | undefined;
 
-    @observable public static invisibleWebDoc: Opt<Doc>;
-    public static invisibleWebRef = React.createRef<HTMLDivElement>();
-
     @action
     @undoBatch
     onLinkButtonMoved = (e: PointerEvent) => {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index c7001f846..fcb3ccb07 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -74,7 +74,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (oldDiv.className === 'pdfBox-ui' || oldDiv.className === 'pdfViewerDash-overlay-inking') {
             newDiv.style.display = 'none';
         }
-        if (newDiv && newDiv.style) newDiv.style.overflow = 'hidden';
+        if (newDiv?.style) newDiv.style.overflow = 'hidden';
         if (oldDiv instanceof HTMLCanvasElement) {
             const canvas = oldDiv;
             const img = document.createElement('img'); // create a Image Element
@@ -398,9 +398,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             </div>
         );
     }
-    sidebarWidth = () =>
-        !this.SidebarShown ? 0 : PDFBox.sidebarResizerWidth + (this._previewWidth ? PDFBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
-
+    sidebarWidth = () => {
+        if (!this.SidebarShown) return 0;
+        if (this._previewWidth) return PDFBox.sidebarResizerWidth + PDFBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
+        const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
+        return PDFBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+    };
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
         funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' });
@@ -483,7 +486,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         crop={this.crop}
                     />
                 </div>
-                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index db493934a..5ce6a0eb1 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema';
 import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -836,9 +836,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
     });
-    sidebarWidth = () =>
-        !this.SidebarShown ? 0 : WebBox.sidebarResizerWidth + (this._previewWidth ? WebBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
-
+    sidebarWidth = () => {
+        if (!this.SidebarShown) return 0;
+        if (this._previewWidth) return WebBox.sidebarResizerWidth + WebBox.openSidebarWidth; // return default sidebar if previewing (as in viewing a link target)
+        const nativeDiff = NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc);
+        return WebBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
+    };
     @computed get content() {
         const interactive =
             !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
@@ -1000,6 +1003,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                                 docView={this.props.docViewPath().lastElement()}
                                 finishMarquee={this.finishMarquee}
                                 savedAnnotations={this.savedAnnotationsCreator}
+                                selectionText={returnEmptyString}
                                 annotationLayer={this._annotationLayer.current}
                                 mainCont={this._mainCont.current}
                             />{' '}
@@ -1015,7 +1019,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     }}
                     onPointerDown={e => this.sidebarBtnDown(e, false)}
                 />
-                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.props.PanelWidth()}%` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 2b83e9da8..42d20ba99 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,7 +1,6 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import Color from 'color';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -12,7 +11,7 @@ import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { WebField } from '../../../../fields/URLField';
 import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, Utils } from '../../../../Utils';
+import { aggregateBounds, StopEvent, Utils } from '../../../../Utils';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -382,12 +381,15 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         //     style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
         //     <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
         // </div>;
-        setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
+        //setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
         return (
             <div
                 className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')} ${this.colorPickerClosed}`}
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
-                onClick={action(() => (this.colorPickerClosed = !this.colorPickerClosed))}
+                onClick={action(e => {
+                    this.colorPickerClosed = !this.colorPickerClosed;
+                    e.stopPropagation();
+                })}
                 onPointerDown={e => e.stopPropagation()}>
                 {this.Icon(color)}
                 <div className="colorButton-color" style={{ backgroundColor: curColor }} />
@@ -395,7 +397,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 {/* {dropdownCaret} */}
                 {this.colorPickerClosed ? null : (
                     <div>
-                        <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
+                        <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
                             {this.colorPicker(curColor)}
                         </div>
                         <div
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index 40dd6fbc7..85523c176 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -1,5 +1,5 @@
 import { TextSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
 import { DocServer } from '../../../DocServer';
 import React = require('react');
@@ -9,6 +9,7 @@ import React = require('react');
 // the comment can be toggled on/off with the '<-' text anchor.
 export class DashDocCommentView {
     dom: HTMLDivElement; // container for label and value
+    root: any;
 
     constructor(node: any, view: any, getPos: any) {
         this.dom = document.createElement('div');
@@ -30,12 +31,13 @@ export class DashDocCommentView {
             e.stopPropagation();
         };
 
-        ReactDOM.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />, this.dom);
+        this.root = ReactDOM.createRoot(this.dom);
+        this.root.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />);
         (this as any).dom = this.dom;
     }
 
     destroy() {
-        ReactDOM.unmountComponentAtNode(this.dom);
+        // ReactDOM.unmountComponentAtNode(this.dom);
     }
 
     selectNode() {}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 5f576be41..63ee7d1f3 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,7 +1,7 @@
 import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { NodeSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
@@ -15,6 +15,7 @@ import React = require('react');
 
 export class DashDocView {
     dom: HTMLSpanElement; // container for label and value
+    root: any;
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
         this.dom = document.createElement('span');
@@ -38,14 +39,13 @@ export class DashDocView {
             e.stopPropagation();
         };
 
-        ReactDOM.render(
-            <DashDocViewInternal docid={node.attrs.docid} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />,
-            this.dom
+        this.root = ReactDOM.createRoot(this.dom);
+        this.root.render(
+            <DashDocViewInternal docid={node.attrs.docid} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />
         );
-        (this as any).dom = this.dom;
     }
     destroy() {
-        ReactDOM.unmountComponentAtNode(this.dom);
+        // ReactDOM.unmountComponentAtNode(this.dom);
     }
     selectNode() {}
 }
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 35d919f38..f088b39d1 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,6 +1,6 @@
 import { action, computed, IReactionDisposer, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import { DataSym, Doc, DocListCast, Field } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
@@ -19,6 +19,7 @@ import { CollectionViewType } from '../../../documents/DocumentTypes';
 
 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);
@@ -43,11 +44,11 @@ export class DashFieldView {
             e.stopPropagation();
         };
 
-        setTimeout(() => ReactDOM.render(<DashFieldViewInternal fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} tbox={tbox} />, this.dom));
-        (this as any).dom = this.dom;
+        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} />);
     }
     destroy() {
-        ReactDOM.unmountComponentAtNode(this.dom);
+        //this.root.unmount();
     }
     selectNode() {}
 }
@@ -59,6 +60,7 @@ interface IDashFieldViewInternal {
     tbox: FormattedTextBox;
     width: number;
     height: number;
+    editable: boolean;
 }
 
 @observer
@@ -116,7 +118,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                 return (
                     <span
                         className="dashFieldView-fieldSpan"
-                        contentEditable={true}
+                        contentEditable={this.props.editable}
                         style={{ display: strVal.length < 2 ? 'inline-block' : undefined }}
                         suppressContentEditableWarning={true}
                         defaultValue={strVal}
@@ -161,6 +163,41 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         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.
@@ -266,14 +303,12 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
         document.addEventListener('pointerdown', hideMenu, true);
     };
     render() {
-        const buttons = [
+        return this.getElement([
             <Tooltip key="trash" title={<div className="dash-tooltip">{`Show Pivot Viewer for '${this._fieldKey}'`}</div>}>
                 <button className="antimodeMenu-button" onPointerDown={this.showFields}>
                     <FontAwesomeIcon icon="eye" size="lg" />
                 </button>
             </Tooltip>,
-        ];
-
-        return this.getElement(buttons);
+        ]);
     }
 }
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 4895dcdc5..0fd2a7808 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -2,7 +2,7 @@ import EquationEditor from 'equation-editor-react';
 import { IReactionDisposer } from 'mobx';
 import { observer } from 'mobx-react';
 import { TextSelection } from 'prosemirror-state';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import { Doc } from '../../../../fields/Doc';
 import { StrCast } from '../../../../fields/Types';
 import './DashFieldView.scss';
@@ -11,7 +11,7 @@ import React = require('react');
 
 export class EquationView {
     dom: HTMLDivElement; // container for label and value
-
+    root: any;
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
         this.dom = document.createElement('div');
         this.dom.style.width = node.attrs.width;
@@ -22,13 +22,13 @@ export class EquationView {
             e.stopPropagation();
         };
 
-        ReactDOM.render(<EquationViewInternal fieldKey={node.attrs.fieldKey} width={node.attrs.width} height={node.attrs.height} getPos={getPos} setEditor={this.setEditor} tbox={tbox} />, this.dom);
-        (this as any).dom = this.dom;
+        this.root = ReactDOM.createRoot(this.dom);
+        this.root.render(<EquationViewInternal fieldKey={node.attrs.fieldKey} width={node.attrs.width} height={node.attrs.height} getPos={getPos} setEditor={this.setEditor} tbox={tbox} />);
     }
     _editor: EquationEditor | undefined;
     setEditor = (editor?: EquationEditor) => (this._editor = editor);
     destroy() {
-        ReactDOM.unmountComponentAtNode(this.dom);
+        // ReactDOM.unmountComponentAtNode(this.dom);
     }
     setSelection() {
         this._editor?.mathField.focus();
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 096f9a92c..0e48dd93a 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1132,6 +1132,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         );
         quickScroll = undefined;
         this.tryUpdateScrollHeight();
+        setTimeout(this.tryUpdateScrollHeight, 250);
     }
 
     pushToGoogleDoc = async () => {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 47833dd43..dc5d8ada8 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -248,8 +248,7 @@ export class RichTextRules {
             // [[fieldKey:Doc]] => show field of doc
             new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
                 const fieldKey = match[1];
-                const rawdocid = match[3];
-                const docid = rawdocid ? normalizeEmail(!rawdocid.includes('@') ? Doc.CurrentUserEmail + rawdocid : rawdocid.substring(1)) : undefined;
+                const docid = match[3]?.replace(':', '');
                 const value = match[2]?.substring(1);
                 if (!fieldKey) {
                     if (docid) {
@@ -259,7 +258,7 @@ export class RichTextRules {
                             if (rstate) {
                                 this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
                             }
-                            const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: rawdocid.replace(/^:/, ''), _width: 500, _height: 500 }, docid);
+                            const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
                             DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, 'portal to:portal from', undefined);
 
                             const fstate = this.TextBox.EditorView?.state;
@@ -275,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 });
+                const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid, hideKey: true });
                 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/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 01acc3de9..1fe6d822b 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -1,6 +1,6 @@
 import { TextSelection } from 'prosemirror-state';
 import { Fragment, Node, Slice } from 'prosemirror-model';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import React = require('react');
 
 // an elidable textblock that collapses when its '<-' is clicked and expands when its '...' anchor is clicked.
@@ -9,6 +9,7 @@ import React = require('react');
 // method instead of changing prosemirror's text when the expand/elide buttons are clicked.
 export class SummaryView {
     dom: HTMLSpanElement; // container for label and value
+    root: any;
 
     constructor(node: any, view: any, getPos: any) {
         const self = this;
@@ -35,13 +36,13 @@ export class SummaryView {
             return js.apply(this, arguments);
         };
 
-        ReactDOM.render(<SummaryViewInternal />, this.dom);
-        (this as any).dom = this.dom;
+        this.root = ReactDOM.createRoot(this.dom);
+        this.root.render(<SummaryViewInternal />);
     }
 
     className = (visible: boolean) => 'formattedTextBox-summarizer' + (visible ? '' : '-collapsed');
     destroy() {
-        ReactDOM.unmountComponentAtNode(this.dom);
+        // ReactDOM.unmountComponentAtNode(this.dom);
     }
     selectNode() {}
 
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 66d747bf7..aa2475dca 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -263,6 +263,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             fieldKey: { default: '' },
             docid: { default: '' },
             hideKey: { default: false },
+            editable: { default: true },
         },
         group: 'inline',
         draggable: false,
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 822af6a68..470aa3eb1 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -27,14 +27,14 @@
         opacity: unset;
         mix-blend-mode: multiply; // bcz: makes text fuzzy!
 
-        span {
-            padding-right: 5px;
-            padding-bottom: 4px;
-        }
+        // span {
+        //     padding-right: 5px;
+        //     padding-bottom: 4px;
+        // }
     }
 
     .textLayer ::selection {
-        background: #ACCEF7;
+        background: #accef7;
     }
 
     // should match the backgroundColor in createAnnotation()
@@ -111,4 +111,4 @@
 
 .pdfViewerDash-interactive {
     pointer-events: all;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 5c10c7cef..abc7336bd 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -79,6 +79,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _initialScroll: Opt<number>;
     private _forcedScroll = true;
 
+    selectionText = () => this._selectionText;
+
     @observable isAnnotating = false;
     // key where data is stored
     @computed get allAnnotations() {
@@ -358,7 +360,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
             this.isAnnotating = true;
-            if (e.target && ((e.target as any).className.includes('endOfContent') || (e.target as any).parentElement.className !== 'textLayer')) {
+            const target = e.target as any;
+            if (e.target && (target.className.includes('endOfContent') || (target.parentElement.className !== 'textLayer' && target.parentElement.parentElement?.className !== 'textLayer'))) {
                 this._textSelecting = false;
                 document.addEventListener('pointermove', this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called
             } else {
@@ -574,6 +577,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                             docView={this.props.docViewPath().lastElement()}
                             finishMarquee={this.finishMarquee}
                             savedAnnotations={this.savedAnnotations}
+                            selectionText={this.selectionText}
                             annotationLayer={this._annotationLayer.current}
                             mainCont={this._mainCont.current}
                             anchorMenuCrop={this._textSelecting ? undefined : this.crop}
diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx
index 1b12e208c..b8081648f 100644
--- a/src/debug/Repl.tsx
+++ b/src/debug/Repl.tsx
@@ -1,5 +1,5 @@
 import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import { observer } from 'mobx-react';
 import { observable, computed } from 'mobx';
 import { CompileScript } from '../client/util/Scripting';
@@ -11,39 +11,40 @@ import { resolvedPorts } from '../client/util/CurrentUserUtils';
 
 @observer
 class Repl extends React.Component {
-    @observable text: string = "";
+    @observable text: string = '';
 
-    @observable executedCommands: { command: string, result: any }[] = [];
+    @observable executedCommands: { command: string; result: any }[] = [];
 
     onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
         this.text = e.target.value;
-    }
+    };
 
     onKeyDown = (e: React.KeyboardEvent) => {
-        if (!e.ctrlKey && e.key === "Enter") {
+        if (!e.ctrlKey && e.key === 'Enter') {
             e.preventDefault();
             const script = CompileScript(this.text, {
-                addReturn: true, typecheck: false,
-                params: { makeInterface: "any" }
+                addReturn: true,
+                typecheck: false,
+                params: { makeInterface: 'any' },
             });
             if (!script.compiled) {
-                this.executedCommands.push({ command: this.text, result: "Compile Error" });
+                this.executedCommands.push({ command: this.text, result: 'Compile Error' });
             } else {
                 const result = script.run({ makeInterface }, e => this.executedCommands.push({ command: this.text, result: e.message || e }));
                 result.success && this.executedCommands.push({ command: this.text, result: result.result });
             }
-            this.text = "";
+            this.text = '';
         }
-    }
+    };
 
     @computed
     get commands() {
         return this.executedCommands.map(command => {
             return (
-                <div style={{ marginTop: "5px" }}>
+                <div style={{ marginTop: '5px' }}>
                     <p>{command.command}</p>
                     {/* <pre>{JSON.stringify(command.result, null, 2)}</pre> */}
-                    <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? "object" : String(command.result)}</pre>
+                    <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? 'object' : String(command.result)}</pre>
                 </div>
             );
         });
@@ -52,16 +53,14 @@ class Repl extends React.Component {
     render() {
         return (
             <div>
-                <div style={{ verticalAlign: "bottom" }}>
-                    {this.commands}
-                </div>
-                <textarea style={{ width: "100%", position: "absolute", bottom: "0px" }} value={this.text} onChange={this.onChange} onKeyDown={this.onKeyDown} />
+                <div style={{ verticalAlign: 'bottom' }}>{this.commands}</div>
+                <textarea style={{ width: '100%', position: 'absolute', bottom: '0px' }} value={this.text} onChange={this.onChange} onKeyDown={this.onKeyDown} />
             </div>
         );
     }
 }
 
 (async function () {
-    DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, "repl");
-    ReactDOM.render(<Repl />, document.getElementById("root"));
-})();
\ No newline at end of file
+    DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, 'repl');
+    ReactDOM.createRoot(document.getElementById('root')!).render(<Repl />);
+})();
-- 
cgit v1.2.3-70-g09d2


From 90bf9ea9e32ed695a36d0334e99fd859bbe51c15 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Nov 2022 19:59:13 -0500
Subject: made alternate image use a field data-useAlt.  added alt images to
 preselements. fixed removing TabDoc documentviews from document view list. 
 added playing audio tags to preselements

---
 src/client/goldenLayout.js                         |  1 +
 src/client/util/LinkFollower.ts                    |  7 ++-----
 src/client/views/nodes/ImageBox.tsx                |  9 ++++++---
 .../nodes/formattedText/DashDocCommentView.tsx     |  3 +++
 .../views/nodes/formattedText/DashDocView.tsx      |  1 +
 .../views/nodes/formattedText/DashFieldView.tsx    |  2 +-
 .../views/nodes/formattedText/EquationView.tsx     |  1 +
 .../views/nodes/formattedText/SummaryView.tsx      |  1 +
 src/client/views/nodes/trails/PresBox.tsx          | 22 ++++++++++++++++++----
 9 files changed, 34 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/formattedText/DashDocView.tsx')

diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index bc08b4d0b..9cb20d834 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -5408,6 +5408,7 @@
          * @returns {void}
          */
         _destroy: function () {
+            this._reactComponent.unmount();
           //  ReactDOM.unmountComponentAtNode(this._container.getElement()[0]);
             this._container.off('open', this._render, this);
             this._container.off('destroy', this._destroy, this);
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 43b2caf88..1608a77f2 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -1,8 +1,5 @@
-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 { action, runInAction } from 'mobx';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { BoolCast, Cast, StrCast } from '../../fields/Types';
 import { DocumentDecorations } from '../views/DocumentDecorations';
 import { LightboxView } from '../views/LightboxView';
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d02f181cc..d0df41023 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -8,7 +8,7 @@ import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { createSchema } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -120,6 +120,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @undoBatch
     resolution = () => (this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes);
+    @undoBatch
+    setUseAlt = () => (this.layoutDoc[this.fieldKey + '-useAlt'] = !this.layoutDoc[this.fieldKey + '-useAlt']);
 
     @undoBatch
     rotate = action(() => {
@@ -182,6 +184,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const funcs: ContextMenuProps[] = [];
             funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'expand-arrows-alt' });
             funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand-arrows-alt' });
+            funcs.push({ description: `${this.layoutDoc[this.fieldKey + '-useAlt'] ? 'Show Primary' : 'Show Alternate'}`, event: this.setUseAlt, icon: 'expand-arrows-alt' });
             funcs.push({ description: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'expand-arrows-alt' });
             if (!Doc.noviceMode) {
                 funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });
@@ -335,8 +338,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? 'auto' : undefined }}>
                     <img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
                     {fadepath === srcpath ? null : (
-                        <div className={`imageBox-fadeBlocker${this.props.isHovering?.() ? '-hover' : ''}`}>
-                            <img className="imageBox-fadeaway" key={'fadeaway'} src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
+                        <div className={`imageBox-fadeBlocker${(this.props.isHovering?.() && this.layoutDoc[this.fieldKey + '-useAlt'] === undefined) || BoolCast(this.layoutDoc[this.fieldKey + '-useAlt']) ? '-hover' : ''}`}>
+                            <img className="imageBox-fadeaway" key="fadeaway" src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
                         </div>
                     )}
                 </div>
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index 4bff57842..fcd6e0c55 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -36,6 +36,9 @@ export class DashDocCommentView {
         (this as any).dom = this.dom;
     }
 
+    destroy() {
+        this.root.unmount();
+    }
     deselectNode() {
         this.dom.classList.remove('ProseMirror-selectednode');
     }
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 63ee7d1f3..722d0cc4f 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -45,6 +45,7 @@ export class DashDocView {
         );
     }
     destroy() {
+        this.root.unmount();
         // ReactDOM.unmountComponentAtNode(this.dom);
     }
     selectNode() {}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 41b92a3c7..1e7cb6ea5 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -47,7 +47,7 @@ export class DashFieldView {
         );
     }
     destroy() {
-        //this.root.unmount();
+        this.root.unmount();
     }
     deselectNode() {
         this.dom.classList.remove('ProseMirror-selectednode');
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 89f2a7c81..714ae458c 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -32,6 +32,7 @@ export class EquationView {
     _editor: EquationEditor | undefined;
     setEditor = (editor?: EquationEditor) => (this._editor = editor);
     destroy() {
+        this.root.unmount();
         // ReactDOM.unmountComponentAtNode(this.dom);
     }
     setSelection() {
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 1fe6d822b..4e75d374c 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -42,6 +42,7 @@ export class SummaryView {
 
     className = (visible: boolean) => 'formattedTextBox-summarizer' + (visible ? '' : '-collapsed');
     destroy() {
+        this.root.unmount();
         // ReactDOM.unmountComponentAtNode(this.dom);
     }
     selectNode() {}
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 14f767595..c1973c4b4 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -349,7 +349,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform);
         const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any);
         const clippable = [DocumentType.COMPARISON].includes(target.type as any);
-        const dataview = [DocumentType.INK, DocumentType.COL].includes(target.type as any) && target.activeFrame === undefined;
+        const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG].includes(target.type as any) && target.activeFrame === undefined;
         const poslayoutview = [DocumentType.COL].includes(target.type as any) && target.activeFrame === undefined;
         const textview = [DocumentType.RTF].includes(target.type as any) && target.activeFrame === undefined;
         return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview };
@@ -371,7 +371,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] });
             }
         }
-        if (dataview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+        if (dataview && activeItem.presData !== undefined) {
+            const fkey = Doc.LayoutFieldKey(bestTarget);
+            Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
+            bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt;
+        }
         if (textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData;
         if (poslayoutview) {
             StrListCast(activeItem.presPinLayoutData)
@@ -434,7 +438,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (pinProps?.pinDocContent) {
             pinDoc.presPinData = scrollable || temporal || pannable || clippable || dataview || textview || poslayoutview || pinProps.activeFrame !== undefined;
-            if (dataview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.data;
+            if (dataview) {
+                const fkey = Doc.LayoutFieldKey(targetDoc);
+                pinDoc.presUseAlt = targetDoc[fkey + '-useAlt'];
+                pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
+            }
             if (textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text;
             if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop;
             if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth;
@@ -541,8 +549,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (activeItem.presPinData || activeItem.presPinView) {
             clearTimeout(PresBox._navTimer);
             // targetDoc may or may not be displayed.  this gets the first available document (or alias) view that matches targetDoc
-            const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
+            const bestTargetView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
+            const bestTarget = bestTargetView?.props.Document;
             if (bestTarget) PresBox._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem);
+            activeItem.presPinAudioPlay && bestTargetView?.docView?.playAnnotation();
         }
     }
 
@@ -1369,6 +1379,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     {isPresCollection ? null : (
                         <div className="ribbon-box">
                             Effects
+                            <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
+                                <div className="presBox-subheading">Play Audio Annotation</div>
+                                <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPinAudioPlay = !BoolCast(activeItem.presPinAudioPlay))} checked={BoolCast(activeItem.presPinAudioPlay)} />
+                            </div>
                             <div
                                 className="presBox-dropdown"
                                 onClick={action(e => {
-- 
cgit v1.2.3-70-g09d2


From 32f5040c44dc302e3dd53cecd9be4cd51a474d3f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 14 Feb 2023 14:57:25 -0500
Subject: fixed pinning regions on pdf/web pages to allow proper pres
 following.  added click on pres item# to select w/o navigation.  fixed
 dashDocView to be selectable without losing selection to parent textbox. 
 added BingMaps rudimentary option to googleMaps

---
 package-lock.json                                  |  18 ++-
 package.json                                       |   2 +
 src/client/views/nodes/MapBox/MapBox.tsx           | 148 +++++++++++++--------
 src/client/views/nodes/PDFBox.tsx                  |   1 +
 src/client/views/nodes/WebBox.tsx                  |   1 +
 .../views/nodes/formattedText/DashDocView.tsx      |   5 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   3 +
 src/client/views/nodes/trails/PresBox.tsx          |  20 +--
 src/client/views/nodes/trails/PresElementBox.scss  |   6 +
 src/client/views/nodes/trails/PresElementBox.tsx   |  12 +-
 10 files changed, 143 insertions(+), 73 deletions(-)

(limited to 'src/client/views/nodes/formattedText/DashDocView.tsx')

diff --git a/package-lock.json b/package-lock.json
index 547a6e1d7..95a128419 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3675,6 +3675,11 @@
                 "file-uri-to-path": "1.0.0"
             }
         },
+        "bingmaps-react": {
+            "version": "1.2.10",
+            "resolved": "https://registry.npmjs.org/bingmaps-react/-/bingmaps-react-1.2.10.tgz",
+            "integrity": "sha512-fM887Sr6OIlo6ThmSpGfNEDssSytdnsMrbDTDs+YH/43etf0dlfcR4oaJvygy+fwn21hpP2lVvwTHUDZvGxZqA=="
+        },
         "bl": {
             "version": "4.1.0",
             "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@@ -4970,9 +4975,9 @@
             }
         },
         "core-js": {
-            "version": "1.2.7",
-            "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
-            "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+            "version": "3.28.0",
+            "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.28.0.tgz",
+            "integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw=="
         },
         "core-js-pure": {
             "version": "3.22.8",
@@ -8260,6 +8265,13 @@
                 "promise": "^7.1.1",
                 "setimmediate": "^1.0.5",
                 "ua-parser-js": "^0.7.30"
+            },
+            "dependencies": {
+                "core-js": {
+                    "version": "1.2.7",
+                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+                    "integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA=="
+                }
             }
         },
         "fd-slicer": {
diff --git a/package.json b/package.json
index 712354355..00ce356f9 100644
--- a/package.json
+++ b/package.json
@@ -165,6 +165,7 @@
         "bcrypt-nodejs": "0.0.3",
         "bezier-curve": "^1.0.0",
         "bezier-js": "^4.1.1",
+        "bingmaps-react": "^1.2.10",
         "bluebird": "^3.7.2",
         "body-parser": "^1.19.2",
         "bootstrap": "^4.6.1",
@@ -181,6 +182,7 @@
         "connect-mongo": "^2.0.3",
         "cookie-parser": "^1.4.6",
         "cookie-session": "^2.0.0",
+        "core-js": "^3.28.0",
         "cors": "^2.8.5",
         "depcheck": "^0.9.2",
         "equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 5940fc075..039f11cd1 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,5 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+import BingMapsReact from 'bingmaps-react';
 import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -43,20 +44,22 @@ const mapContainerStyle = {
 };
 
 const defaultCenter = {
-    lat: 38.685,
-    lng: -115.234,
+    lat: 42.360081,
+    lng: -71.058884,
 };
 
 const mapOptions = {
     fullscreenControl: false,
 };
 
+const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
 const apiKey = process.env.GOOGLE_MAPS;
 
 const script = document.createElement('script');
 script.defer = true;
 script.async = true;
 script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+console.log(script.src);
 document.head.appendChild(script);
 
 /**
@@ -84,6 +87,7 @@ const options = {
 
 @observer
 export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+    static UseBing = true;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -551,69 +555,99 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     infoHeight = () => this.props.PanelHeight() / 5;
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     savedAnnotations = () => this._savedAnnotations;
+
+    _bingSearchManager: any;
+    _bingMap: any;
+    get MicrosoftMaps() {
+        return (window as any).Microsoft.Maps;
+    }
+    // uses Bing Search to retrieve lat/lng for a location.  eg.,
+    //   const results = this.geocodeQuery(map.map, 'Philadelphia, PA');
+    // to move the map to that location:
+    //     const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA');
+    //     this._bingMap.current.setView({
+    //        mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+    //        center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+    //     });
+    //
+    bingGeocode = (map: any, query: string) => {
+        return new Promise<{ latitude: number; longitude: number }>((res, reject) => {
+            //If search manager is not defined, load the search module.
+            if (!this._bingSearchManager) {
+                //Create an instance of the search manager and call the geocodeQuery function again.
+                this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => {
+                    this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current);
+                    res(this.bingGeocode(map, query));
+                });
+            } else {
+                this._bingSearchManager.geocode({
+                    where: query,
+                    callback: action((r: any) => {
+                        res(r.results[0].location);
+                    }),
+                    errorCallback: (e: any) => reject(),
+                });
+            }
+        });
+    };
+
+    bingViewOptions = {
+        center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },
+        mapTypeId: 'grayscale',
+    };
+    bingMapOptions = {
+        navigationBarMode: 'square',
+    };
+    bingMapReady = (map: any) => (this._bingMap = map.map);
     render() {
         const renderAnnotations = (docFilters?: () => string[]) => null;
-        // bcz: commmented this out.  Otherwise, any documents that are rendered with an InfoWindow of a marker
-        //      will also be rendered as freeform annotations on the map. However, it doesn't seem that rendering
-        //      freeform documents on the map does anything anyway, so getting rid of it for now.  Also, since documents
-        //      are rendered twice, adding a new note to the InfoWindow loses focus immediately on creation since it gets
-        //      shifted to the non-visible view of the document in this freeform view.
-        // <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-        //     renderDepth={this.props.renderDepth + 1}
-        //     isAnnotationOverlay={true}
-        //     fieldKey={this.annotationKey}
-        //     CollectionView={undefined}
-        //     setPreviewCursor={this.setPreviewCursor}
-        //     PanelWidth={this.panelWidth}
-        //     PanelHeight={this.panelHeight}
-        //     ScreenToLocalTransform={this.scrollXf}
-        //     scaling={returnOne}
-        //     dropAction={"alias"}
-        //     docFilters={docFilters || this.props.docFilters}
-        //     dontRenderDocuments={docFilters ? false : true}
-        //     select={emptyFunction}
-        //     bringToFront={emptyFunction}
-        //     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-        //     removeDocument={this.removeDocument}
-        //     moveDocument={this.moveDocument}
-        //     addDocument={this.sidebarAddDocument}
-        //     childPointerEvents={"all"}
-        //     pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="mapBox" ref={this._ref}>
-                {/*console.log(apiKey)*/}
-                {/* <LoadScript
-                googleMapsApiKey={apiKey!}
-                libraries={['places', 'drawing']}
-            > */}
-                <div className="mapBox-wrapper" onWheel={e => e.stopPropagation()} onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+                <div
+                    className="mapBox-wrapper"
+                    onWheel={e => e.stopPropagation()}
+                    onPointerDown={async e => {
+                        e.button === 0 && !e.ctrlKey && e.stopPropagation();
+                        // just a simple test of bing maps geocode api
+                        // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA');
+                        // this._bingMap.current.setView({
+                        //     mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+                        //     center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+                        //     zoom: 15,
+                        // });
+                    }}
+                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                     {this.annotationLayer}
-                    <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
-                        <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
-                            <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
-                        </Autocomplete>
-
-                        {this.renderMarkers()}
-                        {this.allMapMarkers
-                            .filter(marker => marker.infoWindowOpen)
-                            .map(marker => (
-                                <MapBoxInfoWindow
-                                    key={marker[Id]}
-                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
-                                    place={marker}
-                                    markerMap={this.markerMap}
-                                    PanelWidth={this.infoWidth}
-                                    PanelHeight={this.infoHeight}
-                                    moveDocument={this.moveDocument}
-                                    isAnyChildContentActive={this.isAnyChildContentActive}
-                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                />
-                            ))}
-                        {/* {this.handleMapCenter(this._map)} */}
-                    </GoogleMap>
+
+                    {!MapBox.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
+                    <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
+                        <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+                            <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+                                <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
+                            </Autocomplete>
+
+                            {this.renderMarkers()}
+                            {this.allMapMarkers
+                                .filter(marker => marker.infoWindowOpen)
+                                .map(marker => (
+                                    <MapBoxInfoWindow
+                                        key={marker[Id]}
+                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                        place={marker}
+                                        markerMap={this.markerMap}
+                                        PanelWidth={this.infoWidth}
+                                        PanelHeight={this.infoHeight}
+                                        moveDocument={this.moveDocument}
+                                        isAnyChildContentActive={this.isAnyChildContentActive}
+                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                    />
+                                ))}
+                            {/* {this.handleMapCenter(this._map)} */}
+                        </GoogleMap>
+                    </div>
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
                             rootDoc={this.rootDoc}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b88ac113e..5890c536d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -222,6 +222,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const anchor = Docs.Create.TextanchorDocument({
                 title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
                 unrendered: true,
+                annotationOn: this.rootDoc,
             });
             PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
             return anchor;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c4cca9679..c392f3577 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -322,6 +322,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
                 y: NumCast(this.layoutDoc._scrollTop),
                 unrendered: true,
+                annotationOn: this.rootDoc,
             });
         anchor.text = ele?.textContent ?? '';
         anchor.textHtml = ele?.innerHTML;
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 722d0cc4f..19ee1fc7b 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -12,6 +12,7 @@ import { Transform } from '../../../util/Transform';
 import { DocumentView } from '../DocumentView';
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require('react');
+import { SelectionManager } from '../../../util/SelectionManager';
 
 export class DashDocView {
     dom: HTMLSpanElement; // container for label and value
@@ -192,10 +193,10 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     Document={this._finalLayout}
                     DataDoc={this._resolvedDataDoc}
                     addDocument={returnFalse}
-                    rootSelected={this._textBox.props.isSelected}
+                    rootSelected={returnFalse} //{this._textBox.props.isSelected}
                     removeDocument={this.removeDoc}
                     isDocumentActive={returnFalse}
-                    isContentActive={this._textBox.props.isContentActive}
+                    isContentActive={emptyFunction}
                     styleProvider={this._textBox.props.styleProvider}
                     docViewPath={this._textBox.props.docViewPath}
                     ScreenToLocalTransform={this.getDocTransform}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 80b18b8b9..49acd8a57 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1487,6 +1487,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             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 === 'true');
+            if (pcords && pcords.inside > 0 && this._editorView.state.doc.nodeAt(pcords.inside)?.type === this._editorView.state.schema.nodes.dashDoc) {
+                return;
+            }
         }
 
         if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 0a96297b7..e07517113 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -14,7 +14,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty
 import { AudioField } from '../../../../fields/URLField';
 import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
-import { Docs } from '../../../documents/Documents';
+import { Docs, DocumentOptions } from '../../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
@@ -869,13 +869,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         presDocView && SelectionManager.SelectView(presDocView, false);
     };
 
+    focusElement = (doc: Doc, options: DocFocusOptions) => this.selectElement(doc);
+
     //Regular click
     @action
-    selectElement = async (doc: Doc) => {
+    selectElement = async (doc: Doc, noNav = false) => {
         CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
-        this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
-        // if (doc.presPinView) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
-        // else
+        !noNav && this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
         this.updateCurrentPresentation(DocCast(doc.context));
     };
 
@@ -911,19 +911,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     //regular click
     @action
-    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
+    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true, noNav = false) => {
         this.clearSelectedArray();
         this.addToSelectedArray(doc);
         this._eleArray.splice(0, this._eleArray.length, ref);
         this._dragArray.splice(0, this._dragArray.length, drag);
-        focus && this.selectElement(doc);
+        focus && this.selectElement(doc, noNav);
         selectPres && this.selectPres();
     };
 
-    modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
+    modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean, noNav: boolean = false) => {
         if (cmdClick) this.multiSelect(doc, ref, drag);
         else if (shiftClick) this.shiftSelect(doc, ref, drag);
-        else this.regularSelect(doc, ref, drag, focus);
+        else this.regularSelect(doc, ref, drag, focus, noNav);
     };
 
     static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e);
@@ -2253,7 +2253,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 filterAddDocument={this.addDocumentFilter}
                                 removeDocument={returnFalse}
                                 dontRegisterView={true}
-                                focus={this.selectElement}
+                                focus={this.focusElement}
                                 scriptContext={this}
                                 ScreenToLocalTransform={this.getTransform}
                                 AddToMap={this.AddToMap}
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 415253af1..4f95f0c1f 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -44,6 +44,12 @@ $slide-active: #5b9fdd;
     display: flex;
     align-items: center;
 
+    .presItem-number {
+        cursor: pointer;
+        &:hover {
+            background-color: $light-blue;
+        }
+    }
     .presItem-name {
         display: flex;
         min-width: 20px;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 788900b46..f265c1315 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -592,7 +592,17 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 width: `calc(100% ${this.rootDoc.presExpandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
                                 cursor: isSelected ? 'text' : 'grab',
                             }}>
-                            <div>{`${this.indexInPres + 1}.  `}</div>
+                            <div
+                                className="presItem-number"
+                                title="select without navigation"
+                                style={{ pointerEvents: this.presBoxView?.isContentActive() ? 'all' : undefined }}
+                                onPointerDown={e => {
+                                    e.stopPropagation();
+                                    if (this._itemRef.current && this._dragRef.current) {
+                                        this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, false, false, false, true);
+                                    }
+                                }}
+                                onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}.  `}</div>
                             <EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
                         </div>
                         {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
-- 
cgit v1.2.3-70-g09d2


From b25f333713706a7456ab418960082bcabe830f11 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Mar 2023 20:48:38 -0400
Subject: fixed copying of docs with template layout docs.  fixed clone to
 search through RTFs for referenced documents to clone.  renamed nested
 documents docId instead of docid for consistency with other nested field ids.

---
 src/client/util/CurrentUserUtils.ts                |  6 +--
 src/client/views/GlobalKeyHandler.ts               |  2 +-
 src/client/views/SidebarAnnos.tsx                  |  4 +-
 src/client/views/collections/CollectionSubView.tsx | 10 ++---
 src/client/views/nodes/DocumentView.tsx            |  6 +--
 .../nodes/formattedText/DashDocCommentView.tsx     | 16 ++++----
 .../views/nodes/formattedText/DashDocView.tsx      | 16 ++++----
 .../views/nodes/formattedText/DashFieldView.tsx    |  8 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  8 ++--
 .../views/nodes/formattedText/RichTextRules.ts     | 26 ++++++------
 src/client/views/nodes/formattedText/nodes_rts.ts  |  8 ++--
 src/fields/Doc.ts                                  | 46 ++++++++++++++--------
 src/fields/RichTextField.ts                        | 26 ++++++------
 src/fields/RichTextUtils.ts                        | 10 ++---
 14 files changed, 103 insertions(+), 89 deletions(-)

(limited to 'src/client/views/nodes/formattedText/DashDocView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2820c66ee..c038fd6ab 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -148,7 +148,7 @@ export class CurrentUserUtils {
             { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
             { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
         const reqdNoteList = reqdTempOpts.map(opts => {
-            const reqdOpts = {...opts, title: "text", width:200, autoHeight: true, fitWidth: true, system: true};
+            const reqdOpts = {...opts, title: "text", width:200, autoHeight: true, fitWidth: true};
             const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
             return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
         });
@@ -224,11 +224,11 @@ export class CurrentUserUtils {
                     {
                         type: "paragraph", attrs: {}, content: [{
                             type: "dashField",
-                            attrs: { fieldKey: "author", docid: "", hideKey: false },
+                            attrs: { fieldKey: "author", docId: "", hideKey: false },
                             marks: [{ type: "strong" }]
                         }, {
                             type: "dashField",
-                            attrs: { fieldKey: "creationDate", docid: "", hideKey: false },
+                            attrs: { fieldKey: "creationDate", docId: "", hideKey: false },
                             marks: [{ type: "strong" }]
                         }]
                     }]
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index f849b21e3..0f8b46dbe 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -372,7 +372,7 @@ export class KeyManager {
                                 list.push(doc);
                             }
                             if (count === docids.length) {
-                                const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d, ['links'])).clone : d)));
+                                const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d, true)).clone : d)));
                                 if (added.length) {
                                     added.map(doc => (doc.context = targetDataDoc));
                                     undoBatch(() => {
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 7519cbb05..02dd15960 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -84,7 +84,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                 Doc.GetProto(target)[key] = val;
                 return {
                     type: 'dashField',
-                    attrs: { fieldKey: key, docid: '', hideKey: false, editable: true },
+                    attrs: { fieldKey: key, docId: '', hideKey: false, editable: true },
                     marks: [{ type: 'pFontSize', attrs: { fontSize: '12px' } }, { type: 'strong' }, { type: 'user_mark', attrs: { userid: Doc.CurrentUserEmail, modified: 0 } }],
                 };
             });
@@ -111,7 +111,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                             { type: 'pFontSize', attrs: { fontSize: '8px' } },
                             { type: 'em' },
                         ],
-                        attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false },
+                        attrs: { fieldKey: 'text', docId: anchor[Id], hideKey: true, editable: false },
                     },
                 ],
             },
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e46220f02..bd74c9399 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -269,10 +269,10 @@ export function CollectionSubView<X>(moreProps?: X) {
                 if (FormattedTextBox.IsFragment(html)) {
                     const href = FormattedTextBox.GetHref(html);
                     if (href) {
-                        const docid = FormattedTextBox.GetDocFromUrl(href);
-                        if (docid) {
+                        const docId = FormattedTextBox.GetDocFromUrl(href);
+                        if (docId) {
                             // prosemirror text containing link to dash document
-                            DocServer.GetRefField(docid).then(f => {
+                            DocServer.GetRefField(docId).then(f => {
                                 if (f instanceof Doc) {
                                     if (options.x || options.y) {
                                         f.x = options.x as number;
@@ -311,8 +311,8 @@ export function CollectionSubView<X>(moreProps?: X) {
                     } else {
                         const path = window.location.origin + '/doc/';
                         if (text.startsWith(path)) {
-                            const docid = text.replace(Doc.globalServerPath(), '').split('?')[0];
-                            DocServer.GetRefField(docid).then(f => {
+                            const docId = text.replace(Doc.globalServerPath(), '').split('?')[0];
+                            DocServer.GetRefField(docId).then(f => {
                                 if (f instanceof Doc) {
                                     if (options.x || options.y) {
                                         f.x = options.x as number;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 36e8facf5..edf508c95 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1578,8 +1578,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     public static showBackLinks(linkSource: Doc) {
-        const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish';
-        DocServer.GetRefField(docid).then(docx => {
+        const docId = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish';
+        DocServer.GetRefField(docId).then(docx => {
             const rootAlias = () => {
                 const rootAlias = Doc.MakeAlias(linkSource);
                 rootAlias.x = rootAlias.y = 0;
@@ -1592,7 +1592,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         /*rootAlias()*/
                     ],
                     { title: linkSource.title + '-pivot', _width: 500, _height: 500 },
-                    docid
+                    docId
                 );
             linkCollection.linkSource = linkSource;
             if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript('updateLinkCollection(self)');
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index fcd6e0c55..aa269d8d6 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -32,7 +32,7 @@ export class DashDocCommentView {
         };
 
         this.root = ReactDOM.createRoot(this.dom);
-        this.root.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />);
+        this.root.render(<DashDocCommentViewInternal view={view} getPos={getPos} docId={node.attrs.docId} />);
         (this as any).dom = this.dom;
     }
 
@@ -48,7 +48,7 @@ export class DashDocCommentView {
 }
 
 interface IDashDocCommentViewInternal {
-    docid: string;
+    docId: string;
     view: any;
     getPos: any;
 }
@@ -63,13 +63,13 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
     }
 
     onPointerLeaveCollapsed(e: any) {
-        DocServer.GetRefField(this.props.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowUnhighlight());
+        DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowUnhighlight());
         e.preventDefault();
         e.stopPropagation();
     }
 
     onPointerEnterCollapsed(e: any) {
-        DocServer.GetRefField(this.props.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false));
+        DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false));
         e.preventDefault();
         e.stopPropagation();
     }
@@ -82,7 +82,7 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
             const tr = this.props.view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true });
             this.props.view.dispatch(tr.setSelection(TextSelection.create(tr.doc, this.props.getPos() + (expand ? 2 : 1)))); // update the attrs
             setTimeout(() => {
-                expand && DocServer.GetRefField(this.props.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
+                expand && DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
                 try {
                     this.props.view.dispatch(this.props.view.state.tr.setSelection(TextSelection.create(this.props.view.state.tr.doc, this.props.getPos() + (expand ? 2 : 1))));
                 } catch (e) {}
@@ -100,12 +100,12 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
         const state = this.props.view.state;
         for (let i = this.props.getPos() + 1; i < state.doc.content.size; i++) {
             const m = state.doc.nodeAt(i);
-            if (m && m.type === state.schema.nodes.dashDoc && m.attrs.docid === this.props.docid) {
+            if (m && m.type === state.schema.nodes.dashDoc && m.attrs.docId === this.props.docId) {
                 return { node: m, pos: i, hidden: m.attrs.hidden } as { node: any; pos: number; hidden: boolean };
             }
         }
 
-        const dashDoc = state.schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docid: this.props.docid, float: 'right' });
+        const dashDoc = state.schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docId: this.props.docId, float: 'right' });
         this.props.view.dispatch(state.tr.insert(this.props.getPos() + 1, dashDoc));
         setTimeout(() => {
             try {
@@ -119,7 +119,7 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
         return (
             <span
                 className="formattedTextBox-inlineComment"
-                id={'DashDocCommentView-' + this.props.docid}
+                id={'DashDocCommentView-' + this.props.docId}
                 onPointerLeave={this.onPointerLeaveCollapsed}
                 onPointerEnter={this.onPointerEnterCollapsed}
                 onPointerUp={this.onPointerUpCollapsed}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 19ee1fc7b..61345f891 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -42,7 +42,7 @@ export class DashDocView {
 
         this.root = ReactDOM.createRoot(this.dom);
         this.root.render(
-            <DashDocViewInternal docid={node.attrs.docid} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />
+            <DashDocViewInternal docId={node.attrs.docId} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />
         );
     }
     destroy() {
@@ -53,7 +53,7 @@ export class DashDocView {
 }
 
 interface IDashDocViewInternal {
-    docid: string;
+    docId: string;
     alias: string;
     tbox: FormattedTextBox;
     width: string;
@@ -77,7 +77,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
 
     updateDoc = action((dashDoc: Doc) => {
         this._dashDoc = dashDoc;
-        this._finalLayout = this.props.docid ? dashDoc : Doc.expandTemplateLayout(Doc.Layout(dashDoc), dashDoc, this.props.fieldKey);
+        this._finalLayout = this.props.docId ? dashDoc : Doc.expandTemplateLayout(Doc.Layout(dashDoc), dashDoc, this.props.fieldKey);
 
         if (this._finalLayout) {
             if (!Doc.AreProtosEqual(this._finalLayout, dashDoc)) {
@@ -107,12 +107,12 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
         super(props);
         this._textBox = this.props.tbox;
 
-        DocServer.GetRefField(this.props.docid + this.props.alias).then(async dashDoc => {
+        DocServer.GetRefField(this.props.docId + this.props.alias).then(async dashDoc => {
             if (!(dashDoc instanceof Doc)) {
                 this.props.alias &&
-                    DocServer.GetRefField(this.props.docid).then(async dashDocBase => {
+                    DocServer.GetRefField(this.props.docId).then(async dashDocBase => {
                         if (dashDocBase instanceof Doc) {
-                            const aliasedDoc = Doc.MakeAlias(dashDocBase, this.props.docid + this.props.alias);
+                            const aliasedDoc = Doc.MakeAlias(dashDocBase, this.props.docId + this.props.alias);
                             aliasedDoc.layoutKey = 'layout';
                             this.props.fieldKey && DocUtils.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, this.props.fieldKey, undefined);
                             this.updateDoc(aliasedDoc);
@@ -161,12 +161,12 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
     };
 
     onPointerLeave = () => {
-        const ele = document.getElementById('DashDocCommentView-' + this.props.docid) as HTMLDivElement;
+        const ele = document.getElementById('DashDocCommentView-' + this.props.docId) as HTMLDivElement;
         ele && (ele.style.backgroundColor = '');
     };
 
     onPointerEnter = () => {
-        const ele = document.getElementById('DashDocCommentView-' + this.props.docid) as HTMLDivElement;
+        const ele = document.getElementById('DashDocCommentView-' + this.props.docId) as HTMLDivElement;
         ele && (ele.style.backgroundColor = 'orange');
     };
 
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index a89e8b4ed..21ccf3bc7 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -54,7 +54,7 @@ export class DashFieldView {
                 unclickable={this.unclickable}
                 getPos={getPos}
                 fieldKey={node.attrs.fieldKey}
-                docid={node.attrs.docid}
+                docId={node.attrs.docId}
                 width={node.attrs.width}
                 height={node.attrs.height}
                 hideKey={node.attrs.hideKey}
@@ -76,7 +76,7 @@ export class DashFieldView {
 
 interface IDashFieldViewInternal {
     fieldKey: string;
-    docid: string;
+    docId: string;
     hideKey: boolean;
     tbox: FormattedTextBox;
     width: number;
@@ -100,8 +100,8 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         this._fieldKey = this.props.fieldKey;
         this._textBoxDoc = this.props.tbox.props.Document;
 
-        if (this.props.docid) {
-            DocServer.GetRefField(this.props.docid).then(
+        if (this.props.docId) {
+            DocServer.GetRefField(this.props.docId).then(
                 action(async dashDoc => {
                     dashDoc instanceof Doc && (this._dashDoc = dashDoc);
                 })
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a76f9623a..da10bb0dc 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -186,7 +186,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return '';
     }
     public static GetDocFromUrl(url: string) {
-        return url.startsWith(document.location.origin) ? new URL(url).pathname.split('doc/').lastElement() : ''; // docid
+        return url.startsWith(document.location.origin) ? new URL(url).pathname.split('doc/').lastElement() : ''; // docId
     }
 
     constructor(props: any) {
@@ -545,7 +545,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     width: target[WidthSym](),
                     height: target[HeightSym](),
                     title: 'dashDoc',
-                    docid: target[Id],
+                    docId: target[Id],
                     float: 'unset',
                 });
                 if (!['alias', 'copy'].includes((dragData.dropAction ?? '') as any)) {
@@ -1260,7 +1260,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             DocServer.GetRefField(pdfAnchorId).then(pdfAnchor => {
                 if (pdfAnchor instanceof Doc) {
                     const dashField = view.state.schema.nodes.paragraph.create({}, [
-                        view.state.schema.nodes.dashField.create({ fieldKey: 'text', docid: pdfAnchor[Id], hideKey: true, editable: false }, undefined, [
+                        view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, editable: false }, undefined, [
                             view.state.schema.marks.linkAnchor.create({
                                 allAnchors: [{ href: `/doc/${this.rootDoc[Id]}`, title: this.rootDoc.title, anchorId: `${this.rootDoc[Id]}` }],
                                 location: 'add:right',
@@ -1485,7 +1485,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     onPointerUp = (e: React.PointerEvent): void => {
         const editor = this._editorView!;
         const state = editor?.state;
-        if (!state || !editor) return;
+        if (!state || !editor || !this.ProseRef?.children[0].className.includes('-focused')) return;
         if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
         else if (this.props.isContentActive(true)) {
             const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 5675776fb..9e9b61db3 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -89,8 +89,8 @@ export class RichTextRules {
                 textDoc[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
                 textDoc[inlineFieldKey] = ''; // set a default value for the annotation
                 const node = (state.doc.resolve(start) as any).nodeAfter;
-                const newNode = schema.nodes.dashComment.create({ docid: textDocInline[Id] });
-                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docid: textDocInline[Id], float: 'right' });
+                const newNode = schema.nodes.dashComment.create({ docId: textDocInline[Id] });
+                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docId: textDocInline[Id], float: 'right' });
                 const sm = state.storedMarks || undefined;
                 const replaced = node
                     ? state.tr
@@ -248,17 +248,17 @@ export class RichTextRules {
             // [[fieldKey:Doc]] => show field of doc
             new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
                 const fieldKey = match[1];
-                const docid = match[3]?.replace(':', '');
+                const docId = match[3]?.replace(':', '');
                 const value = match[2]?.substring(1);
                 if (!fieldKey) {
-                    if (docid) {
-                        DocServer.GetRefField(docid).then(docx => {
+                    if (docId) {
+                        DocServer.GetRefField(docId).then(docx => {
                             const rstate = this.TextBox.EditorView?.state;
                             const selection = rstate?.selection.$from.pos;
                             if (rstate) {
                                 this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
                             }
-                            const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
+                            const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500 }, docId);
                             DocUtils.MakeLink({ doc: this.TextBox.getAnchor(true) }, { doc: target }, 'portal to:portal from', undefined);
 
                             const fstate = this.TextBox.EditorView?.state;
@@ -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: false });
+                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);
             }),
 
@@ -311,16 +311,16 @@ export class RichTextRules {
                 const fieldKey = match[1] || '';
                 const fieldParam = match[2]?.replace('…', '...') || '';
                 const rawdocid = match[3]?.substring(1);
-                const docid = rawdocid ? (!rawdocid.includes('@') ? normalizeEmail(Doc.CurrentUserEmail) + '@' + rawdocid : rawdocid) : undefined;
-                if (!fieldKey && !docid) return state.tr;
-                docid &&
-                    DocServer.GetRefField(docid).then(docx => {
+                const docId = rawdocid ? (!rawdocid.includes('@') ? normalizeEmail(Doc.CurrentUserEmail) + '@' + rawdocid : rawdocid) : undefined;
+                if (!fieldKey && !docId) return state.tr;
+                docId &&
+                    DocServer.GetRefField(docId).then(docx => {
                         if (!(docx instanceof Doc && docx)) {
-                            Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docid);
+                            Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docId);
                         }
                     });
                 const node = (state.doc.resolve(start) as any).nodeAfter;
-                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 75, title: 'dashDoc', docid, fieldKey: fieldKey + fieldParam, float: 'unset', alias: Utils.GenerateGuid() });
+                const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 75, title: 'dashDoc', docId, fieldKey: fieldKey + fieldParam, float: 'unset', alias: Utils.GenerateGuid() });
                 const sm = state.storedMarks || undefined;
                 return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
             }),
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index aa2475dca..6c9d5d31a 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -176,7 +176,7 @@ export const nodes: { [index: string]: NodeSpec } = {
 
     dashComment: {
         attrs: {
-            docid: { default: '' },
+            docId: { default: '' },
         },
         inline: true,
         group: 'inline',
@@ -213,7 +213,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             title: { default: null },
             float: { default: 'left' },
             location: { default: 'add:right' },
-            docid: { default: '' },
+            docId: { default: '' },
         },
         group: 'inline',
         draggable: true,
@@ -246,7 +246,7 @@ export const nodes: { [index: string]: NodeSpec } = {
             float: { default: 'right' },
             hidden: { default: false }, // whether dashComment node has toggle the dashDoc's display off
             fieldKey: { default: '' },
-            docid: { default: '' },
+            docId: { default: '' },
             alias: { default: '' },
         },
         group: 'inline',
@@ -261,7 +261,7 @@ export const nodes: { [index: string]: NodeSpec } = {
         inline: true,
         attrs: {
             fieldKey: { default: '' },
-            docid: { default: '' },
+            docId: { default: '' },
             hideKey: { default: false },
             editable: { default: true },
         },
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 40ef67f92..40da482d2 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -695,6 +695,9 @@ export namespace Doc {
         return bestAlias ?? Doc.MakeAlias(doc);
     }
 
+    // this lists out all the tag ids that can be  in a RichTextField that might contain document ids.
+    // if a document is cloned, we need to make sure to clone all of these referenced documents as well;
+    export const DocsInTextFieldIds = ['audioId', 'textId', 'anchorId', 'docId'];
     export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<string, Doc>, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], cloneLinks: boolean, asBranch: boolean): Promise<Doc> {
         if (Doc.IsBaseProto(doc)) return doc;
         if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
@@ -713,12 +716,22 @@ export namespace Doc {
                     if (docs !== undefined && docs.length) {
                         const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, cloneLinks, asBranch)));
                         assignKey(new List<Doc>(clones));
-                    } else if (doc[key] instanceof Doc) {
-                        assignKey(key.includes('layout[') ? undefined : key.startsWith('layout') ? (doc[key] as Doc) : await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, cloneLinks, asBranch)); // reference documents except copy documents that are expanded template fields
                     } else {
                         assignKey(ObjectField.MakeCopy(field));
                         if (field instanceof RichTextField) {
-                            if (field.Data.includes('"audioId":') || field.Data.includes('"textId":') || field.Data.includes('"anchorId":')) {
+                            if (DocsInTextFieldIds.some(id => field.Data.includes(`"${id}":`))) {
+                                const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => '(' + exp + ')').join('|') + ')":"([a-z-A-Z0-9_]*)"', 'g');
+                                const rawdocids = field.Data.match(docidsearch);
+                                const docids = rawdocids?.map((str: string) =>
+                                    DocsInTextFieldIds.reduce((output, exp) => {
+                                        return output.replace(new RegExp(`${exp}":`, 'g'), '');
+                                    }, str)
+                                        .replace(/"/g, '')
+                                        .trim()
+                                );
+                                const results = docids && (await DocServer.GetRefFields(docids));
+                                const docs = results && Array.from(Object.keys(results)).map(key => DocCast(results[key]));
+                                docs && docs.map(doc => Doc.makeClone(doc, cloneMap, linkMap, rtfs, exclusions, cloneLinks, asBranch));
                                 rtfs.push({ copy, key, field });
                             }
                         }
@@ -726,24 +739,22 @@ export namespace Doc {
                 };
                 const docAtKey = doc[key];
                 if (docAtKey instanceof Doc) {
-                    if (!Doc.IsSystem(docAtKey) && (key === 'annotationOn' || key === 'proto' || ((key === 'anchor1' || key === 'anchor2') && doc.author === Doc.CurrentUserEmail))) {
+                    if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || key === 'annotationOn' || key === 'proto' || ((key === 'anchor1' || key === 'anchor2') && doc.author === Doc.CurrentUserEmail))) {
                         assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, cloneLinks, asBranch));
                     } else {
                         assignKey(docAtKey);
                     }
+                } else if (field instanceof RefField) {
+                    assignKey(field);
+                } else if (cfield instanceof ComputedField) {
+                    assignKey(cfield[Copy]());
+                    //   ComputedField.MakeFunction(cfield.script.originalScript));
+                } else if (field instanceof ObjectField) {
+                    await copyObjectField(field);
+                } else if (field instanceof Promise) {
+                    debugger; //This shouldn't happen...
                 } else {
-                    if (field instanceof RefField) {
-                        assignKey(field);
-                    } else if (cfield instanceof ComputedField) {
-                        assignKey(cfield[Copy]());
-                        //   ComputedField.MakeFunction(cfield.script.originalScript));
-                    } else if (field instanceof ObjectField) {
-                        await copyObjectField(field);
-                    } else if (field instanceof Promise) {
-                        debugger; //This shouldn't happen...
-                    } else {
-                        assignKey(field);
-                    }
+                    assignKey(field);
                 }
             })
         );
@@ -807,7 +818,8 @@ export namespace Doc {
             };
             const regex = `(${Doc.localServerPath()})([^"]*)`;
             const re = new RegExp(regex, 'g');
-            copy[key] = new RichTextField(field.Data.replace(/("textId":|"audioId":|"anchorId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
+            const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => `"${exp}":`).join('|') + ')"([^"]+)"', 'g');
+            copy[key] = new RichTextField(field.Data.replace(docidsearch, replacer).replace(re, replacer2), field.Text);
         });
         return { clone: copy, map: cloneMap, linkMap };
     }
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index d7edd4266..3e75a071f 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -1,11 +1,11 @@
-import { serializable } from "serializr";
-import { scriptingGlobal } from "../client/util/ScriptingGlobals";
-import { Deserializable } from "../client/util/SerializationHelper";
-import { Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { ObjectField } from "./ObjectField";
+import { serializable } from 'serializr';
+import { scriptingGlobal } from '../client/util/ScriptingGlobals';
+import { Deserializable } from '../client/util/SerializationHelper';
+import { Copy, ToScriptString, ToString } from './FieldSymbols';
+import { ObjectField } from './ObjectField';
 
 @scriptingGlobal
-@Deserializable("RichTextField")
+@Deserializable('RichTextField')
 export class RichTextField extends ObjectField {
     @serializable(true)
     readonly Data: string;
@@ -13,14 +13,14 @@ export class RichTextField extends ObjectField {
     @serializable(true)
     readonly Text: string;
 
-    constructor(data: string, text: string = "") {
+    constructor(data: string, text: string = '') {
         super();
         this.Data = data;
         this.Text = text;
     }
 
     Empty() {
-        return !(this.Text || this.Data.toString().includes("dashField") || this.Data.toString().includes("align"));
+        return !(this.Text || this.Data.toString().includes('dashField') || this.Data.toString().includes('align'));
     }
 
     [Copy]() {
@@ -28,14 +28,16 @@ export class RichTextField extends ObjectField {
     }
 
     [ToScriptString]() {
-        return `new RichTextField("${this.Data.replace(/"/g, "\\\"")}", "${this.Text}")`;
+        return `new RichTextField("${this.Data.replace(/"/g, '\\"')}", "${this.Text}")`;
     }
     [ToString]() {
         return this.Text;
     }
 
     public static DashField(fieldKey: string) {
-        return new RichTextField(`{"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":"${fieldKey}","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`, "");
+        return new RichTextField(
+            `{"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":"${fieldKey}","docId":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`,
+            ''
+        );
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index bf055cd8b..239b59e83 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -264,18 +264,18 @@ export namespace RichTextUtils {
 
         const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => {
             const { url: src, width, agnostic } = image;
-            let docid: string;
+            let docId: string;
             const guid = Utils.GenerateDeterministicGuid(agnostic);
             const backingDocId = StrCast(textNote[guid]);
             if (!backingDocId) {
                 const backingDoc = Docs.Create.ImageDocument(agnostic, { _width: 300, _height: 300 });
                 DocUtils.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument);
-                docid = backingDoc[Id];
-                textNote[guid] = docid;
+                docId = backingDoc[Id];
+                textNote[guid] = docId;
             } else {
-                docid = backingDocId;
+                docId = backingDocId;
             }
-            return schema.node('image', { src, agnostic, width, docid, float: null, location: 'add:right' });
+            return schema.node('image', { src, agnostic, width, docId, float: null, location: 'add:right' });
         };
 
         const textNode = (schema: any, run: docs_v1.Schema$TextRun) => {
-- 
cgit v1.2.3-70-g09d2

