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

---
 src/client/views/nodes/formattedText/DashDocCommentView.tsx | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

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

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() {}
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


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/DashDocCommentView.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 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/DashDocCommentView.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

