From 57c1b8235b26a1b6a315922b4dc2926e1e597674 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 13 May 2022 18:17:10 -0400
Subject: removed layerProvider functionality.

---
 src/client/views/collections/collectionSchema/SchemaTable.tsx | 1 -
 1 file changed, 1 deletion(-)

(limited to 'src/client/views/collections/collectionSchema/SchemaTable.tsx')

diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 605481ddf..bea5b3be6 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -573,7 +573,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         Document={this._showDoc}
                         DataDoc={this._showDataDoc}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={undefined}
                         docViewPath={returnEmptyDoclist}
                         freezeDimensions={true}
                         focus={DocUtils.DefaultFocus}
-- 
cgit v1.2.3-70-g09d2


From 0dd3bbfc119d48bd2c32f8e55b7051e3ddc530c3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 21 Jun 2022 14:40:04 -0400
Subject: restored experimental templates.  fixed (enabled) pointer events on
 nested column/row views when container is active.  got rid of
 freezeDimensions which wasn't needed and introduced artifacts for views that
 set it (timeView, GridView)

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/CurrentUserUtils.ts                | 74 +++++++++++-----------
 src/client/util/DragManager.ts                     | 11 ++--
 src/client/views/PropertiesView.tsx                |  1 -
 .../views/collections/CollectionStackingView.tsx   |  1 -
 .../views/collections/CollectionTimeView.tsx       |  5 +-
 src/client/views/collections/CollectionView.tsx    |  1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 -
 .../collectionGrid/CollectionGridView.tsx          |  1 -
 .../CollectionMulticolumnView.tsx                  | 14 ++--
 .../CollectionMultirowView.tsx                     |  6 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  1 -
 .../collections/collectionSchema/SchemaTable.tsx   |  1 -
 src/client/views/nodes/DocumentContentsView.tsx    |  1 -
 src/client/views/nodes/DocumentView.tsx            |  7 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  4 +-
 16 files changed, 61 insertions(+), 69 deletions(-)

(limited to 'src/client/views/collections/collectionSchema/SchemaTable.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 73b756a8d..59391d150 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -194,6 +194,7 @@ export class DocumentOptions {
     childLimitHeight?: number; // whether to limit the height of collection children.  0 - means  height can be no bigger than width
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
+    childDocumentsActive?: boolean; // whether child documents are active when parent is document active
     childDontRegisterViews?: boolean;
     childHideLinkButton?: boolean; // hide link buttons on all children
     childContextMenuFilters?: List<ScriptField>;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 9b5fc5b98..e80fe6776 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,6 +1,6 @@
 import { computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
-import { DataSym, Doc, DocListCast, DocListCastAsync, StrListCast } from "../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, Opt, StrListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
@@ -109,40 +109,43 @@ export class CurrentUserUtils {
     }
 
     // initializes experimental advanced template views - slideView,  headerView
-    static setupExperimentalTemplateButtons(doc: Doc, field="template-experimental-buttons") {
-        const tempDocs = DocCast(doc[field]);
-        const requiredTypeNameFields:{opts:DocumentOptions, template:() => Doc}[] = [
+    static setupExperimentalTemplateButtons(doc: Doc, tempDocs?:Doc) {
+        const requiredTypeNameFields:{btnOpts:DocumentOptions, templateOpts:DocumentOptions, template:(opts:DocumentOptions) => Doc}[] = [
             {
-                opts:{type: "slide", icon: "address-card"}, template: () => Docs.Create.MultirowDocument(
+                btnOpts: { title: "slide", icon: "address-card" }, 
+                templateOpts: { _width: 400, _height: 300, title: "slideView", childDocumentsActive: true, _xMargin: 3, _yMargin: 3, system: true }, 
+                template: (opts:DocumentOptions) => Docs.Create.MultirowDocument(
                     [
-                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }),
-                        Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
-                    ],
-                    { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true }
-                )
+                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200,  system: true }),
+                        Docs.Create.TextDocument("", { title: "text", _fitWidth:true,  _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
+                    ], opts)
             },
             {
-                opts:{type: "mobile", icon: "mobile"}, template: () => this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, },
-                    [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
-                    this.mobileTextContainer({},
-                        [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
-                    ]
+                btnOpts: { title: "mobile", icon: "mobile" }, 
+                templateOpts: { title: "NEW MOBILE BUTTON", onClick: undefined, }, 
+                template: (opts:DocumentOptions) => this.mobileButton(opts,
+                        [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
+                        this.mobileTextContainer({},
+                            [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
+                        ]
                 )
             },
         ];
-        const requiredTypes = requiredTypeNameFields.map(({ opts, template }) => {
-            const docType =  DocListCast(tempDocs?.data)?.find(doc => doc.title === opts.type);
-            const reqdOpts = {
-                dragFactory: template(),
-                title: opts.type,
-                icon: opts.icon
+        const requiredTypes = requiredTypeNameFields.map(({ btnOpts, template, templateOpts }) => {
+            const tempBtn =  DocListCast(tempDocs?.data)?.find(doc => doc.title === btnOpts.title);
+            const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory); }' };
+            const assignBtnAndTempOpts = (templateBtn:Opt<Doc>, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => {
+                if (templateBtn) {
+                    this.AssignOpts(templateBtn,btnOpts);
+                    this.AssignOpts(DocCast(templateBtn.dragFactory), templateOptions) ?? (templateBtn.dragFactory = template(templateOpts));
+                }
+                return templateBtn;
             };
-            const reqdScripts = {onDragStart: 'copyDragFactory(this.dragFactory)'};
             const makeTemp = (doc:Doc) => {
                 doc.isTemplateDoc = makeTemplate(doc);
                 return doc;
             }
-            return this.AssignScripts(!docType ? makeTemp(CurrentUserUtils.createToolButton(reqdOpts)) : this.AssignOpts(docType, reqdOpts)!, reqdScripts)!;
+            return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? CurrentUserUtils.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts);
         });
 
         const reqdOpts = {
@@ -150,12 +153,9 @@ export class CurrentUserUtils {
             _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true,
             _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
-        const reqdScripts = {dropConverter : "convertToButtons(dragData)"};
-        const reqdFuncs = {hidden: "IsNoviceMode()"}
-        return this.AssignScripts(!tempDocs ? 
-                (doc[field] = Docs.Create.MasonryDocument(requiredTypes, reqdOpts)) :
-                this.AssignOpts(tempDocs, reqdOpts, requiredTypes)!, 
-            reqdScripts, reqdFuncs);
+        const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
+        const reqdFuncs = { hidden: "IsNoviceMode()" };
+        return this.AssignScripts(this.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
     }
 
     /// Initializes templates that can be applied to notes
@@ -525,12 +525,12 @@ export class CurrentUserUtils {
     static setupToolsBtnPanel(doc: Doc, field:string) {
         const myTools = DocCast(doc[field]);
         const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined);
-        //const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc);
+        const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
         const reqdToolOps:DocumentOptions = {
             title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
             _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
         };
-        this.AssignOpts(myTools, reqdToolOps, [creatorBtns, /*templateBtns*/]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, /*templateBtns*/], reqdToolOps));
+        this.AssignOpts(myTools, reqdToolOps, [creatorBtns, templateBtns]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, templateBtns], reqdToolOps));
     }
 
     /// initializes the left sidebar dashboard pane
@@ -749,7 +749,7 @@ export class CurrentUserUtils {
     }
 
     static schemaTools():Button[] {
-        return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{onClick:'toggleSchemaPreview(_readOnly_)'}, }];
+        return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{return toggleSchemaPreview(_readOnly_);}'}, }];
     }
 
     static webTools() {
@@ -774,9 +774,9 @@ export class CurrentUserUtils {
             }, // Always show
             { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton,icon: "chevron-left",  scripts:{onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'} },
             { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, icon: "chevron-right", scripts:{onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}},
-            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: 'selectedDocumentType()' }}, // Only when a document is selected
-            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: 'selectedDocumentType()'} },
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: 'selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform
+            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: '!selectedDocumentType()' }}, // Only when a document is selected
+            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: '!selectedDocumentType()'} },
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
             { title: "Text", icon: "text",  subMenu: CurrentUserUtils.textTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available
             { title: "Ink", icon: "ink", subMenu: CurrentUserUtils.inkTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available
@@ -983,7 +983,6 @@ export class CurrentUserUtils {
         doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
         await this.setupLinkDocs(doc, linkDatabaseId);
         await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
@@ -995,7 +994,8 @@ export class CurrentUserUtils {
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
         this.setupDocTemplates(doc); // sets up the template menu of templates
         doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument());
-
+        doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
+        
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
         if (doc.activeDashboard instanceof Doc) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2b62945af..09b463c2f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -223,10 +223,13 @@ export namespace DragManager {
             if (docDragData && !docDragData.droppedDocuments.length) {
                 docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
                 docDragData.droppedDocuments =
-                    await Promise.all(dragData.draggedDocuments.map(async d => !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
-                        docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
-                            docDragData.dropAction === "proto" ? Doc.GetProto(d) :
-                                docDragData.dropAction === "copy" ? (await Doc.MakeClone(d)).clone : d));
+                    await Promise.all(dragData.draggedDocuments.map(async d => 
+                        !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? 
+                        addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) :
+                            docDragData.dropAction === "alias" ? Doc.MakeAlias(d) :
+                                docDragData.dropAction === "proto" ? Doc.GetProto(d) :
+                                    docDragData.dropAction === "copy" ? 
+                                        (await Doc.MakeClone(d)).clone : d));
                 !["same", "proto"].includes(docDragData.dropAction as any) && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
                     const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []);
                     const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 90c86fa18..faab2ed26 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -304,7 +304,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     docViewPath={returnEmptyDoclist}
-                    freezeDimensions={true}
                     dontCenter={"y"}
                     isDocumentActive={returnFalse}
                     isContentActive={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 277fcd59c..684c919bd 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -250,7 +250,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             isDocumentActive={this.isContentActive}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            freezeDimensions={this.props.childFreezeDimensions}
             NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeWidth(doc) ? width : undefined}  // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
             NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeHeight(doc) ? height : undefined}
             dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 7573b938a..3dd9d2d84 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -59,7 +59,7 @@ export class CollectionTimeView extends CollectionSubView() {
         //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), "");
         ///const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
         runInAction(() => {
-            this._childClickedScript = ScriptField.MakeScript("openInLightbox(self, shiftKey)", { this: Doc.name, shiftKey: "boolean" });//, { detailView: detailView! });
+            this._childClickedScript = ScriptField.MakeScript("openInLightbox(self)", { this: Doc.name });
             this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" });
         });
     }
@@ -138,8 +138,7 @@ export class CollectionTimeView extends CollectionSubView() {
                 fitContentsToBox={returnTrue}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
-                childFreezeDimensions={true}
-                dontScaleFilter={this.dontScaleFilter}
+                //dontScaleFilter={this.dontScaleFilter}
                 layoutEngine={this.layoutEngine} />
         </div>;
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 63616263e..b432104a1 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -75,7 +75,6 @@ export interface CollectionViewProps extends FieldViewProps {
     childHideResizeHandles?: () => boolean;
     childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
     childLayoutString?: string;
-    childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height
     childIgnoreNativeSize?: boolean;
     childClickScript?: ScriptField;
     childDoubleClickScript?: ScriptField;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 99eec7892..542b1fce1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1265,7 +1265,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             styleProvider={this.getClusterColor}
             dataProvider={this.childDataProvider}
             sizeProvider={this.childSizeProvider}
-            freezeDimensions={BoolCast(this.props.Document.childFreezeDimensions, this.props.childFreezeDimensions)}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             bringToFront={this.bringToFront}
             showTitle={this.props.childShowTitle}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index da102fe18..4e4c33446 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -162,7 +162,6 @@ export class CollectionGridView extends CollectionSubView() {
             DataDoc={layout.resolvedDataDoc as Doc}
             PanelWidth={width}
             PanelHeight={height}
-            freezeDimensions={true}
             ScreenToLocalTransform={dxf}
             onClick={this.onChildClickHandler}
             renderDepth={this.props.renderDepth + 1}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b7ba94940..777ef464f 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -6,6 +6,7 @@ import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -242,7 +243,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         return this.props.addDocTab(doc, where);
     }
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
+    isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -251,13 +252,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
-            isContentActive={this.isChildContentActive}
-            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
-            hideResizeHandles={this.props.childHideResizeHandles?.()}
-            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToBox={this.props.fitContentsToBox}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
@@ -266,6 +261,11 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             onDoubleClick={this.onChildDoubleClickHandler}
             suppressSetHeight={true}
             ScreenToLocalTransform={dxf}
+            isContentActive={this.isChildContentActive}
+            isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+            hideResizeHandles={this.props.childHideResizeHandles?.()}
+            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+            fitContentsToBox={this.props.fitContentsToBox}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 338639a83..08385bcb5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -6,6 +6,7 @@ import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -242,7 +243,7 @@ export class CollectionMultirowView extends CollectionSubView() {
         return this.props.addDocTab(doc, where);
     }
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
+    isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -251,7 +252,6 @@ export class CollectionMultirowView extends CollectionSubView() {
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
-            freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             PanelWidth={width}
             PanelHeight={height}
@@ -261,7 +261,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             isContentActive={this.isChildContentActive}
-            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
+            isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
             fitContentsToBox={this.props.fitContentsToBox}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f45068b6a..9eba788a9 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -403,7 +403,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     Document={this.previewDocument}
                     DataDoc={undefined}
                     fitContentsToBox={returnTrue}
-                    freezeDimensions={true}
                     dontCenter={"y"}
                     focus={DocUtils.DefaultFocus}
                     renderDepth={this.props.renderDepth}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index bea5b3be6..43266a571 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -574,7 +574,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         DataDoc={this._showDataDoc}
                         styleProvider={DefaultStyleProvider}
                         docViewPath={returnEmptyDoclist}
-                        freezeDimensions={true}
                         focus={DocUtils.DefaultFocus}
                         renderDepth={this.props.renderDepth}
                         rootSelected={returnFalse}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 70732e74c..db076c069 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -143,7 +143,6 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
         const docOnlyProps = [  // these are the properties in  DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
-            "freezeDimensions",
             "hideResizeHandles",
             "hideTitle",
             "treeViewDoc",
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 89096c948..263e1dc4c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -160,7 +160,6 @@ export interface DocumentViewSharedProps {
 // these props are specific to DocuentViews
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
-    freezeDimensions?: boolean;
     hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
     hideTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDecorationTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
@@ -1220,11 +1219,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); }
     @computed get nativeWidth() {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
-            returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
+            returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
     }
     @computed get nativeHeight() {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
-            returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
+            returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
     }
     @computed get shouldNotScale() {
         return (this.fitWidth && !this.nativeWidth) ||
@@ -1312,8 +1311,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     ContentScale = () => this.nativeScaling;
     selfView = () => this;
     screenToLocalTransform = () => {
-        const oshift = this.fitWidth && this.ComponentView instanceof FormattedTextBox;
-        const shift = oshift ? -(this.props.PanelHeight() - this.rootDoc[HeightSym]()) / 2 : 0;
         return this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).translate(0, shift).scale(1 / this.nativeScaling);
     }
     componentDidMount() {
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 1b37e729b..85efc67a5 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -903,9 +903,9 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
     }
     else if (selected) {
         if (NumCast(selected.schemaPreviewWidth) > 0) {
-            selected.schemaPreviewWidth = 200;
-        } else {
             selected.schemaPreviewWidth = 0;
+        } else {
+            selected.schemaPreviewWidth = 200;
         }
     }
 });
-- 
cgit v1.2.3-70-g09d2


From 6a637ac37962dbb402bb3ec1172cadafef9eab20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 2 Jul 2022 10:41:40 -0400
Subject: fixing  many eslint errors

---
 .eslintrc.json                                     |   23 +-
 src/client/util/CurrentUserUtils.ts                |    1 -
 .../CollectionSchemaMovableRow.tsx                 |  102 +-
 .../collections/collectionSchema/SchemaTable.tsx   |  660 +++++++------
 src/client/views/nodes/button/FontIconBox.tsx      |   12 +-
 .../nodes/formattedText/DashDocCommentView.tsx     |   77 +-
 .../views/nodes/formattedText/DashDocView.tsx      |  181 ++--
 .../views/nodes/formattedText/DashFieldView.tsx    |  229 +++--
 .../views/nodes/formattedText/EquationView.tsx     |   99 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 1008 +++++++++++---------
 .../formattedText/FormattedTextBoxComment.tsx      |  107 ++-
 .../formattedText/ProsemirrorExampleTransfer.ts    |  213 +++--
 .../views/nodes/formattedText/RichTextMenu.tsx     |  361 ++++---
 .../views/nodes/formattedText/RichTextRules.ts     |  574 ++++++-----
 .../views/nodes/formattedText/SummaryView.tsx      |   73 +-
 src/client/views/nodes/formattedText/marks_rts.ts  |  382 ++++----
 src/client/views/nodes/formattedText/nodes_rts.ts  |  361 ++++---
 src/fields/Doc.ts                                  |  865 +++++++++--------
 src/fields/RichTextUtils.ts                        |  230 +++--
 19 files changed, 3033 insertions(+), 2525 deletions(-)

(limited to 'src/client/views/collections/collectionSchema/SchemaTable.tsx')

diff --git a/.eslintrc.json b/.eslintrc.json
index 5cc0ab6dc..b9f8e1b7a 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,13 +1,14 @@
 {
-  "extends": ["airbnb", "prettier", "plugin:node/recommended"],
-  "plugins": ["prettier"],
-  "rules": {
-    "prettier/prettier": "error",
-    "no-unused-vars": "warn",
-    "no-console": "off",
-    "func-names": "off",
-    "no-process-exit": "off",
-    "object-shorthand": "off",
-    "class-methods-use-this": "off"
-  }
+    "extends": ["airbnb", "prettier", "plugin:node/recommended"],
+    "plugins": ["prettier"],
+    "rules": {
+        "prettier/prettier": "error",
+        "no-unused-vars": "warn",
+        "no-console": "off",
+        "func-names": "off",
+        "no-process-exit": "off",
+        "object-shorthand": "off",
+        "class-methods-use-this": "off",
+        "single-quote": "off"
+    }
 }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index dca77250c..b2a5fddcd 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -905,7 +905,6 @@ export class CurrentUserUtils {
         return doc.clickFuncs as Doc;
     }
 
-
     /// Updates the UserDoc to have all required fields, docs, etc.  No changes should need to be
     /// written to the server if the code hasn't changed.  However, choices need to be made for each Doc/field
     /// whether to revert to "default" values, or to leave them as the user/system last set them.
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
index 0e19ef3d9..f872637e5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
@@ -1,17 +1,16 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action } from "mobx";
-import { ReactTableDefaults, RowInfo, TableCellRenderer } from "react-table";
-import { Doc } from "../../../../fields/Doc";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, FieldValue, StrCast } from "../../../../fields/Types";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { DragManager, dropActionType, SetupDrag } from "../../../util/DragManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { ContextMenu } from "../../ContextMenu";
-import "./CollectionSchemaView.scss";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action } from 'mobx';
+import * as React from 'react';
+import { ReactTableDefaults, RowInfo } from 'react-table';
+import { Doc } from '../../../../fields/Doc';
+import { Cast, FieldValue, StrCast } from '../../../../fields/Types';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { DragManager, dropActionType, SetupDrag } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import './CollectionSchemaView.scss';
 
 export interface MovableRowProps {
     rowInfo: RowInfo;
@@ -25,7 +24,7 @@ export interface MovableRowProps {
     addDocTab: any;
 }
 
-export class MovableRow extends React.Component<MovableRowProps> {
+export class MovableRow extends React.Component<React.PropsWithChildren<MovableRowProps>> {
     private _header?: React.RefObject<HTMLDivElement> = React.createRef();
     private _rowDropDisposer?: DragManager.DragDropDisposer;
 
@@ -33,28 +32,27 @@ export class MovableRow extends React.Component<MovableRowProps> {
     // Create one when the mouse starts hovering...
     onPointerEnter = (e: React.PointerEvent): void => {
         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
-            this._header!.current!.className = "collectionSchema-row-wrapper";
-            document.addEventListener("pointermove", this.onDragMove, true);
+            this._header!.current!.className = 'collectionSchema-row-wrapper';
+            document.addEventListener('pointermove', this.onDragMove, true);
         }
-    }
+    };
     // ... and delete it when the mouse leaves
     onPointerLeave = (e: React.PointerEvent): void => {
-        this._header!.current!.className = "collectionSchema-row-wrapper";
-        document.removeEventListener("pointermove", this.onDragMove, true);
-    }
+        this._header!.current!.className = 'collectionSchema-row-wrapper';
+        document.removeEventListener('pointermove', this.onDragMove, true);
+    };
     // The method for the event listener, reorders columns when dragged to their new locations.
     onDragMove = (e: PointerEvent): void => {
         const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
         const rect = this._header!.current!.getBoundingClientRect();
         const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
         const before = x[1] < bounds[1];
-        this._header!.current!.className = "collectionSchema-row-wrapper";
-        if (before) this._header!.current!.className += " row-above";
-        if (!before) this._header!.current!.className += " row-below";
+        this._header!.current!.className = 'collectionSchema-row-wrapper';
+        if (before) this._header!.current!.className += ' row-above';
+        if (!before) this._header!.current!.className += ' row-below';
         e.stopPropagation();
-    }
+    };
     componentWillUnmount() {
-
         this._rowDropDisposer?.();
     }
     //
@@ -63,7 +61,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
         if (ele) {
             this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
         }
-    }
+    };
     // Controls what hppens when a row is dragged and dropped
     rowDrop = (e: Event, de: DragManager.DropEvent) => {
         this.onPointerLeave(e as any);
@@ -81,34 +79,34 @@ export class MovableRow extends React.Component<MovableRowProps> {
             if (docDragData.draggedDocuments[0] === rowDoc) return true;
             const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before);
             const movedDocs = docDragData.draggedDocuments;
-            return (docDragData.dropAction || docDragData.userDropAction) ?
-                docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
-                : (docDragData.moveDocument) ?
-                    movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
-                    : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
+            return docDragData.dropAction || docDragData.userDropAction
+                ? docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
+                : docDragData.moveDocument
+                ? movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
+                : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
         }
         return false;
-    }
+    };
 
     onRowContextMenu = (e: React.MouseEvent): void => {
-        const description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row";
-        ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: "file-pdf" });
-    }
+        const description = this.props.rowWrapped ? 'Unwrap text on row' : 'Text wrap row';
+        ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: 'file-pdf' });
+    };
 
     @undoBatch
     @action
     move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => {
         const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
         return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
-    }
+    };
 
     @action
     onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        console.log("yes");
-        if (e.key === "Backspace" || e.key === "Delete") {
+        console.log('yes');
+        if (e.key === 'Backspace' || e.key === 'Delete') {
             undoBatch(() => this.props.removeDoc(this.props.rowInfo.original));
         }
-    }
+    };
 
     render() {
         const { children = null, rowInfo } = this.props;
@@ -120,23 +118,29 @@ export class MovableRow extends React.Component<MovableRowProps> {
         const { original } = rowInfo;
         const doc = FieldValue(Cast(original, Doc));
 
-        if (!doc) return (null);
+        if (!doc) return null;
 
         const reference = React.createRef<HTMLDivElement>();
         const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
 
-        let className = "collectionSchema-row";
-        if (this.props.rowFocused) className += " row-focused";
-        if (this.props.rowWrapped) className += " row-wrapped";
+        let className = 'collectionSchema-row';
+        if (this.props.rowFocused) className += ' row-focused';
+        if (this.props.rowWrapped) className += ' row-wrapped';
 
         return (
             <div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
                 <div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
-                    <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown} >
+                    <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown}>
                         <div className="row-dragger">
-                            <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
-                            <div className="row-option" style={{ cursor: "grab" }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
-                            <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+                            <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}>
+                                <FontAwesomeIcon icon="trash" size="sm" />
+                            </div>
+                            <div className="row-option" style={{ cursor: 'grab' }} ref={reference} onPointerDown={onItemDown}>
+                                <FontAwesomeIcon icon="grip-vertical" size="sm" />
+                            </div>
+                            <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, 'add:right')}>
+                                <FontAwesomeIcon icon="external-link-alt" size="sm" />
+                            </div>
                         </div>
                         {children}
                     </ReactTableDefaults.TrComponent>
@@ -144,4 +148,4 @@ export class MovableRow extends React.Component<MovableRowProps> {
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 43266a571..fafea5ce3 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -1,37 +1,47 @@
-import React = require("react");
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace } from "mobx";
-import { observer } from "mobx-react";
-import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
-import { DateField } from "../../../../fields/DateField";
-import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { List } from "../../../../fields/List";
-import { listSpec } from "../../../../fields/Schema";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { ComputedField } from "../../../../fields/ScriptField";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { ImageField } from "../../../../fields/URLField";
-import { GetEffectiveAcl } from "../../../../fields/util";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../../Utils";
-import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CompileScript, Transformer, ts } from "../../../util/Scripting";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
-import { ContextMenu } from "../../ContextMenu";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from 'react-table';
+import { DateField } from '../../../../fields/DateField';
+import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { ComputedField } from '../../../../fields/ScriptField';
+import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { GetEffectiveAcl } from '../../../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../../Utils';
+import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { CompileScript, Transformer, ts } from '../../../util/Scripting';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
 import '../../../views/DocumentDecorations.scss';
-import { DocumentView } from "../../nodes/DocumentView";
-import { DefaultStyleProvider } from "../../StyleProvider";
-import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
-import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders";
-import { MovableColumn } from "./CollectionSchemaMovableColumn";
-import { MovableRow } from "./CollectionSchemaMovableRow";
-import "./CollectionSchemaView.scss";
-import { CollectionView } from "../CollectionView";
-
+import { ContextMenu } from '../../ContextMenu';
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
+import { DocumentView } from '../../nodes/DocumentView';
+import { DefaultStyleProvider } from '../../StyleProvider';
+import { CollectionView } from '../CollectionView';
+import {
+    CellProps,
+    CollectionSchemaButtons,
+    CollectionSchemaCell,
+    CollectionSchemaCheckboxCell,
+    CollectionSchemaDateCell,
+    CollectionSchemaDocCell,
+    CollectionSchemaImageCell,
+    CollectionSchemaListCell,
+    CollectionSchemaNumberCell,
+    CollectionSchemaStringCell,
+} from './CollectionSchemaCells';
+import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders';
+import { MovableColumn } from './CollectionSchemaMovableColumn';
+import { MovableRow } from './CollectionSchemaMovableRow';
+import './CollectionSchemaView.scss';
 
 enum ColumnType {
     Any,
@@ -41,15 +51,22 @@ enum ColumnType {
     Doc,
     Image,
     List,
-    Date
+    Date,
 }
 
 // this map should be used for keys that should have a const type of value
 const columnTypes: Map<string, ColumnType> = new Map([
-    ["title", ColumnType.String],
-    ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number],
-    ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
-    ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
+    ['title', ColumnType.String],
+    ['x', ColumnType.Number],
+    ['y', ColumnType.Number],
+    ['_width', ColumnType.Number],
+    ['_height', ColumnType.Number],
+    ['_nativeWidth', ColumnType.Number],
+    ['_nativeHeight', ColumnType.Number],
+    ['isPrototype', ColumnType.Boolean],
+    ['_curPage', ColumnType.Number],
+    ['_currentTimecode', ColumnType.Number],
+    ['zIndex', ColumnType.Number],
 ]);
 
 export interface SchemaTableProps {
@@ -92,18 +109,24 @@ export interface SchemaTableProps {
 @observer
 export class SchemaTable extends React.Component<SchemaTableProps> {
     @observable _cellIsEditing: boolean = false;
-    @observable _focusedCell: { row: number, col: number } = { row: 0, col: 0 };
-    @observable _openCollections: Set<number> = new Set;
+    @observable _focusedCell: { row: number; col: number } = { row: 0, col: 0 };
+    @observable _openCollections: Set<number> = new Set();
 
     @observable _showDoc: Doc | undefined;
-    @observable _showDataDoc: any = "";
+    @observable _showDataDoc: any = '';
     @observable _showDocPos: number[] = [];
 
     @observable _showTitleDropdown: boolean = false;
 
-    @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
-    @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
-    @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); }
+    @computed get previewWidth() {
+        return () => NumCast(this.props.Document.schemaPreviewWidth);
+    }
+    @computed get previewHeight() {
+        return () => this.props.PanelHeight() - 2 * this.borderWidth;
+    }
+    @computed get tableWidth() {
+        return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
+    }
 
     @computed get childDocs() {
         if (this.props.childDocs) return this.props.childDocs;
@@ -117,17 +140,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     }
 
     @computed get textWrappedRows() {
-        return Cast(this.props.Document.textwrappedSchemaRows, listSpec("string"), []);
+        return Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
     }
     set textWrappedRows(textWrappedRows: string[]) {
         this.props.Document.textwrappedSchemaRows = new List<string>(textWrappedRows);
     }
 
-    @computed get resized(): { id: string, value: number }[] {
+    @computed get resized(): { id: string; value: number }[] {
         return this.props.columns.reduce((resized, shf) => {
-            (shf.width > -1) && resized.push({ id: shf.heading, value: shf.width });
+            shf.width > -1 && resized.push({ id: shf.heading, value: shf.width });
             return resized;
-        }, [] as { id: string, value: number }[]);
+        }, [] as { id: string; value: number }[]);
     }
     @computed get sorted(): SortingRule[] {
         return this.props.columns.reduce((sorted, shf) => {
@@ -139,12 +162,14 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
     @action
     changeSorting = (col: any) => {
         this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
-    }
+    };
 
     @action
-    changeTitleMode = () => this._showTitleDropdown = !this._showTitleDropdown
+    changeTitleMode = () => (this._showTitleDropdown = !this._showTitleDropdown);
 
-    @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
+    @computed get borderWidth() {
+        return Number(COLLECTION_BORDER_WIDTH);
+    }
     @computed get tableColumns(): Column<Doc>[] {
         const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
         const columns: Column<Doc>[] = [];
@@ -154,149 +179,185 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
         const isEditable = !this.props.headerIsEditing;
 
         columns.push({
-            expander: true, Header: "", width: 58,
-            Expander: (rowInfo) => {
-                return rowInfo.original.type !== DocumentType.COL ? (null) :
-                    <div className="collectionSchemaView-expander" onClick={action(() => (this._openCollections[rowInfo.isExpanded ? "delete" : "add"])(rowInfo.viewIndex))}>
-                        <FontAwesomeIcon icon={rowInfo.isExpanded ? "caret-down" : "caret-right"} size="lg" />
-                    </div>;
-            }
+            expander: true,
+            Header: '',
+            width: 58,
+            Expander: rowInfo => {
+                return rowInfo.original.type !== DocumentType.COL ? null : (
+                    <div className="collectionSchemaView-expander" onClick={action(() => this._openCollections[rowInfo.isExpanded ? 'delete' : 'add'](rowInfo.viewIndex))}>
+                        <FontAwesomeIcon icon={rowInfo.isExpanded ? 'caret-down' : 'caret-right'} size="lg" />
+                    </div>
+                );
+            },
         });
-        columns.push(...this.props.columns.map(col => {
-            const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" :
-                this.getColumnType(col) === ColumnType.Boolean ? "check-square" : this.getColumnType(col) === ColumnType.Doc ? "file" :
-                    this.getColumnType(col) === ColumnType.Image ? "image" : this.getColumnType(col) === ColumnType.List ? "list-ul" :
-                        this.getColumnType(col) === ColumnType.Date ? "calendar" : "align-justify";
-
-            const keysDropdown = <KeysDropdown
-                keyValue={col.heading}
-                possibleKeys={possibleKeys}
-                existingKeys={this.props.columns.map(c => c.heading)}
-                canAddNew={true}
-                addNew={false}
-                onSelect={this.props.changeColumns}
-                setIsEditing={this.props.setHeaderIsEditing}
-                docs={this.props.childDocs}
-                Document={this.props.Document}
-                dataDoc={this.props.dataDoc}
-                fieldKey={this.props.fieldKey}
-                ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                ContainingCollectionView={this.props.ContainingCollectionView}
-                active={this.props.active}
-                openHeader={this.props.openHeader}
-                icon={icon}
-                col={col}
-                // try commenting this out
-                width={"100%"}
-            />;
-
-            const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
-            const header = <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}>
-                {keysDropdown}
-                <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "pointer" }}>
-                    <FontAwesomeIcon icon={sortIcon} size="lg" />
-                </div>
-                {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
-            </div>;
-
-            return {
-                Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
-                accessor: (doc: Doc) => doc ? Field.toString(doc[col.heading] as Field) : 0,
-                id: col.heading,
-                Cell: (rowProps: CellInfo) => {
-                    const rowIndex = rowProps.index;
-                    const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
-                    const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
-
-                    const props: CellProps = {
-                        row: rowIndex,
-                        col: columnIndex,
-                        rowProps: rowProps,
-                        isFocused: isFocused,
-                        changeFocusedCellByIndex: this.changeFocusedCellByIndex,
-                        CollectionView: this.props.CollectionView,
-                        ContainingCollection: this.props.ContainingCollectionView,
-                        Document: this.props.Document,
-                        fieldKey: this.props.fieldKey,
-                        renderDepth: this.props.renderDepth,
-                        addDocTab: this.props.addDocTab,
-                        pinToPres: this.props.pinToPres,
-                        moveDocument: this.props.moveDocument,
-                        setIsEditing: this.setCellIsEditing,
-                        isEditable: isEditable,
-                        setPreviewDoc: this.props.setPreviewDoc,
-                        setComputed: this.setComputed,
-                        getField: this.getField,
-                        showDoc: this.showDoc,
-                    };
-
-
-                    switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
-                        case ColumnType.Number: return <CollectionSchemaNumberCell {...props} />;
-                        case ColumnType.String: return <CollectionSchemaStringCell {...props} />;
-                        case ColumnType.Boolean: return <CollectionSchemaCheckboxCell {...props} />;
-                        case ColumnType.Doc: return <CollectionSchemaDocCell {...props} />;
-                        case ColumnType.Image: return <CollectionSchemaImageCell {...props} />;
-                        case ColumnType.List: return <CollectionSchemaListCell {...props} />;
-                        case ColumnType.Date: return <CollectionSchemaDateCell {...props} />;
-                        default:
-                            return <CollectionSchemaCell {...props} />;
-                    }
-                },
-                minWidth: 200,
-            };
-        }));
+        columns.push(
+            ...this.props.columns.map(col => {
+                const icon: IconProp =
+                    this.getColumnType(col) === ColumnType.Number
+                        ? 'hashtag'
+                        : this.getColumnType(col) === ColumnType.String
+                        ? 'font'
+                        : this.getColumnType(col) === ColumnType.Boolean
+                        ? 'check-square'
+                        : this.getColumnType(col) === ColumnType.Doc
+                        ? 'file'
+                        : this.getColumnType(col) === ColumnType.Image
+                        ? 'image'
+                        : this.getColumnType(col) === ColumnType.List
+                        ? 'list-ul'
+                        : this.getColumnType(col) === ColumnType.Date
+                        ? 'calendar'
+                        : 'align-justify';
+
+                const keysDropdown = (
+                    <KeysDropdown
+                        keyValue={col.heading}
+                        possibleKeys={possibleKeys}
+                        existingKeys={this.props.columns.map(c => c.heading)}
+                        canAddNew={true}
+                        addNew={false}
+                        onSelect={this.props.changeColumns}
+                        setIsEditing={this.props.setHeaderIsEditing}
+                        docs={this.props.childDocs}
+                        Document={this.props.Document}
+                        dataDoc={this.props.dataDoc}
+                        fieldKey={this.props.fieldKey}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                        ContainingCollectionView={this.props.ContainingCollectionView}
+                        active={this.props.active}
+                        openHeader={this.props.openHeader}
+                        icon={icon}
+                        col={col}
+                        // try commenting this out
+                        width={'100%'}
+                    />
+                );
+
+                const sortIcon = col.desc === undefined ? 'caret-right' : col.desc === true ? 'caret-down' : 'caret-up';
+                const header = (
+                    <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: '2px', display: 'flex', cursor: 'default', height: '100%' }}>
+                        {keysDropdown}
+                        <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: 'inline', zIndex: 1, background: 'inherit', cursor: 'pointer' }}>
+                            <FontAwesomeIcon icon={sortIcon} size="lg" />
+                        </div>
+                        {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
+                    </div>
+                );
+
+                return {
+                    Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
+                    accessor: (doc: Doc) => (doc ? Field.toString(doc[col.heading] as Field) : 0),
+                    id: col.heading,
+                    Cell: (rowProps: CellInfo) => {
+                        const rowIndex = rowProps.index;
+                        const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
+                        const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
+
+                        const props: CellProps = {
+                            row: rowIndex,
+                            col: columnIndex,
+                            rowProps: rowProps,
+                            isFocused: isFocused,
+                            changeFocusedCellByIndex: this.changeFocusedCellByIndex,
+                            CollectionView: this.props.CollectionView,
+                            ContainingCollection: this.props.ContainingCollectionView,
+                            Document: this.props.Document,
+                            fieldKey: this.props.fieldKey,
+                            renderDepth: this.props.renderDepth,
+                            addDocTab: this.props.addDocTab,
+                            pinToPres: this.props.pinToPres,
+                            moveDocument: this.props.moveDocument,
+                            setIsEditing: this.setCellIsEditing,
+                            isEditable: isEditable,
+                            setPreviewDoc: this.props.setPreviewDoc,
+                            setComputed: this.setComputed,
+                            getField: this.getField,
+                            showDoc: this.showDoc,
+                        };
+
+                        switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
+                            case ColumnType.Number:
+                                return <CollectionSchemaNumberCell {...props} />;
+                            case ColumnType.String:
+                                return <CollectionSchemaStringCell {...props} />;
+                            case ColumnType.Boolean:
+                                return <CollectionSchemaCheckboxCell {...props} />;
+                            case ColumnType.Doc:
+                                return <CollectionSchemaDocCell {...props} />;
+                            case ColumnType.Image:
+                                return <CollectionSchemaImageCell {...props} />;
+                            case ColumnType.List:
+                                return <CollectionSchemaListCell {...props} />;
+                            case ColumnType.Date:
+                                return <CollectionSchemaDateCell {...props} />;
+                            default:
+                                return <CollectionSchemaCell {...props} />;
+                        }
+                    },
+                    minWidth: 200,
+                };
+            })
+        );
         columns.push({
             Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
             accessor: (doc: Doc) => 0,
-            id: "add",
+            id: 'add',
             Cell: (rowProps: CellInfo) => {
                 const rowIndex = rowProps.index;
                 const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
                 const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
-                return <CollectionSchemaButtons  {...{
-                    row: rowProps.index,
-                    col: columnIndex,
-                    rowProps: rowProps,
-                    isFocused: isFocused,
-                    changeFocusedCellByIndex: this.changeFocusedCellByIndex,
-                    CollectionView: this.props.CollectionView,
-                    ContainingCollection: this.props.ContainingCollectionView,
-                    Document: this.props.Document,
-                    fieldKey: this.props.fieldKey,
-                    renderDepth: this.props.renderDepth,
-                    addDocTab: this.props.addDocTab,
-                    pinToPres: this.props.pinToPres,
-                    moveDocument: this.props.moveDocument,
-                    setIsEditing: this.setCellIsEditing,
-                    isEditable: isEditable,
-                    setPreviewDoc: this.props.setPreviewDoc,
-                    setComputed: this.setComputed,
-                    getField: this.getField,
-                    showDoc: this.showDoc,
-                }} />;
+                return (
+                    <CollectionSchemaButtons
+                        {...{
+                            row: rowProps.index,
+                            col: columnIndex,
+                            rowProps: rowProps,
+                            isFocused: isFocused,
+                            changeFocusedCellByIndex: this.changeFocusedCellByIndex,
+                            CollectionView: this.props.CollectionView,
+                            ContainingCollection: this.props.ContainingCollectionView,
+                            Document: this.props.Document,
+                            fieldKey: this.props.fieldKey,
+                            renderDepth: this.props.renderDepth,
+                            addDocTab: this.props.addDocTab,
+                            pinToPres: this.props.pinToPres,
+                            moveDocument: this.props.moveDocument,
+                            setIsEditing: this.setCellIsEditing,
+                            isEditable: isEditable,
+                            setPreviewDoc: this.props.setPreviewDoc,
+                            setComputed: this.setComputed,
+                            getField: this.getField,
+                            showDoc: this.showDoc,
+                        }}
+                    />
+                );
             },
             width: 28,
-            resizable: false
+            resizable: false,
         });
         return columns;
     }
 
-
     constructor(props: SchemaTableProps) {
         super(props);
         if (this.props.Document._schemaHeaders === undefined) {
-            this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb", ColumnType.Date),
-            new SchemaHeaderField("text", "#f1efeb", ColumnType.String), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb", ColumnType.Doc)]);
+            this.props.Document._schemaHeaders = new List<SchemaHeaderField>([
+                new SchemaHeaderField('title', '#f1efeb'),
+                new SchemaHeaderField('author', '#f1efeb'),
+                new SchemaHeaderField('*lastModified', '#f1efeb', ColumnType.Date),
+                new SchemaHeaderField('text', '#f1efeb', ColumnType.String),
+                new SchemaHeaderField('type', '#f1efeb'),
+                new SchemaHeaderField('context', '#f1efeb', ColumnType.Doc),
+            ]);
         }
     }
 
     componentDidMount() {
-        document.addEventListener("keydown", this.onKeyDown);
+        document.addEventListener('keydown', this.onKeyDown);
     }
 
     componentWillUnmount() {
-        document.removeEventListener("keydown", this.onKeyDown);
+        document.removeEventListener('keydown', this.onKeyDown);
     }
 
     tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
@@ -305,25 +366,27 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
 
         if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) {
             doc.context = this.props.Document;
-            tableDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+            tableDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
             return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
         }
         return false;
-    }
+    };
 
     private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
-        return !rowInfo ? {} : {
-            ScreenToLocalTransform: this.props.ScreenToLocalTransform,
-            addDoc: this.tableAddDoc,
-            removeDoc: this.props.deleteDocument,
-            rowInfo,
-            rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
-            textWrapRow: this.toggleTextWrapRow,
-            rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
-            dropAction: StrCast(this.props.Document.childDropAction),
-            addDocTab: this.props.addDocTab
-        };
-    }
+        return !rowInfo
+            ? {}
+            : {
+                  ScreenToLocalTransform: this.props.ScreenToLocalTransform,
+                  addDoc: this.tableAddDoc,
+                  removeDoc: this.props.deleteDocument,
+                  rowInfo,
+                  rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
+                  textWrapRow: this.toggleTextWrapRow,
+                  rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
+                  dropAction: StrCast(this.props.Document.childDropAction),
+                  addDocTab: this.props.addDocTab,
+              };
+    };
 
     private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
         if (!rowInfo || column) return {};
@@ -334,16 +397,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
         const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
         // TODO: editing border doesn't work :(
         return {
-            style: { border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb" }
+            style: { border: !this.props.headerIsEditing && isFocused ? '2px solid rgb(255, 160, 160)' : '1px solid #f1efeb' },
         };
-    }
+    };
 
-    @action setCellIsEditing = (isEditing: boolean) => this._cellIsEditing = isEditing;
+    @action setCellIsEditing = (isEditing: boolean) => (this._cellIsEditing = isEditing);
 
     @action
     onKeyDown = (e: KeyboardEvent): void => {
-        if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {// && this.props.isSelected(true)) {
-            const direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
+        if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {
+            // && this.props.isSelected(true)) {
+            const direction = e.key === 'Tab' ? 'tab' : e.which === 39 ? 'right' : e.which === 37 ? 'left' : e.which === 38 ? 'up' : e.which === 40 ? 'down' : '';
             this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
 
             if (direction) {
@@ -353,20 +417,25 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             }
         } else if (e.keyCode === 27) {
             this.props.setPreviewDoc(undefined);
-            e.stopPropagation(); // stopPropagation for left/right arrows 
+            e.stopPropagation(); // stopPropagation for left/right arrows
         }
-    }
+    };
 
     changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => {
         switch (direction) {
-            case "tab": return { row: (curRow + 1 === this.childDocs.length ? 0 : curRow + 1), col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 };
-            case "right": return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 };
-            case "left": return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
-            case "up": return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
-            case "down": return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
+            case 'tab':
+                return { row: curRow + 1 === this.childDocs.length ? 0 : curRow + 1, col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 };
+            case 'right':
+                return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 };
+            case 'left':
+                return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
+            case 'up':
+                return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
+            case 'down':
+                return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
         }
         return this._focusedCell;
-    }
+    };
 
     @action
     changeFocusedCellByIndex = (row: number, col: number): void => {
@@ -374,25 +443,25 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             this._focusedCell = { row: row, col: col };
         }
         this.props.setFocused(this.props.Document);
-    }
+    };
 
     @undoBatch
     createRow = action(() => {
-        this.props.addDocument?.(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
+        this.props.addDocument?.(Docs.Create.TextDocument('', { title: '', _width: 100, _height: 30 }));
         this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
     });
 
     @undoBatch
     @action
     createColumn = () => {
-        const newFieldName = (index: number) => `New field${index ? ` (${index})` : ""}`;
+        const newFieldName = (index: number) => `New field${index ? ` (${index})` : ''}`;
         for (let index = 0; index < 100; index++) {
             if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) {
-                this.props.columns.push(new SchemaHeaderField(newFieldName(index), "#f1efeb"));
+                this.props.columns.push(new SchemaHeaderField(newFieldName(index), '#f1efeb'));
                 break;
             }
         }
-    }
+    };
 
     @action
     getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => {
@@ -407,15 +476,15 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             return column.type;
         }
         if (columnTypes.get(column.heading)) {
-            return column.type = columnTypes.get(column.heading)!;
+            return (column.type = columnTypes.get(column.heading)!);
         }
-        return column.type = ColumnType.Any;
-    }
+        return (column.type = ColumnType.Any);
+    };
 
     @undoBatch
     @action
     toggleTextwrap = async () => {
-        const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec("string"), []);
+        const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
         if (textwrappedRows.length) {
             this.props.Document.textwrappedSchemaRows = new List<string>([]);
         } else {
@@ -423,7 +492,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
             this.props.Document.textwrappedSchemaRows = new List<string>(allRows);
         }
-    }
+    };
 
     @action
     toggleTextWrapRow = (doc: Doc): void => {
@@ -433,41 +502,50 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
         index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
 
         this.textWrappedRows = textWrapped;
-    }
+    };
 
     @computed
     get reactTable() {
         const children = this.childDocs;
         const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
         const expanded: { [name: string]: any } = {};
-        Array.from(this._openCollections.keys()).map(col => expanded[col.toString()] = true);
+        Array.from(this._openCollections.keys()).map(col => (expanded[col.toString()] = true));
         const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
 
-        return <ReactTable
-            style={{ position: "relative" }}
-            data={children}
-            page={0}
-            pageSize={children.length}
-            showPagination={false}
-            columns={this.tableColumns}
-            getTrProps={this.getTrProps}
-            getTdProps={this.getTdProps}
-            sortable={false}
-            TrComponent={MovableRow}
-            sorted={this.sorted}
-            expanded={expanded}
-            resized={this.resized}
-            onResizedChange={this.props.onResizedChange}
-            // if it has a child, render another table with the children
-            SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== DocumentType.COL) ? (null) :
-                <div style={{ paddingLeft: 57 + "px" }} className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /></div>}
-
-        />;
+        return (
+            <ReactTable
+                style={{ position: 'relative' }}
+                data={children}
+                page={0}
+                pageSize={children.length}
+                showPagination={false}
+                columns={this.tableColumns}
+                getTrProps={this.getTrProps}
+                getTdProps={this.getTdProps}
+                sortable={false}
+                TrComponent={MovableRow}
+                sorted={this.sorted}
+                expanded={expanded}
+                resized={this.resized}
+                onResizedChange={this.props.onResizedChange}
+                // if it has a child, render another table with the children
+                SubComponent={
+                    !hasCollectionChild
+                        ? undefined
+                        : row =>
+                              row.original.type !== DocumentType.COL ? null : (
+                                  <div style={{ paddingLeft: 57 + 'px' }} className="reactTable-sub">
+                                      <SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} />
+                                  </div>
+                              )
+                }
+            />
+        );
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
-        ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
-    }
+        ContextMenu.Instance.addItem({ description: 'Toggle text wrapping', event: this.toggleTextwrap, icon: 'table' });
+    };
 
     getField = (row: number, col?: number) => {
         const docs = this.childDocs;
@@ -484,7 +562,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             return doc[column];
         }
         return undefined;
-    }
+    };
 
     createTransformer = (row: number, col: number): Transformer => {
         const self = this;
@@ -498,11 +576,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
                         const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
                         if (isntPropAccess && isntPropAssign) {
-                            if (node.text === "$r") {
+                            if (node.text === '$r') {
                                 return ts.createNumericLiteral(row.toString());
-                            } else if (node.text === "$c") {
+                            } else if (node.text === '$c') {
                                 return ts.createNumericLiteral(col.toString());
-                            } else if (node.text === "$") {
+                            } else if (node.text === '$') {
                                 if (ts.isCallExpression(node.parent)) {
                                     // captures.doc = self.props.Document;
                                     // captures.key = self.props.fieldKey;
@@ -521,12 +599,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
         //     return { capturedVariables: captures };
         // };
 
-        return { transformer, /*getVars*/ };
-    }
+        return { transformer /*getVars*/ };
+    };
 
     setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => {
-        script =
-            `const $ = (row:number, col?:number) => {
+        script = `const $ = (row:number, col?:number) => {
                 const rval = (doc as any)[key][row + ${row}];
                 return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading];
             }
@@ -537,7 +614,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             return true;
         }
         return false;
-    }
+    };
 
     @action
     showDoc = (doc: Doc | undefined, dataDoc?: Doc, screenX?: number, screenY?: number) => {
@@ -545,55 +622,72 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
         if (dataDoc && screenX && screenY) {
             this._showDocPos = this.props.ScreenToLocalTransform().transformPoint(screenX, screenY);
         }
-    }
+    };
 
     onOpenClick = () => {
-        this._showDoc && this.props.addDocTab(this._showDoc, "add:right");
-    }
+        this._showDoc && this.props.addDocTab(this._showDoc, 'add:right');
+    };
 
     getPreviewTransform = (): Transform => {
-        return this.props.ScreenToLocalTransform().translate(- this.borderWidth - 4 - this.tableWidth, - this.borderWidth);
-    }
+        return this.props.ScreenToLocalTransform().translate(-this.borderWidth - 4 - this.tableWidth, -this.borderWidth);
+    };
 
     render() {
-        const preview = "";
-        return <div className="collectionSchemaView-table"
-            onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()}
-            onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
-            {this.reactTable}
-            {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
-            {!this._showDoc ? (null) :
-                <div className="collectionSchemaView-documentPreview" ref="overlay"
-                    style={{
-                        position: "absolute", width: 150, height: 150,
-                        background: "dimgray", display: "block", top: 0, left: 0,
-                        transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`
-                    }} >
-                    <DocumentView
-                        Document={this._showDoc}
-                        DataDoc={this._showDataDoc}
-                        styleProvider={DefaultStyleProvider}
-                        docViewPath={returnEmptyDoclist}
-                        focus={DocUtils.DefaultFocus}
-                        renderDepth={this.props.renderDepth}
-                        rootSelected={returnFalse}
-                        isContentActive={returnTrue}
-                        isDocumentActive={returnFalse}
-                        PanelWidth={() => 150}
-                        PanelHeight={() => 150}
-                        ScreenToLocalTransform={this.getPreviewTransform}
-                        docFilters={returnEmptyFilter}
-                        docRangeFilters={returnEmptyFilter}
-                        searchFilterDocs={returnEmptyDoclist}
-                        ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-                        ContainingCollectionView={this.props.CollectionView}
-                        moveDocument={this.props.moveDocument}
-                        whenChildContentsActiveChanged={emptyFunction}
-                        addDocTab={this.props.addDocTab}
-                        pinToPres={this.props.pinToPres}
-                        bringToFront={returnFalse}>
-                    </DocumentView>
-                </div>}
-        </div>;
+        const preview = '';
+        return (
+            <div
+                className="collectionSchemaView-table"
+                onPointerDown={this.props.onPointerDown}
+                onClick={this.props.onClick}
+                onWheel={e => this.props.active(true) && e.stopPropagation()}
+                onDrop={e => this.props.onDrop(e, {})}
+                onContextMenu={this.onContextMenu}>
+                {this.reactTable}
+                {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : (
+                    <div className="collectionSchemaView-addRow" onClick={this.createRow}>
+                        + new
+                    </div>
+                )}
+                {!this._showDoc ? null : (
+                    <div
+                        className="collectionSchemaView-documentPreview"
+                        ref="overlay"
+                        style={{
+                            position: 'absolute',
+                            width: 150,
+                            height: 150,
+                            background: 'dimgray',
+                            display: 'block',
+                            top: 0,
+                            left: 0,
+                            transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`,
+                        }}>
+                        <DocumentView
+                            Document={this._showDoc}
+                            DataDoc={this._showDataDoc}
+                            styleProvider={DefaultStyleProvider}
+                            docViewPath={returnEmptyDoclist}
+                            focus={DocUtils.DefaultFocus}
+                            renderDepth={this.props.renderDepth}
+                            rootSelected={returnFalse}
+                            isContentActive={returnTrue}
+                            isDocumentActive={returnFalse}
+                            PanelWidth={() => 150}
+                            PanelHeight={() => 150}
+                            ScreenToLocalTransform={this.getPreviewTransform}
+                            docFilters={returnEmptyFilter}
+                            docRangeFilters={returnEmptyFilter}
+                            searchFilterDocs={returnEmptyDoclist}
+                            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                            ContainingCollectionView={this.props.CollectionView}
+                            moveDocument={this.props.moveDocument}
+                            whenChildContentsActiveChanged={emptyFunction}
+                            addDocTab={this.props.addDocTab}
+                            pinToPres={this.props.pinToPres}
+                            bringToFront={returnFalse}></DocumentView>
+                    </div>
+                )}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 85efc67a5..fa3029a98 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,14 +1,13 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { StringIterator } from 'lodash';
+import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Doc, HeightSym, StrListCast, WidthSym } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
-import { createSchema } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { WebField } from '../../../../fields/URLField';
@@ -33,9 +32,6 @@ import { RichTextMenu } from '../formattedText/RichTextMenu';
 import { WebBox } from '../WebBox';
 import { FontIconBadge } from './FontIconBadge';
 import './FontIconBox.scss';
-const FontIconSchema = createSchema({
-    icon: "string",
-});
 
 export enum ButtonType {
     TextButton = "textBtn",
@@ -628,11 +624,7 @@ ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", chec
         if (active === mapStyle) return Colors.MEDIUM_BLUE;
         return "transparent";
     }
-    if (editorView) {
-        const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
-        editorView?.state && RichTextMenu.Instance.changeListType(
-            editorView.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle }));
-    }
+    editorView?.state && RichTextMenu.Instance.changeListType(mapStyle);
 });
 
 // toggle: Set overlay status of selected document
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index 5c75a589a..40dd6fbc7 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -1,37 +1,44 @@
-import { TextSelection } from "prosemirror-state";
+import { TextSelection } from 'prosemirror-state';
 import * as ReactDOM from 'react-dom';
-import { Doc } from "../../../../fields/Doc";
-import { DocServer } from "../../../DocServer";
-import React = require("react");
-
+import { Doc } from '../../../../fields/Doc';
+import { DocServer } from '../../../DocServer';
+import React = require('react');
 
 // creates an inline comment in a note when '>>' is typed.
 // the comment sits on the right side of the note and vertically aligns with its anchor in the text.
 // the comment can be toggled on/off with the '<-' text anchor.
 export class DashDocCommentView {
-    _fieldWrapper: HTMLDivElement; // container for label and value
+    dom: HTMLDivElement; // container for label and value
 
     constructor(node: any, view: any, getPos: any) {
-        this._fieldWrapper = document.createElement("div");
-        this._fieldWrapper.style.width = node.attrs.width;
-        this._fieldWrapper.style.height = node.attrs.height;
-        this._fieldWrapper.style.fontWeight = "bold";
-        this._fieldWrapper.style.position = "relative";
-        this._fieldWrapper.style.display = "inline-block";
-        this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
-
-        ReactDOM.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />, this._fieldWrapper);
-        (this as any).dom = this._fieldWrapper;
+        this.dom = document.createElement('div');
+        this.dom.style.width = node.attrs.width;
+        this.dom.style.height = node.attrs.height;
+        this.dom.style.fontWeight = 'bold';
+        this.dom.style.position = 'relative';
+        this.dom.style.display = 'inline-block';
+        this.dom.onkeypress = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onkeydown = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onkeyup = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onmousedown = function (e: any) {
+            e.stopPropagation();
+        };
+
+        ReactDOM.render(<DashDocCommentViewInternal view={view} getPos={getPos} docid={node.attrs.docid} />, this.dom);
+        (this as any).dom = this.dom;
     }
 
     destroy() {
-        ReactDOM.unmountComponentAtNode(this._fieldWrapper);
+        ReactDOM.unmountComponentAtNode(this.dom);
     }
 
-    selectNode() { }
+    selectNode() {}
 }
 
 interface IDashDocCommentViewInternal {
@@ -40,8 +47,7 @@ interface IDashDocCommentViewInternal {
     getPos: any;
 }
 
-export class DashDocCommentViewInternal extends React.Component<IDashDocCommentViewInternal>{
-
+export class DashDocCommentViewInternal extends React.Component<IDashDocCommentViewInternal> {
     constructor(props: IDashDocCommentViewInternal) {
         super(props);
         this.onPointerLeaveCollapsed = this.onPointerLeaveCollapsed.bind(this);
@@ -71,7 +77,9 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
             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));
-                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) { }
+                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) {}
             }, 0);
         }
         e.stopPropagation();
@@ -81,32 +89,35 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
         e.stopPropagation();
     }
 
-    targetNode = () => {  // search forward in the prosemirror doc for the attached dashDocNode that is the target of the comment anchor
+    targetNode = () => {
+        // search forward in the prosemirror doc for the attached dashDocNode that is the target of the comment anchor
         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) {
-                return { node: m, pos: i, hidden: m.attrs.hidden } as { node: any, pos: number, hidden: boolean };
+                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 { this.props.view.dispatch(state.tr.setSelection(TextSelection.create(state.tr.doc, this.props.getPos() + 2))); } catch (e) { } }, 0);
+        setTimeout(() => {
+            try {
+                this.props.view.dispatch(state.tr.setSelection(TextSelection.create(state.tr.doc, this.props.getPos() + 2)));
+            } catch (e) {}
+        }, 0);
         return undefined;
-    }
+    };
 
     render() {
         return (
             <span
                 className="formattedTextBox-inlineComment"
-                id={"DashDocCommentView-" + this.props.docid}
+                id={'DashDocCommentView-' + this.props.docid}
                 onPointerLeave={this.onPointerLeaveCollapsed}
                 onPointerEnter={this.onPointerEnterCollapsed}
                 onPointerUp={this.onPointerUpCollapsed}
-                onPointerDown={this.onPointerDownCollapsed}
-            >
-            </span>
+                onPointerDown={this.onPointerDownCollapsed}></span>
         );
     }
 }
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 1d8e3a2cf..9d203b6cc 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,52 +1,54 @@
-import { IReactionDisposer, reaction, observable, action } from "mobx";
-import { NodeSelection } from "prosemirror-state";
-import { Doc, HeightSym, WidthSym } from "../../../../fields/Doc";
-import { Cast, StrCast, NumCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, Utils, returnTransparent } from "../../../../Utils";
-import { DocServer } from "../../../DocServer";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { Transform } from "../../../util/Transform";
-import { DocumentView } from "../DocumentView";
-import { FormattedTextBox } from "./FormattedTextBox";
-import React = require("react");
+import { IReactionDisposer, reaction, observable, action } from 'mobx';
+import { NodeSelection } from 'prosemirror-state';
+import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
+import { Cast, StrCast, NumCast } from '../../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, Utils, returnTransparent } from '../../../../Utils';
+import { DocServer } from '../../../DocServer';
+import { Docs, DocUtils } from '../../../documents/Documents';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { Transform } from '../../../util/Transform';
+import { DocumentView } from '../DocumentView';
+import { FormattedTextBox } from './FormattedTextBox';
+import React = require('react');
 import * as ReactDOM from 'react-dom';
-import { observer } from "mobx-react";
-import { ColorScheme } from "../../../util/SettingsManager";
+import { observer } from 'mobx-react';
+import { ColorScheme } from '../../../util/SettingsManager';
 
 export class DashDocView {
-    _fieldWrapper: HTMLSpanElement; // container for label and value
+    dom: HTMLSpanElement; // container for label and value
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
-        this._fieldWrapper = document.createElement("span");
-        this._fieldWrapper.style.position = "relative";
-        this._fieldWrapper.style.textIndent = "0";
-        this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimgray" : "lightGray"));
-        this._fieldWrapper.style.width = node.attrs.width;
-        this._fieldWrapper.style.height = node.attrs.height;
-        this._fieldWrapper.style.display = node.attrs.hidden ? "none" : "inline-block";
-        (this._fieldWrapper.style as any).float = node.attrs.float;
-        this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onmousedown = function (e: any) { 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._fieldWrapper);
-        (this as any).dom = this._fieldWrapper;
+        this.dom = document.createElement('span');
+        this.dom.style.position = 'relative';
+        this.dom.style.textIndent = '0';
+        this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'lightGray');
+        this.dom.style.width = node.attrs.width;
+        this.dom.style.height = node.attrs.height;
+        this.dom.style.display = node.attrs.hidden ? 'none' : 'inline-block';
+        (this.dom.style as any).float = node.attrs.float;
+        this.dom.onkeypress = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onkeydown = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onkeyup = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onmousedown = function (e: any) {
+            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 as any).dom = this.dom;
     }
-    destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
-    selectNode() { }
+    destroy() {
+        ReactDOM.unmountComponentAtNode(this.dom);
+    }
+    selectNode() {}
 }
 
 interface IDashDocViewInternal {
@@ -70,7 +72,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
     @observable _finalLayout: any;
     @observable _resolvedDataDoc: any;
 
-
     updateDoc = action((dashDoc: Doc) => {
         this._dashDoc = dashDoc;
         this._finalLayout = this.props.docid ? dashDoc : Doc.expandTemplateLayout(Doc.Layout(dashDoc), dashDoc, this.props.fieldKey);
@@ -81,13 +82,18 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
             }
             this._resolvedDataDoc = Cast(this._finalLayout.resolvedDataDoc, Doc, null);
         }
-        if (this.props.width !== (this._dashDoc?._width ?? "") + "px" || this.props.height !== (this._dashDoc?._height ?? "") + "px") {
-            try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
-                this.props.view.dispatch(this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
-                    ...this.props.node.attrs, width: (this._dashDoc?._width ?? "") + "px", height: (this._dashDoc?._height ?? "") + "px"
-                }));
+        if (this.props.width !== (this._dashDoc?._width ?? '') + 'px' || this.props.height !== (this._dashDoc?._height ?? '') + 'px') {
+            try {
+                // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
+                this.props.view.dispatch(
+                    this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
+                        ...this.props.node.attrs,
+                        width: (this._dashDoc?._width ?? '') + 'px',
+                        height: (this._dashDoc?._height ?? '') + 'px',
+                    })
+                );
             } catch (e) {
-                console.log("DashDocView:" + e);
+                console.log('DashDocView:' + e);
             }
         }
     });
@@ -98,14 +104,15 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
 
         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 => {
-                    if (dashDocBase instanceof Doc) {
-                        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);
-                    }
-                });
+                this.props.alias &&
+                    DocServer.GetRefField(this.props.docid).then(async dashDocBase => {
+                        if (dashDocBase instanceof Doc) {
+                            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);
+                        }
+                    });
             } else {
                 this.updateDoc(dashDoc);
             }
@@ -113,67 +120,70 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
     }
 
     componentDidMount() {
-        this._disposers.upater = reaction(() => this._dashDoc && (NumCast(this._dashDoc._height) + NumCast(this._dashDoc._width)),
+        this._disposers.upater = reaction(
+            () => this._dashDoc && NumCast(this._dashDoc._height) + NumCast(this._dashDoc._width),
             () => {
                 if (this._dashDoc) {
-                    this.props.view.dispatch(this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
-                        ...this.props.node.attrs, width: (this._dashDoc?._width ?? "") + "px", height: (this._dashDoc?._height ?? "") + "px"
-                    }));
+                    this.props.view.dispatch(
+                        this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
+                            ...this.props.node.attrs,
+                            width: (this._dashDoc?._width ?? '') + 'px',
+                            height: (this._dashDoc?._height ?? '') + 'px',
+                        })
+                    );
                 }
-            });
+            }
+        );
     }
 
-
     removeDoc = () => {
-        this.props.view.dispatch(this.props.view.state.tr
-            .setSelection(new NodeSelection(this.props.view.state.doc.resolve(this.props.getPos())))
-            .deleteSelection());
+        this.props.view.dispatch(this.props.view.state.tr.setSelection(new NodeSelection(this.props.view.state.doc.resolve(this.props.getPos()))).deleteSelection());
         return true;
-    }
+    };
 
     getDocTransform = () => {
         if (!this._spanRef.current) return Transform.Identity();
         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();
-        if (e.key === "Tab" || e.key === "Enter") {
+        if (e.key === 'Tab' || e.key === 'Enter') {
             e.preventDefault();
         }
-    }
+    };
 
     onPointerLeave = () => {
-        const ele = document.getElementById("DashDocCommentView-" + this.props.docid) as HTMLDivElement;
-        ele && (ele.style.backgroundColor = "");
-    }
+        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;
-        ele && (ele.style.backgroundColor = "orange");
-    }
+        const ele = document.getElementById('DashDocCommentView-' + this.props.docid) as HTMLDivElement;
+        ele && (ele.style.backgroundColor = 'orange');
+    };
 
     componentWillUnmount = () => Object.values(this._disposers).forEach(disposer => disposer?.());
 
     render() {
-        return !this._dashDoc || !this._finalLayout || this.props.hidden ? null :
-            <div ref={this._spanRef}
+        return !this._dashDoc || !this._finalLayout || this.props.hidden ? null : (
+            <div
+                ref={this._spanRef}
                 className="dash-span"
                 style={{
                     width: this.props.width,
                     height: this.props.height,
                     position: 'absolute',
-                    display: 'inline-block'
+                    display: 'inline-block',
                 }}
                 onPointerLeave={this.onPointerLeave}
                 onPointerEnter={this.onPointerEnter}
                 onKeyDown={this.onKeyDown}
                 onKeyPress={e => e.stopPropagation()}
                 onKeyUp={e => e.stopPropagation()}
-                onWheel={e => e.preventDefault()}
-            >
+                onWheel={e => e.preventDefault()}>
                 <DocumentView
                     Document={this._finalLayout}
                     DataDoc={this._resolvedDataDoc}
@@ -200,6 +210,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     ContainingCollectionView={this._textBox.props.ContainingCollectionView}
                     ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
                 />
-            </div>;
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index bb3791f1e..940ed6386 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,52 +1,55 @@
-import { action, computed, IReactionDisposer, observable } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable } from 'mobx';
+import { observer } from 'mobx-react';
 import * as ReactDOM from 'react-dom';
-import { DataSym, Doc, DocListCast, Field } from "../../../../fields/Doc";
-import { List } from "../../../../fields/List";
-import { listSpec } from "../../../../fields/Schema";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { ComputedField } from "../../../../fields/ScriptField";
-import { Cast, StrCast } from "../../../../fields/Types";
-import { DocServer } from "../../../DocServer";
-import { CollectionViewType } from "../../collections/CollectionView";
-import "./DashFieldView.scss";
-import { FormattedTextBox } from "./FormattedTextBox";
-import React = require("react");
-import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils";
-import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
-import { Tooltip } from "@material-ui/core";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { DataSym, Doc, DocListCast, Field } from '../../../../fields/Doc';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { ComputedField } from '../../../../fields/ScriptField';
+import { Cast, StrCast } from '../../../../fields/Types';
+import { DocServer } from '../../../DocServer';
+import { CollectionViewType } from '../../collections/CollectionView';
+import './DashFieldView.scss';
+import { FormattedTextBox } from './FormattedTextBox';
+import React = require('react');
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { Tooltip } from '@material-ui/core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 
 export class DashFieldView {
-    _fieldWrapper: HTMLDivElement; // container for label and value
+    dom: HTMLDivElement; // container for label and value
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
         const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey);
 
-        this._fieldWrapper = document.createElement("div");
-        this._fieldWrapper.style.width = node.attrs.width;
-        this._fieldWrapper.style.height = node.attrs.height;
-        this._fieldWrapper.style.fontWeight = "bold";
-        this._fieldWrapper.style.position = "relative";
-        this._fieldWrapper.style.display = "inline-block";
-        this._fieldWrapper.textContent = node.attrs.fieldKey.startsWith("#") ? node.attrs.fieldKey : node.attrs.fieldKey + " " + strVal;
-        this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
+        this.dom = document.createElement('div');
+        this.dom.style.width = node.attrs.width;
+        this.dom.style.height = node.attrs.height;
+        this.dom.style.fontWeight = 'bold';
+        this.dom.style.position = 'relative';
+        this.dom.style.display = 'inline-block';
+        this.dom.textContent = node.attrs.fieldKey.startsWith('#') ? node.attrs.fieldKey : node.attrs.fieldKey + ' ' + strVal;
+        this.dom.onkeypress = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onkeydown = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onkeyup = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onmousedown = function (e: any) {
+            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._fieldWrapper));
-        (this as any).dom = this._fieldWrapper;
+        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;
     }
-    destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
-    selectNode() { }
+    destroy() {
+        ReactDOM.unmountComponentAtNode(this.dom);
+    }
+    selectNode() {}
 }
 
 interface IDashFieldViewInternal {
@@ -72,8 +75,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         this._textBoxDoc = this.props.tbox.props.Document;
 
         if (this.props.docid) {
-            DocServer.GetRefField(this.props.docid).
-                then(action(async dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
+            DocServer.GetRefField(this.props.docid).then(action(async dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
         } else {
             this._dashDoc = this.props.tbox.rootDoc;
         }
@@ -82,11 +84,11 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         this._reactionDisposer?.();
     }
 
-    public static multiValueDelimeter = ";";
+    public static multiValueDelimeter = ';';
     public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
-        const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === "PARAMS" ? textBoxDoc[fieldKey] : "");
-        const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
-        return { boolVal: Cast(fval, "boolean", null), strVal: Field.toString(fval as Field) || "" };
+        const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === 'PARAMS' ? textBoxDoc[fieldKey] : '');
+        const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(':=') || dashVal === '' ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
+        return { boolVal: Cast(fval, 'boolean', null), strVal: Field.toString(fval as Field) || '' };
     }
 
     // set the display of the field's value (checkbox for booleans, span of text for strings)
@@ -95,30 +97,40 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             const { boolVal, strVal } = DashFieldViewInternal.fieldContent(this._textBoxDoc, this._dashDoc, this._fieldKey);
             // field value is a boolean, so use a checkbox or similar widget to display it
             if (boolVal === true || boolVal === false) {
-                return <input
-                    className="dashFieldView-fieldCheck"
-                    type="checkbox" checked={boolVal}
-                    onChange={e => {
-                        if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = e.target.checked;
-                        Doc.SetInPlace(this._dashDoc!, this._fieldKey, e.target.checked, true);
-                    }}
-                />;
-            }
-            else // field value is a string, so display it as an editable span
-            {
+                return (
+                    <input
+                        className="dashFieldView-fieldCheck"
+                        type="checkbox"
+                        checked={boolVal}
+                        onChange={e => {
+                            if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = e.target.checked;
+                            Doc.SetInPlace(this._dashDoc!, this._fieldKey, e.target.checked, true);
+                        }}
+                    />
+                );
+            } // field value is a string, so display it as an editable span
+            else {
                 // bcz: this is unfortunate, but since this React component is nested within a non-React text box (prosemirror), we can't
                 // use React events.  Essentially, React events occur after native events have been processed, so corresponding React events
                 // will never fire because Prosemirror has handled the native events.  So we add listeners for native events here.
-                return <span className="dashFieldView-fieldSpan" contentEditable={true}
-                    style={{ display: strVal.length < 2 ? "inline-block" : undefined }}
-                    suppressContentEditableWarning={true} defaultValue={strVal}
-                    ref={r => {
-                        r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
-                        r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
-                        r?.addEventListener("pointerdown", action(e => e.stopPropagation()));
-                    }} >
-                    {strVal}
-                </span>;
+                return (
+                    <span
+                        className="dashFieldView-fieldSpan"
+                        contentEditable={true}
+                        style={{ display: strVal.length < 2 ? 'inline-block' : undefined }}
+                        suppressContentEditableWarning={true}
+                        defaultValue={strVal}
+                        ref={r => {
+                            r?.addEventListener('keydown', e => this.fieldSpanKeyDown(e, r));
+                            r?.addEventListener('blur', e => r && this.updateText(r.textContent!, false));
+                            r?.addEventListener(
+                                'pointerdown',
+                                action(e => e.stopPropagation())
+                            );
+                        }}>
+                        {strVal}
+                    </span>
+                );
             }
         }
     }
@@ -126,11 +138,13 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     // we need to handle all key events on the input span or else they will propagate to prosemirror.
     @action
     fieldSpanKeyDown = (e: KeyboardEvent, span: HTMLSpanElement) => {
-        if (e.key === "Enter") {  // handle the enter key by "submitting" the current text to Dash's database. 
+        if (e.key === 'Enter') {
+            // handle the enter key by "submitting" the current text to Dash's database.
             this.updateText(span.textContent!, true);
-            e.preventDefault();// prevent default to avoid a newline from being generated and wiping out this field view
+            e.preventDefault(); // prevent default to avoid a newline from being generated and wiping out this field view
         }
-        if (e.key === "a" && (e.ctrlKey || e.metaKey)) { // handle ctrl-A to select all the text within the span
+        if (e.key === 'a' && (e.ctrlKey || e.metaKey)) {
+            // handle ctrl-A to select all the text within the span
             if (window.getSelection) {
                 const range = document.createRange();
                 range.selectNodeContents(span);
@@ -139,44 +153,44 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             }
             e.preventDefault(); //prevent default so that all the text in the prosemirror text box isn't selected
         }
-        e.stopPropagation();  // we need to handle all events or else they will propagate to prosemirror.
-    }
+        e.stopPropagation(); // we need to handle all events or else they will propagate to prosemirror.
+    };
 
     @action
     updateText = (nodeText: string, forceMatch: boolean) => {
         if (nodeText) {
-            const newText = nodeText.startsWith(":=") || nodeText.startsWith("=:=") ? ":=-computed-" : nodeText;
+            const newText = nodeText.startsWith(':=') || nodeText.startsWith('=:=') ? ':=-computed-' : nodeText;
 
             // 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.
             DocServer.GetRefField(this._fieldKey).then(options => {
-                let modText = "";
-                (options instanceof Doc) && DocListCast(options.data).forEach(opt => (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title)));
+                let modText = '';
+                options instanceof Doc && DocListCast(options.data).forEach(opt => (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title)));
                 if (modText) {
                     //  elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText;
                     Doc.SetInPlace(this._dashDoc!, this._fieldKey, modText, true);
                 } // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
-                else if (nodeText.startsWith(":=")) {
+                else if (nodeText.startsWith(':=')) {
                     this._dashDoc![DataSym][this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
-                } else if (nodeText.startsWith("=:=")) {
+                } else if (nodeText.startsWith('=:=')) {
                     Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3));
                 } else {
                     if (Number(newText).toString() === newText) {
-                        if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
+                        if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
                         Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
                     } else {
                         const splits = newText.split(DashFieldViewInternal.multiValueDelimeter);
-                        if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
+                        if (this._fieldKey !== 'PARAMS' || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
                             const strVal = splits.length > 1 ? new List<string>(splits) : newText;
-                            if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
+                            if (this._fieldKey.startsWith('_')) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
                             Doc.SetInPlace(this._dashDoc!, this._fieldKey, strVal, true);
                         }
                     }
                 }
             });
         }
-    }
+    };
 
     createPivotForField = (e: React.MouseEvent) => {
         let container = this.props.tbox.props.ContainingCollectionView;
@@ -190,36 +204,39 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
             if (!list) {
                 alias._columnHeaders = list = new List<SchemaHeaderField>();
             }
-            list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
-            list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
-            alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey;
-            this.props.tbox.props.addDocTab(alias, "add:right");
+            list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb'));
+            list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb'));
+            alias._pivotField = this._fieldKey.startsWith('#') ? '#' : this._fieldKey;
+            this.props.tbox.props.addDocTab(alias, 'add:right');
         }
-    }
-
+    };
 
     // clicking on the label creates a pivot view collection of all documents
     // in the same collection.  The pivot field is the fieldKey of this label
     onPointerDownLabelSpan = (e: any) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, (e) => {
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, e => {
             DashFieldViewMenu.createFieldView = this.createPivotForField;
             DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16);
         });
-    }
+    };
 
     render() {
-        return <div className="dashFieldView" style={{
-            width: this.props.width,
-            height: this.props.height,
-        }}>
-            {this.props.hideKey ? (null) :
-                <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
-                    {this._fieldKey}
-                </span>}
-
-            {this.props.fieldKey.startsWith("#") ? (null) : this.fieldValueContent}
+        return (
+            <div
+                className="dashFieldView"
+                style={{
+                    width: this.props.width,
+                    height: this.props.height,
+                }}>
+                {this.props.hideKey ? null : (
+                    <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
+                        {this._fieldKey}
+                    </span>
+                )}
 
-        </div >;
+                {this.props.fieldKey.startsWith('#') ? null : this.fieldValueContent}
+            </div>
+        );
     }
 }
 @observer
@@ -234,19 +251,19 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
     showFields = (e: React.MouseEvent) => {
         DashFieldViewMenu.createFieldView(e);
         DashFieldViewMenu.Instance.fadeOut(true);
-    }
+    };
 
     public show = (x: number, y: number) => {
         this.jumpTo(x, y, true);
         const hideMenu = () => {
             this.fadeOut(true);
-            document.removeEventListener("pointerdown", hideMenu);
+            document.removeEventListener('pointerdown', hideMenu);
         };
-        document.addEventListener("pointerdown", hideMenu);
-    }
+        document.addEventListener('pointerdown', hideMenu);
+    };
     render() {
         const buttons = [
-            <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
+            <Tooltip key="trash" title={<div className="dash-tooltip">{'Remove Link Anchor'}</div>}>
                 <button className="antimodeMenu-button" onPointerDown={this.showFields}>
                     <FontAwesomeIcon icon="eye" size="lg" />
                 </button>
@@ -255,4 +272,4 @@ export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
 
         return this.getElement(buttons);
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 508500ab6..98d611ca6 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -1,38 +1,38 @@
-import EquationEditor from "equation-editor-react";
-import { IReactionDisposer } from "mobx";
-import { observer } from "mobx-react";
+import EquationEditor from 'equation-editor-react';
+import { IReactionDisposer } from 'mobx';
+import { observer } from 'mobx-react';
 import * as ReactDOM from 'react-dom';
-import { Doc } from "../../../../fields/Doc";
-import { StrCast } from "../../../../fields/Types";
-import "./DashFieldView.scss";
-import { FormattedTextBox } from "./FormattedTextBox";
-import React = require("react");
+import { Doc } from '../../../../fields/Doc';
+import { StrCast } from '../../../../fields/Types';
+import './DashFieldView.scss';
+import { FormattedTextBox } from './FormattedTextBox';
+import React = require('react');
 
 export class EquationView {
-    _fieldWrapper: HTMLDivElement; // container for label and value
+    dom: HTMLDivElement; // container for label and value
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
-        this._fieldWrapper = document.createElement("div");
-        this._fieldWrapper.style.width = node.attrs.width;
-        this._fieldWrapper.style.height = node.attrs.height;
-        this._fieldWrapper.style.position = "relative";
-        this._fieldWrapper.style.display = "inline-block";
-        this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
+        this.dom = document.createElement('div');
+        this.dom.style.width = node.attrs.width;
+        this.dom.style.height = node.attrs.height;
+        this.dom.style.position = 'relative';
+        this.dom.style.display = 'inline-block';
+        this.dom.onmousedown = function (e: any) {
+            e.stopPropagation();
+        };
 
-        ReactDOM.render(<EquationViewInternal
-            fieldKey={node.attrs.fieldKey}
-            width={node.attrs.width}
-            height={node.attrs.height}
-            setEditor={this.setEditor}
-            tbox={tbox}
-        />, this._fieldWrapper);
-        (this as any).dom = this._fieldWrapper;
+        ReactDOM.render(<EquationViewInternal fieldKey={node.attrs.fieldKey} width={node.attrs.width} height={node.attrs.height} setEditor={this.setEditor} tbox={tbox} />, this.dom);
+        (this as any).dom = this.dom;
     }
     _editor: EquationEditor | undefined;
-    setEditor = (editor?: EquationEditor) => this._editor = editor;
-    destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
-    selectNode() { this._editor?.mathField.focus(); }
-    deselectNode() { }
+    setEditor = (editor?: EquationEditor) => (this._editor = editor);
+    destroy() {
+        ReactDOM.unmountComponentAtNode(this.dom);
+    }
+    selectNode() {
+        this._editor?.mathField.focus();
+    }
+    deselectNode() {}
 }
 
 interface IEquationViewInternal {
@@ -56,24 +56,33 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
         this._textBoxDoc = this.props.tbox.props.Document;
     }
 
-    componentWillUnmount() { this._reactionDisposer?.(); }
-    componentDidMount() { this.props.setEditor(this._ref.current ?? undefined); }
+    componentWillUnmount() {
+        this._reactionDisposer?.();
+    }
+    componentDidMount() {
+        this.props.setEditor(this._ref.current ?? undefined);
+    }
 
     render() {
-        return <div className="equationView" style={{
-            position: "relative",
-            display: "inline-block",
-            width: this.props.width,
-            height: this.props.height,
-            bottom: 3,
-        }}>
-            <EquationEditor ref={this._ref}
-                value={StrCast(this._textBoxDoc[this._fieldKey], "y=")}
-                onChange={str => this._textBoxDoc[this._fieldKey] = str}
-                autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
-                autoOperatorNames="sin cos tan"
-                spaceBehavesLikeTab={true}
-            />
-        </div >;
+        return (
+            <div
+                className="equationView"
+                style={{
+                    position: 'relative',
+                    display: 'inline-block',
+                    width: this.props.width,
+                    height: this.props.height,
+                    bottom: 3,
+                }}>
+                <EquationEditor
+                    ref={this._ref}
+                    value={StrCast(this._textBoxDoc[this._fieldKey], 'y=')}
+                    onChange={str => (this._textBoxDoc[this._fieldKey] = str)}
+                    autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
+                    autoOperatorNames="sin cos tan"
+                    spaceBehavesLikeTab={true}
+                />
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 8e1698eba..85aa51055 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,89 +1,90 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { isEqual } from "lodash";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { baseKeymap, selectAll } from "prosemirror-commands";
-import { history } from "prosemirror-history";
+import { isEqual } from 'lodash';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { baseKeymap, selectAll } from 'prosemirror-commands';
+import { history } from 'prosemirror-history';
 import { inputRules } from 'prosemirror-inputrules';
-import { keymap } from "prosemirror-keymap";
-import { Fragment, Mark, Node, Slice } from "prosemirror-model";
-import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state";
-import { EditorView } from "prosemirror-view";
+import { keymap } from 'prosemirror-keymap';
+import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
+import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
+import { EditorView } from 'prosemirror-view';
 import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from "../../../../fields/Doc";
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
-import { RichTextField } from "../../../../fields/RichTextField";
+import { RichTextField } from '../../../../fields/RichTextField';
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
 import { ComputedField } from '../../../../fields/ScriptField';
-import { Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
+import { Cast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
-import { DocServer } from "../../../DocServer";
+import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 import { DictationManager } from '../../../util/DictationManager';
 import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager } from "../../../util/DragManager";
+import { DragManager } from '../../../util/DragManager';
 import { MakeTemplate } from '../../../util/DropConverter';
 import { LinkManager } from '../../../util/LinkManager';
-import { SelectionManager } from "../../../util/SelectionManager";
+import { SelectionManager } from '../../../util/SelectionManager';
 import { SnappingManager } from '../../../util/SnappingManager';
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
 import { CollectionStackingView } from '../../collections/CollectionStackingView';
 import { ContextMenu } from '../../ContextMenu';
 import { ContextMenuProps } from '../../ContextMenuItem';
-import { ViewBoxAnnotatableComponent } from "../../DocComponent";
+import { ViewBoxAnnotatableComponent } from '../../DocComponent';
 import { DocumentButtonBar } from '../../DocumentButtonBar';
 import { Colors } from '../../global/globalEnums';
 import { LightboxView } from '../../LightboxView';
 import { AnchorMenu } from '../../pdf/AnchorMenu';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { StyleProp } from '../../StyleProvider';
-import { FieldView, FieldViewProps } from "../FieldView";
+import { FieldView, FieldViewProps } from '../FieldView';
 import { LinkDocPreview } from '../LinkDocPreview';
-import { DashDocCommentView } from "./DashDocCommentView";
-import { DashDocView } from "./DashDocView";
-import { DashFieldView } from "./DashFieldView";
-import { EquationView } from "./EquationView";
-import { FootnoteView } from "./FootnoteView";
-import "./FormattedTextBox.scss";
+import { DashDocCommentView } from './DashDocCommentView';
+import { DashDocView } from './DashDocView';
+import { DashFieldView } from './DashFieldView';
+import { EquationView } from './EquationView';
+import { FootnoteView } from './FootnoteView';
+import './FormattedTextBox.scss';
 import { findLinkMark, FormattedTextBoxComment } from './FormattedTextBoxComment';
-import { OrderedListView } from "./OrderedListView";
-import { buildKeymap, updateBullets } from "./ProsemirrorExampleTransfer";
-import { removeMarkWithAttrs } from "./prosemirrorPatches";
+import { buildKeymap, updateBullets } from './ProsemirrorExampleTransfer';
+import { removeMarkWithAttrs } from './prosemirrorPatches';
 import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
-import { RichTextRules } from "./RichTextRules";
-import { schema } from "./schema_rts";
-import { SummaryView } from "./SummaryView";
-import applyDevTools = require("prosemirror-dev-tools");
-import React = require("react");
-const translateGoogleApi = require("translate-google-api");
+import { RichTextRules } from './RichTextRules';
+import { schema } from './schema_rts';
+import { SummaryView } from './SummaryView';
+import applyDevTools = require('prosemirror-dev-tools');
+import React = require('react');
+const translateGoogleApi = require('translate-google-api');
 
 export interface FormattedTextBoxProps {
-    makeLink?: () => Opt<Doc>;  // bcz: hack: notifies the text document when the container has made a link.  allows the text doc to react and setup a hyeprlink for any selected text
-    xPadding?: number;   // used to override document's settings for xMargin --- see CollectionCarouselView
+    makeLink?: () => Opt<Doc>; // bcz: hack: notifies the text document when the container has made a link.  allows the text doc to react and setup a hyeprlink for any selected text
+    xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
     yPadding?: number;
     noSidebar?: boolean;
     dontScale?: boolean;
     dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field)
 }
-export const GoogleRef = "googleDocId";
+export const GoogleRef = 'googleDocId';
 
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
 
 @observer
-export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps)>() {
-    public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); }
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps & FormattedTextBoxProps>() {
+    public static LayoutString(fieldStr: string) {
+        return FieldView.LayoutString(FormattedTextBox, fieldStr);
+    }
     public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
     public static Instance: FormattedTextBox;
     public static LiveTextUndo: UndoManager.Batch | undefined;
-    static _globalHighlights: string[] = ["Audio Tags", "Text from Others", "Todo Items", "Important Items", "Disagree Items", "Ignore Items"];
+    static _globalHighlights: string[] = ['Audio Tags', 'Text from Others', 'Todo Items', 'Important Items', 'Disagree Items', 'Ignore Items'];
     static _highlightStyleSheet: any = addStyleSheet();
     static _bulletStyleSheet: any = addStyleSheet();
     static _userStyleSheet: any = addStyleSheet();
@@ -93,7 +94,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
     private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _editorView: Opt<EditorView>;
-    private _applyingChange: string = "";
+    private _applyingChange: string = '';
     private _searchIndex = 0;
     private _lastTimedMark: Mark | undefined = undefined;
     private _cachedLinks: Doc[] = [];
@@ -102,7 +103,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _recordingStart: number = 0;
     private _ignoreScroll = false;
-    private _lastText = "";
+    private _lastText = '';
     private _focusSpeed: Opt<number>;
     private _keymap: any = undefined;
     private _rules: RichTextRules | undefined;
@@ -113,21 +114,45 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     private _downY = 0;
     private _break = true;
     public ProseRef?: HTMLDivElement;
-    public get EditorView() { return this._editorView; }
-    public get SidebarKey() { return this.fieldKey + "-sidebar"; }
-    @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); }
-
-    @computed get sidebarWidthPercent() { return this._showSidebar ? "20%" : StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
-    @computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
-    @computed get autoHeight() { return (this.props.forceAutoHeight || this.layoutDoc._autoHeight) && !this.props.ignoreAutoHeight; }
-    @computed get textHeight() { return NumCast(this.rootDoc[this.fieldKey + "-height"]); }
-    @computed get scrollHeight() { return NumCast(this.rootDoc[this.fieldKey + "-scrollHeight"]); }
-    @computed get sidebarHeight() { return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + "-height"]); }
-    @computed get titleHeight() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; }
-    @computed get autoHeightMargins() { return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins); }
-    @computed get _recording() { return this.dataDoc?.mediaState === "recording"; }
+    public get EditorView() {
+        return this._editorView;
+    }
+    public get SidebarKey() {
+        return this.fieldKey + '-sidebar';
+    }
+    @computed get allSidebarDocs() {
+        return DocListCast(this.dataDoc[this.SidebarKey]);
+    }
+
+    @computed get sidebarWidthPercent() {
+        return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    }
+    @computed get sidebarColor() {
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
+    }
+    @computed get autoHeight() {
+        return (this.props.forceAutoHeight || this.layoutDoc._autoHeight) && !this.props.ignoreAutoHeight;
+    }
+    @computed get textHeight() {
+        return NumCast(this.rootDoc[this.fieldKey + '-height']);
+    }
+    @computed get scrollHeight() {
+        return NumCast(this.rootDoc[this.fieldKey + '-scrollHeight']);
+    }
+    @computed get sidebarHeight() {
+        return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '-height']);
+    }
+    @computed get titleHeight() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
+    }
+    @computed get autoHeightMargins() {
+        return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins);
+    }
+    @computed get _recording() {
+        return this.dataDoc?.mediaState === 'recording';
+    }
     set _recording(value) {
-        !this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? "recording" : undefined);
+        !this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? 'recording' : undefined);
     }
     @computed get config() {
         this._keymap = buildKeymap(schema, this.props);
@@ -140,28 +165,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 history(),
                 keymap(this._keymap),
                 keymap(baseKeymap),
-                new Plugin({ props: { attributes: { class: "ProseMirror-example-setup-style" } } }),
-                new Plugin({ view(editorView) { return new FormattedTextBoxComment(editorView); } })
-            ]
+                new Plugin({ props: { attributes: { class: 'ProseMirror-example-setup-style' } } }),
+                new Plugin({
+                    view(editorView) {
+                        return new FormattedTextBoxComment(editorView);
+                    },
+                }),
+            ],
         };
     }
 
     public static PasteOnLoad: ClipboardEvent | undefined;
-    public static SelectOnLoad = "";
+    public static SelectOnLoad = '';
     public static DontSelectInitialText = false; // whether initial text should be selected or not
-    public static SelectOnLoadChar = "";
-    public static IsFragment(html: string) { return html.indexOf("data-pm-slice") !== -1; }
+    public static SelectOnLoadChar = '';
+    public static IsFragment(html: string) {
+        return html.indexOf('data-pm-slice') !== -1;
+    }
     public static GetHref(html: string): string {
         const parser = new DOMParser();
         const parsedHtml = parser.parseFromString(html, 'text/html');
-        if (parsedHtml.body.childNodes.length === 1 && parsedHtml.body.childNodes[0].childNodes.length === 1 &&
-            (parsedHtml.body.childNodes[0].childNodes[0] as any).href) {
+        if (parsedHtml.body.childNodes.length === 1 && parsedHtml.body.childNodes[0].childNodes.length === 1 && (parsedHtml.body.childNodes[0].childNodes[0] as any).href) {
             return (parsedHtml.body.childNodes[0].childNodes[0] as any).href;
         }
-        return "";
+        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) {
@@ -172,7 +202,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     // removes all hyperlink anchors for the removed linkDoc
-    // TODO: bcz: Argh... if a section of text has multiple anchors, this should just remove the intended one. 
+    // TODO: bcz: Argh... if a section of text has multiple anchors, this should just remove the intended one.
     // but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing.
     public RemoveLinkFromDoc(linkDoc?: Doc) {
         this.unhighlightSearchTerms();
@@ -195,9 +225,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         }
     }
-    // removes all the specified link references from the selection. 
+    // removes all the specified link references from the selection.
     // NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references.
-    public RemoveAnchorFromSelection(allAnchors: { href: string, title: string, linkId: string, targetId: string }[]) {
+    public RemoveAnchorFromSelection(allAnchors: { href: string; title: string; linkId: string; targetId: string }[]) {
         const state = this._editorView?.state;
         if (state && this._editorView) {
             this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors }));
@@ -205,11 +235,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
 
-    getAnchor = () => this.makeLinkAnchor(undefined, "add:right", undefined, "Anchored Selection");
+    getAnchor = () => this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection');
 
     @action
     setupAnchorMenu = () => {
-        AnchorMenu.Instance.Status = "marquee";
+        AnchorMenu.Instance.Status = 'marquee';
 
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => {
             !this.layoutDoc.showSidebar && this.toggleSidebar();
@@ -222,14 +252,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         AnchorMenu.Instance.onMakeAnchor = this.getAnchor;
         AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
         /**
-         * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation.  
+         * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation.
          * It also initiates a Drag/Drop interaction to place the text annotation.
          */
         AnchorMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => {
             e.preventDefault();
             e.stopPropagation();
             const targetCreator = (annotationOn?: Doc) => {
-                const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn);
+                const target = CurrentUserUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn);
                 FormattedTextBox.SelectOnLoad = target[Id];
                 return target;
             };
@@ -238,55 +268,56 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         });
         const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
         this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
-    }
+    };
 
     dispatchTransaction = (tx: Transaction) => {
         if (this._editorView) {
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
 
-            const curText = state.doc.textBetween(0, state.doc.content.size, " \n");
-            const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined;               // the actual text in the text box
-            const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null);              // the default text inherited from a prototype
+            const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
+            const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined; // the actual text in the text box
+            const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
             const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
             const json = JSON.stringify(state.toJSON());
             const effectiveAcl = GetEffectiveAcl(this.dataDoc);
 
-            const removeSelection = (json: string | undefined) => json?.indexOf("\"storedMarks\"") === -1 ?
-                json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\"");
+            const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
 
             if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) {
                 const accumTags = [] as string[];
                 state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
-                    if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith("#")) {
+                    if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith('#')) {
                         accumTags.push(node.attrs.fieldKey);
                     }
                 });
-                const curTags = Object.keys(this.dataDoc).filter(key => key.startsWith("#"));
+                const curTags = Object.keys(this.dataDoc).filter(key => key.startsWith('#'));
                 const added = accumTags.filter(tag => !curTags.includes(tag));
                 const removed = curTags.filter(tag => !accumTags.includes(tag));
-                removed.forEach(r => this.dataDoc[r] = undefined);
-                added.forEach(a => this.dataDoc[a] = a);
+                removed.forEach(r => (this.dataDoc[r] = undefined));
+                added.forEach(a => (this.dataDoc[a] = a));
 
                 let unchanged = true;
                 if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) {
                     this._applyingChange = this.fieldKey;
-                    (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())));
-                    if ((!curTemp && !curProto) || curText || json.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
+                    curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text && (this.dataDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now())));
+                    if ((!curTemp && !curProto) || curText || json.includes('dash')) {
+                        // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
                         if (removeSelection(json) !== removeSelection(curLayout?.Data)) {
                             this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText);
-                            this.dataDoc[this.props.fieldKey + "-noTemplate"] = true;//(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+                            this.dataDoc[this.props.fieldKey + '-noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
                             ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
                             unchanged = false;
                         }
-                    } else { // if we've deleted all the text in a note driven by a template, then restore the template data
+                    } else {
+                        // if we've deleted all the text in a note driven by a template, then restore the template data
                         this.dataDoc[this.props.fieldKey] = undefined;
                         this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data)));
-                        this.dataDoc[this.props.fieldKey + "-noTemplate"] = undefined; // mark the data field as not being split from any template it might have
+                        this.dataDoc[this.props.fieldKey + '-noTemplate'] = undefined; // mark the data field as not being split from any template it might have
                         ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
                         unchanged = false;
                     }
-                    this._applyingChange = "";
+                    this._applyingChange = '';
                     if (!unchanged) {
                         this.updateTitle();
                         this.tryUpdateScrollHeight();
@@ -304,16 +335,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 AnchorMenu.Instance.fadeOut(true);
             }
         }
-    }
+    };
 
-    // for inserting timestamps 
+    // for inserting timestamps
     insertTime = () => {
         let linkTime;
         let linkAnchor;
         let link;
         DocListCast(this.dataDoc.links).forEach((l, i) => {
-            const anchor = (l.anchor1 as Doc).annotationOn ? l.anchor1 as Doc : (l.anchor2 as Doc).annotationOn ? (l.anchor2 as Doc) : undefined;
-            if (anchor && (anchor.annotationOn as Doc).mediaState === "recording") {
+            const anchor = (l.anchor1 as Doc).annotationOn ? (l.anchor1 as Doc) : (l.anchor2 as Doc).annotationOn ? (l.anchor2 as Doc) : undefined;
+            if (anchor && (anchor.annotationOn as Doc).mediaState === 'recording') {
                 linkTime = NumCast(anchor._timecodeToShow /* audioStart */);
                 linkAnchor = anchor;
                 link = l;
@@ -344,7 +375,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 this._editorView.dispatch(replaced.setSelection(new TextSelection(replaced.doc.resolve(from + 1))));
             }
         }
-    }
+    };
 
     autoLink = () => {
         if (this._editorView?.state.doc.textContent) {
@@ -355,38 +386,42 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             var tr = this._editorView.state.tr as any;
             const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
             tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
-            DocListCast(CurrentUserUtils.MyPublishedDocs.data).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks));
+            DocListCast(CurrentUserUtils.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
             tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
             oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
         }
-    }
+    };
 
     updateTitle = () => {
         const title = StrCast(this.dataDoc.title);
-        if (!this.props.dontRegisterView &&  // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
-            (title.startsWith("-") || title.startsWith("@")) && this._editorView && !this.dataDoc["title-custom"] &&
-            (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === "text")) {
+        if (
+            !this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
+            (title.startsWith('-') || title.startsWith('@')) &&
+            this._editorView &&
+            !this.dataDoc['title-custom'] &&
+            (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === 'text')
+        ) {
             let node = this._editorView.state.doc;
-            while (node.firstChild && node.firstChild.type.name !== "text") node = node.firstChild;
+            while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
             const str = node.textContent;
-            const prefix = str.startsWith("@") ? "" : "-";
+            const prefix = str.startsWith('@') ? '' : '-';
 
             const cfield = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc.title));
             if (!(cfield instanceof ComputedField)) {
-                this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : "");
-                if (str.startsWith("@") && str.length > 1) {
+                this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '');
+                if (str.startsWith('@') && str.length > 1) {
                     Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, this.rootDoc);
                 }
             }
         }
-    }
+    };
 
     // creates links between terms in a document and published documents (myPublishedDocs) that have titles starting with an '@'
     hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set<Doc>) => {
         const editorView = this._editorView;
         if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) {
-            const autoLinkTerm = StrCast(target.title).replace(/^@/, ""); 
+            const autoLinkTerm = StrCast(target.title).replace(/^@/, '');
             const flattened1 = this.findInNode(editorView, editorView.state.doc, autoLinkTerm);
             var alink: Doc | undefined;
             flattened1.forEach((flat, i) => {
@@ -397,14 +432,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 tr = tr.addMark(sel.from, sel.to, splitter);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
                     if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
-                        alink = alink ?? (DocListCast(this.Document.links).find(link =>
-                            Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) &&
-                            Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target },
-                                LinkManager.AutoKeywords)!);
+                        alink =
+                            alink ??
+                            (DocListCast(this.Document.links).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) ||
+                                DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!);
                         newAutoLinks.add(alink);
-                        const allAnchors = [{ href: Doc.localServerPath(target), title: "a link", anchorId: this.props.Document[Id] }];
+                        const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
                         allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
-                        const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: "auto term", location: "add:right" });
+                        const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term', location: 'add:right' });
                         tr = tr.addMark(pos, pos + node.nodeSize, link);
                     }
                 });
@@ -412,13 +447,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             });
         }
         return tr;
-    }
+    };
     @action
     search = (searchString: string, bwd?: boolean, clear: boolean = false) => {
         if (clear) this.unhighlightSearchTerms();
         else this.highlightSearchTerms([searchString], bwd!);
         return true;
-    }
+    };
     highlightSearchTerms = (terms: string[], backward: boolean) => {
         if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
             const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
@@ -432,21 +467,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             if (backward === true) {
                 if (this._searchIndex > 1) {
                     this._searchIndex += -2;
-                }
-                else if (this._searchIndex === 1) {
+                } else if (this._searchIndex === 1) {
                     this._searchIndex = length - 1;
-                }
-                else if (this._searchIndex === 0 && length !== 1) {
+                } else if (this._searchIndex === 0 && length !== 1) {
                     this._searchIndex = length - 2;
                 }
-
             }
 
             const lastSel = Math.min(flattened.length - 1, this._searchIndex);
-            flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
+            flattened.forEach((h: TextSelection, ind: number) => (tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark)));
             flattened[lastSel] && this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
         }
-    }
+    };
 
     unhighlightSearchTerms = () => {
         if (window.screen.width < 600) null;
@@ -455,20 +487,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
             const end = this._editorView.state.doc.nodeSize - 2;
             this._editorView.dispatch(this._editorView.state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
-
         }
         if (FormattedTextBox.PasteOnLoad) {
-            const pdfDocId = FormattedTextBox.PasteOnLoad.clipboardData?.getData("dash/pdfOrigin");
-            const pdfRegionId = FormattedTextBox.PasteOnLoad.clipboardData?.getData("dash/pdfRegion");
+            const pdfDocId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfOrigin');
+            const pdfRegionId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfRegion');
             FormattedTextBox.PasteOnLoad = undefined;
             setTimeout(() => pdfDocId && pdfRegionId && this.addPdfReference(pdfDocId, pdfRegionId, undefined), 10);
         }
-    }
+    };
     adoptAnnotation = (start: number, end: number, mark: Mark) => {
         const view = this._editorView!;
         const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: Doc.CurrentUserEmail });
         view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark));
-    }
+    };
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer?.();
         this.ProseRef = ele;
@@ -476,8 +507,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this.setupEditor(this.config, this.props.fieldKey);
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc);
         }
-        // if (this.autoHeight) this.tryUpdateScrollHeight(); 
-    }
+        // if (this.autoHeight) this.tryUpdateScrollHeight();
+    };
 
     @undoBatch
     @action
@@ -499,18 +530,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 const node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](),
                     height: target[HeightSym](),
-                    title: "dashDoc",
+                    title: 'dashDoc',
                     docid: target[Id],
-                    float: "unset"
+                    float: 'unset',
                 });
                 const view = this._editorView!;
                 view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
                 e.stopPropagation();
             } // otherwise, fall through to outer collection to handle drop
         }
-    }
+    };
 
-    getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null {
+    getNodeEndpoints(context: Node, node: Node): { from: number; to: number } | null {
         let offset = 0;
 
         if (context === node) return { from: offset, to: offset + node.nodeSize };
@@ -521,8 +552,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 const result = this.getNodeEndpoints((context.content as any).content[i], node);
                 if (result) {
                     return {
-                        from: result.from + offset + (context.type.name === "doc" ? 0 : 1),
-                        to: result.to + offset + (context.type.name === "doc" ? 0 : 1)
+                        from: result.from + offset + (context.type.name === 'doc' ? 0 : 1),
+                        to: result.to + offset + (context.type.name === 'doc' ? 0 : 1),
                     };
                 }
                 offset += (context.content as any).content[i].nodeSize;
@@ -538,9 +569,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         let ret: TextSelection[] = [];
 
         if (node.isTextblock) {
-            let index = 0, foundAt;
+            let index = 0,
+                foundAt;
             const ep = this.getNodeEndpoints(pm.state.doc, node);
-            const regexp = new RegExp(find.replace("*", ""), "i");
+            const regexp = new RegExp(find.replace('*', ''), 'i');
             if (regexp) {
                 while (ep && (foundAt = node.textContent.slice(index).search(regexp)) > -1) {
                     const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
@@ -549,7 +581,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             }
         } else {
-            node.content.forEach((child, i) => ret = ret.concat(this.findInNode(pm, child, find)));
+            node.content.forEach((child, i) => (ret = ret.concat(this.findInNode(pm, child, find))));
         }
         return ret;
     }
@@ -557,162 +589,178 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     updateHighlights = () => {
         const highlights = FormattedTextBox._globalHighlights;
         clearStyleSheetRules(FormattedTextBox._userStyleSheet);
-        if (highlights.indexOf("Audio Tags") === -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "audiotag", { display: "none" }, "");
+        if (highlights.indexOf('Audio Tags') === -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'audiotag', { display: 'none' }, '');
         }
-        if (highlights.indexOf("Text from Others") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-remote", { background: "yellow" });
+        if (highlights.indexOf('Text from Others') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-remote', { background: 'yellow' });
         }
-        if (highlights.indexOf("My Text") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { background: "moccasin" });
+        if (highlights.indexOf('My Text') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + Doc.CurrentUserEmail.replace('.', '').replace('@', ''), { background: 'moccasin' });
         }
-        if (highlights.indexOf("Todo Items") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "todo", { outline: "black solid 1px" });
+        if (highlights.indexOf('Todo Items') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-' + 'todo', { outline: 'black solid 1px' });
         }
-        if (highlights.indexOf("Important Items") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "important", { "font-size": "larger" });
+        if (highlights.indexOf('Important Items') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-' + 'important', { 'font-size': 'larger' });
         }
-        if (highlights.indexOf("Bold Text") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, ".formattedTextBox-inner-selected .ProseMirror strong > span", { "font-size": "large" }, "");
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, ".formattedTextBox-inner-selected .ProseMirror :not(strong > span)", { "font-size": "0px" }, "");
+        if (highlights.indexOf('Bold Text') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, '.formattedTextBox-inner-selected .ProseMirror strong > span', { 'font-size': 'large' }, '');
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, '.formattedTextBox-inner-selected .ProseMirror :not(strong > span)', { 'font-size': '0px' }, '');
         }
-        if (highlights.indexOf("Disagree Items") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "disagree", { "text-decoration": "line-through" });
+        if (highlights.indexOf('Disagree Items') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-' + 'disagree', { 'text-decoration': 'line-through' });
         }
-        if (highlights.indexOf("Ignore Items") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "ignore", { "font-size": "1" });
+        if (highlights.indexOf('Ignore Items') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-' + 'ignore', { 'font-size': '1' });
         }
-        if (highlights.indexOf("By Recent Minute") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
+        if (highlights.indexOf('By Recent Minute') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + Doc.CurrentUserEmail.replace('.', '').replace('@', ''), { opacity: '0.1' });
             const min = Math.round(Date.now() / 1000 / 60);
-            numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() }));
+            numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-min-' + (min - i), { opacity: ((10 - i - 1) / 10).toString() }));
             setTimeout(this.updateHighlights);
         }
-        if (highlights.indexOf("By Recent Hour") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
+        if (highlights.indexOf('By Recent Hour') !== -1) {
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + Doc.CurrentUserEmail.replace('.', '').replace('@', ''), { opacity: '0.1' });
             const hr = Math.round(Date.now() / 1000 / 60 / 60);
-            numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-hr-" + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
+            numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
         }
-    }
+    };
 
     @observable _showSidebar = false;
-    @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
+    @computed get SidebarShown() {
+        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+    }
 
     @action
     toggleSidebar = (preview: boolean = false) => {
         const prevWidth = this.sidebarWidth();
         if (preview) this._showSidebar = true;
-        else this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%";
+        else this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
 
         this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
-    }
+    };
     sidebarDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false);
-    }
+    };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this._ref.current!.getBoundingClientRect();
-        this.layoutDoc._sidebarWidthPercent = "" + 100 * Math.max(0, (1 - (e.clientX - bounds.left) / bounds.width)) + "%";
-        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== "0%";
+        this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
         e.preventDefault();
         return false;
-    }
+    };
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
 
         const changeItems: ContextMenuProps[] = [];
         changeItems.push({
-            description: "plain", event: undoBatch(() => {
+            description: 'plain',
+            event: undoBatch(() => {
                 Doc.setNativeView(this.rootDoc);
                 this.layoutDoc.autoHeightMargins = undefined;
-            }), icon: "eye"
+            }),
+            icon: 'eye',
         });
         changeItems.push({
-            description: "metadata", event: undoBatch(() => {
+            description: 'metadata',
+            event: undoBatch(() => {
                 this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
-                this.rootDoc.layoutKey = "layout_meta";
-                setTimeout(() => this.rootDoc._headerHeight = this.rootDoc._autoHeightMargins = 50, 50);
-            }), icon: "eye"
+                this.rootDoc.layoutKey = 'layout_meta';
+                setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._autoHeightMargins = 50), 50);
+            }),
+            icon: 'eye',
         });
-        const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
+        const noteTypesDoc = Cast(Doc.UserDoc()['template-notes'], Doc, null);
         DocListCast(noteTypesDoc?.data).forEach(note => {
             const icon: IconProp = StrCast(note.icon) as IconProp;
             changeItems.push({
-                description: StrCast(note.title), event: undoBatch(() => {
+                description: StrCast(note.title),
+                event: undoBatch(() => {
                     this.layoutDoc.autoHeightMargins = undefined;
                     Doc.setNativeView(this.rootDoc);
                     DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
-                }), icon: icon
+                }),
+                icon: icon,
             });
         });
         const highlighting: ContextMenuProps[] = [];
-        const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others", "Bold Text"];
-        const expertHighlighting = [...noviceHighlighting, "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"];
+        const noviceHighlighting = ['Audio Tags', 'My Text', 'Text from Others', 'Bold Text'];
+        const expertHighlighting = [...noviceHighlighting, 'Important Items', 'Ignore Items', 'Disagree Items', 'By Recent Minute', 'By Recent Hour'];
         (Doc.noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
             highlighting.push({
-                description: (FormattedTextBox._globalHighlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
+                description: (FormattedTextBox._globalHighlights.indexOf(option) === -1 ? 'Highlight ' : 'Unhighlight ') + option,
+                event: () => {
                     e.stopPropagation();
                     if (FormattedTextBox._globalHighlights.indexOf(option) === -1) {
                         FormattedTextBox._globalHighlights.push(option);
                     } else {
                         FormattedTextBox._globalHighlights.splice(FormattedTextBox._globalHighlights.indexOf(option), 1);
                     }
-                    runInAction(() => this.layoutDoc._highlights = FormattedTextBox._globalHighlights.join(""));
+                    runInAction(() => (this.layoutDoc._highlights = FormattedTextBox._globalHighlights.join('')));
                     this.updateHighlights();
-                }, icon: "expand-arrows-alt"
-            }));
+                },
+                icon: 'expand-arrows-alt',
+            })
+        );
 
         const uicontrols: ContextMenuProps[] = [];
-        !Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" });
-        uicontrols.push({ description: !this.Document._noSidebar ? "Hide Sidebar Handle" : "Show Sidebar Handle", event: () => this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar, icon: "expand-arrows-alt" });
-        uicontrols.push({ description: `${this.layoutDoc._showAudio ? "Hide" : "Show"} Dictation Icon`, event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
-        uicontrols.push({ description: "Show Highlights...", noexpand: true, subitems: highlighting, icon: "hand-point-right" });
-        !Doc.noviceMode && uicontrols.push({
-            description: "Broadcast Message", event: () => DocServer.GetRefField("rtfProto").then(proto =>
-                proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), icon: "expand-arrows-alt"
-        });
-        cm.addItem({ description: "UI Controls...", subitems: uicontrols, icon: "asterisk" });
+        !Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ''} Show Menu on Selections`, event: () => (FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate), icon: 'expand-arrows-alt' });
+        uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: 'expand-arrows-alt' });
+        uicontrols.push({ description: `${this.layoutDoc._showAudio ? 'Hide' : 'Show'} Dictation Icon`, event: () => (this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: 'expand-arrows-alt' });
+        uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
+        !Doc.noviceMode &&
+            uicontrols.push({
+                description: 'Broadcast Message',
+                event: () => DocServer.GetRefField('rtfProto').then(proto => proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)),
+                icon: 'expand-arrows-alt',
+            });
+        cm.addItem({ description: 'UI Controls...', subitems: uicontrols, icon: 'asterisk' });
 
-        const appearance = cm.findByDescription("Appearance...");
-        const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
-        appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
+        const appearance = cm.findByDescription('Appearance...');
+        const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
+        appearanceItems.push({ description: 'Change Perspective...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
         // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
-        !Doc.noviceMode && appearanceItems.push({
-            description: "Make Default Layout", event: () => {
-                if (!this.layoutDoc.isTemplateDoc) {
-                    const title = StrCast(this.rootDoc.title);
-                    this.rootDoc.title = "text";
-                    MakeTemplate(this.rootDoc, true, title);
-                } else if (!this.rootDoc.isTemplateDoc) {
-                    const title = StrCast(this.rootDoc.title);
-                    this.rootDoc.title = "text";
-                    this.rootDoc.layout = this.layoutDoc.layout as string;
-                    this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
-                    this.rootDoc.isTemplateDoc = false;
-                    this.rootDoc.isTemplateForField = "";
-                    this.rootDoc.layoutKey = "layout";
-                    MakeTemplate(this.rootDoc, true, title);
-                    setTimeout(() => {
-                        this.rootDoc._autoHeight = this.layoutDoc._autoHeight; // autoHeight, width and height
-                        this.rootDoc._width = this.layoutDoc._width || 300;  // are stored on the template, since we're getting rid of the old template
-                        this.rootDoc._height = this.layoutDoc._height || 200;  // we need to copy them over to the root.  This should probably apply to all '_' fields
-                        this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null);
-                        this.rootDoc.backgroundColor = Cast(this.layoutDoc.backgroundColor, "string", null);
-                    }, 10);
-                }
-                Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
-                Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc);
-            }, icon: "eye"
-        });
-        cm.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
+        !Doc.noviceMode &&
+            appearanceItems.push({
+                description: 'Make Default Layout',
+                event: () => {
+                    if (!this.layoutDoc.isTemplateDoc) {
+                        const title = StrCast(this.rootDoc.title);
+                        this.rootDoc.title = 'text';
+                        MakeTemplate(this.rootDoc, true, title);
+                    } else if (!this.rootDoc.isTemplateDoc) {
+                        const title = StrCast(this.rootDoc.title);
+                        this.rootDoc.title = 'text';
+                        this.rootDoc.layout = this.layoutDoc.layout as string;
+                        this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
+                        this.rootDoc.isTemplateDoc = false;
+                        this.rootDoc.isTemplateForField = '';
+                        this.rootDoc.layoutKey = 'layout';
+                        MakeTemplate(this.rootDoc, true, title);
+                        setTimeout(() => {
+                            this.rootDoc._autoHeight = this.layoutDoc._autoHeight; // autoHeight, width and height
+                            this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template
+                            this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root.  This should probably apply to all '_' fields
+                            this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, 'string', null);
+                            this.rootDoc.backgroundColor = Cast(this.layoutDoc.backgroundColor, 'string', null);
+                        }, 10);
+                    }
+                    Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
+                    Doc.AddDocToList(Cast(Doc.UserDoc()['template-notes'], Doc, null), 'data', this.rootDoc);
+                },
+                icon: 'eye',
+            });
+        cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
 
-        const options = cm.findByDescription("Options...");
-        const optionItems = options && "subitems" in options ? options.subitems : [];
-        optionItems.push({ description: !this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
-        optionItems.push({ description: `${this.Document._autoHeight ? "Lock" : "Auto"} Height`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
-        !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
+        const options = cm.findByDescription('Options...');
+        const optionItems = options && 'subitems' in options ? options.subitems : [];
+        optionItems.push({ description: !this.Document._singleLine ? 'Make Single Line' : 'Make Multi Line', event: () => (this.layoutDoc._singleLine = !this.layoutDoc._singleLine), icon: 'expand-arrows-alt' });
+        optionItems.push({ description: `${this.Document._autoHeight ? 'Lock' : 'Auto'} Height`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+        !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
         this._downX = this._downY = Number.NaN;
-    }
+    };
 
     breakupDictation = () => {
         if (this._editorView && this._recording) {
@@ -721,12 +769,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const state = this._editorView.state;
             const to = state.selection.to;
             const updated = TextSelection.create(state.doc, to, to);
-            this._editorView.dispatch(state.tr.setSelection(updated).insertText("\n", to));
+            this._editorView.dispatch(state.tr.setSelection(updated).insertText('\n', to));
             if (this._recording) {
                 this.recordDictation();
             }
         }
-    }
+    };
     recordDictation = () => {
         DictationManager.Controls.listen({
             interimHandler: this.setDictationContent,
@@ -736,26 +784,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 DictationManager.Controls.stop();
             }
         });
-    }
+    };
     stopDictation = (abort: boolean) => DictationManager.Controls.stop(!abort);
 
     setDictationContent = (value: string) => {
         if (this._editorView && this._recordingStart) {
             if (this._break) {
-                const textanchor = Docs.Create.TextanchorDocument({ title: "dictation anchor" });
+                const textanchor = Docs.Create.TextanchorDocument({ title: 'dictation anchor' });
                 this.addDocument(textanchor);
                 const link = DocUtils.MakeLinkToActiveAudio(() => textanchor, false).lastElement();
                 link && (Doc.GetProto(link).isDictation = true);
                 if (!link) return;
                 const audioanchor = Cast(link.anchor2, Doc, null);
                 if (!audioanchor) return;
-                audioanchor.backgroundColor = "tan";
+                audioanchor.backgroundColor = 'tan';
                 const audiotag = this._editorView.state.schema.nodes.audiotag.create({
                     timeCode: NumCast(audioanchor._timecodeToShow),
                     audioId: audioanchor[Id],
-                    textId: textanchor[Id]
+                    textId: textanchor[Id],
                 });
-                Doc.GetProto(textanchor).title = "dictation:" + audiotag.attrs.timeCode;
+                Doc.GetProto(textanchor).title = 'dictation:' + audiotag.attrs.timeCode;
                 const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag);
                 const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
                 this._editorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
@@ -765,7 +813,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const tr = this._editorView.state.tr.insertText(value);
             this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, tr.doc.content.size)).scrollIntoView());
         }
-    }
+    };
 
     // TODO: nda -- Look at how link anchors are added
     makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string) {
@@ -775,7 +823,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
             let tr = state.tr.addMark(sel.from, sel.to, splitter);
             if (sel.from !== sel.to) {
-                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: "#" + this._editorView?.state.doc.textBetween(sel.from, sel.to), annotationOn: this.dataDoc, unrendered: true });
+                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: '#' + this._editorView?.state.doc.textBetween(sel.from, sel.to), annotationOn: this.dataDoc, unrendered: true });
                 const href = targetHref ?? Doc.localServerPath(anchor);
                 if (anchor !== anchorDoc) this.addDocument(anchor);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
@@ -786,7 +834,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         tr = tr.addMark(pos, pos + node.nodeSize, link);
                     }
                 });
-                this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true;  // need to allow permissions for adding links to readonly/augment only documents
+                this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true; // need to allow permissions for adding links to readonly/augment only documents
                 this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
                 this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
                 return anchor;
@@ -797,7 +845,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     scrollFocus = (textAnchor: Doc, smooth: boolean) => {
-        if (DocListCast(this.Document[this.fieldKey + "-sidebar"]).includes(textAnchor) && !this.SidebarShown) {
+        if (DocListCast(this.Document[this.fieldKey + '-sidebar']).includes(textAnchor) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
         }
         const textAnchorId = textAnchor[Id];
@@ -827,7 +875,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
             const marks = [...node.marks];
             const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor);
-            return linkIndex !== -1 && marks[linkIndex].attrs.allAnchors.find((item: { href: string }) => textAnchorId === item.href.replace(/.*\/doc\//, "")) ? { node, start: 0 } : undefined;
+            return linkIndex !== -1 && marks[linkIndex].attrs.allAnchors.find((item: { href: string }) => textAnchorId === item.href.replace(/.*\/doc\//, '')) ? { node, start: 0 } : undefined;
         };
 
         let start = 0;
@@ -845,59 +893,69 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
                 editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
                 const escAnchorId = textAnchorId[0] >= '0' && textAnchorId[0] <= '9' ? `\\3${textAnchorId[0]} ${textAnchorId.substr(1)}` : textAnchorId;
-                addStyleSheetRule(FormattedTextBox._highlightStyleSheet, `${escAnchorId}`, { background: "yellow", transform: "scale(3)", "transform-origin": "left bottom" });
-                setTimeout(() => this._focusSpeed = undefined, this._focusSpeed);
+                addStyleSheetRule(FormattedTextBox._highlightStyleSheet, `${escAnchorId}`, { background: 'yellow', transform: 'scale(3)', 'transform-origin': 'left bottom' });
+                setTimeout(() => (this._focusSpeed = undefined), this._focusSpeed);
                 setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000));
             }
         }
 
         return this._didScroll ? this._focusSpeed : undefined; // if we actually scrolled, then return some focusSpeed
-    }
+    };
 
     getScrollHeight = () => this.scrollHeight;
     // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc.
     // Since we also monitor all component height changes, this will update the document's height.
     resetNativeHeight = (scrollHeight: number) => {
         const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight);
-        this.rootDoc[this.fieldKey + "-height"] = scrollHeight;
+        this.rootDoc[this.fieldKey + '-height'] = scrollHeight;
         if (nh) this.layoutDoc._nativeHeight = scrollHeight;
-    }
+    };
 
-    @computed get contentScaling() { return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.scaling?.() || 1 : 1;}
+    @computed get contentScaling() {
+        return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.scaling?.() || 1 : 1;
+    }
     componentDidMount() {
         !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         this._cachedLinks = DocListCast(this.Document.links);
         this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation);
-        this._disposers.autoHeight = reaction(() => this.autoHeight, autoHeight => autoHeight && this.tryUpdateScrollHeight());
-        this._disposers.scrollHeight = reaction(() => ({ scrollHeight: this.scrollHeight, autoHeight: this.autoHeight, width: NumCast(this.layoutDoc._width) }),
+        this._disposers.autoHeight = reaction(
+            () => this.autoHeight,
+            autoHeight => autoHeight && this.tryUpdateScrollHeight()
+        );
+        this._disposers.scrollHeight = reaction(
+            () => ({ scrollHeight: this.scrollHeight, autoHeight: this.autoHeight, width: NumCast(this.layoutDoc._width) }),
             ({ width, scrollHeight, autoHeight }) => {
                 width && autoHeight && this.resetNativeHeight(scrollHeight);
-            }, { fireImmediately: true }
+            },
+            { fireImmediately: true }
         );
-        this._disposers.componentHeights = reaction(  // set the document height when one of the component heights changes and autoHeight is on
+        this._disposers.componentHeights = reaction(
+            // set the document height when one of the component heights changes and autoHeight is on
             () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }),
             ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => {
                 autoHeight && this.props.setHeight?.(this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight)));
-            }, { fireImmediately: true });
-        this._disposers.links = reaction(() => DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
+            },
+            { fireImmediately: true }
+        );
+        this._disposers.links = reaction(
+            () => DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
             newLinks => {
                 this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l));
                 this._cachedLinks = newLinks;
-            });
+            }
+        );
         this._disposers.buttonBar = reaction(
             () => DocumentButtonBar.Instance,
             instance => {
                 if (instance) {
                     this.pullFromGoogleDoc(this.checkState);
-                    this.dataDoc[GoogleRef] && this.dataDoc.googleDocUnchanged && runInAction(() => instance.isAnimatingFetch = true);
+                    this.dataDoc[GoogleRef] && this.dataDoc.googleDocUnchanged && runInAction(() => (instance.isAnimatingFetch = true));
                 }
             }
         );
         this._disposers.editorState = reaction(
             () => {
-                const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined :
-                    this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey] ?
-                        this.dataDoc : this.layoutDoc;
+                const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : this.dataDoc?.[this.props.fieldKey + '-noTemplate'] || !this.layoutDoc[this.props.fieldKey] ? this.dataDoc : this.layoutDoc;
                 return !whichDoc ? undefined : { data: Cast(whichDoc[this.props.fieldKey], RichTextField, null), str: StrCast(whichDoc[this.props.fieldKey]) };
             },
             incomingValue => {
@@ -912,7 +970,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         selectAll(this._editorView.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue.str)));
                     }
                 }
-            },
+            }
         );
         this._disposers.pullDoc = reaction(
             () => this.props.Document[Pulls],
@@ -933,13 +991,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         );
 
-        this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc),
-            search => search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(),
-            { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false });
+        this._disposers.search = reaction(
+            () => Doc.IsSearchMatch(this.rootDoc),
+            search => (search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms()),
+            { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false }
+        );
 
-        this._disposers.selected = reaction(() => this.props.isSelected(),
+        this._disposers.selected = reaction(
+            () => this.props.isSelected(),
             action(selected => {
-                this.layoutDoc._highlights = selected ? FormattedTextBox._globalHighlights.join("") : "";
+                this.layoutDoc._highlights = selected ? FormattedTextBox._globalHighlights.join('') : '';
                 if (RichTextMenu.Instance?.view === this._editorView && !selected) {
                     RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
                 }
@@ -947,21 +1008,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
                     this.autoLink();
                 }
-            }), { fireImmediately: true });
+            }),
+            { fireImmediately: true }
+        );
 
         if (!this.props.dontRegisterView) {
-            this._disposers.record = reaction(() => this._recording,
+            this._disposers.record = reaction(
+                () => this._recording,
                 () => {
                     this.stopDictation(true);
                     if (this._recording) {
                         this.recordDictation();
                     }
-                },
+                }
             );
             if (this._recording) setTimeout(this.recordDictation);
         }
-        var quickScroll: string | undefined = "";
-        this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop),
+        var quickScroll: string | undefined = '';
+        this._disposers.scroll = reaction(
+            () => NumCast(this.layoutDoc._scrollTop),
             pos => {
                 if (!this._ignoreScroll && this._scrollRef.current && !this.props.dontSelectOnLoad) {
                     const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
@@ -974,7 +1039,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         this._scrollRef.current.scrollTo({ top: pos });
                     }
                 }
-            }, { fireImmediately: true }
+            },
+            { fireImmediately: true }
         );
         quickScroll = undefined;
         this.tryUpdateScrollHeight();
@@ -984,7 +1050,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this.pullFromGoogleDoc(async (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
             const modes = GoogleApiClientUtils.Docs.WriteMode;
             let mode = modes.Replace;
-            let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[GoogleRef], "string");
+            let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[GoogleRef], 'string');
             if (!reference) {
                 mode = modes.Insert;
                 reference = { title: StrCast(this.dataDoc.title) };
@@ -994,7 +1060,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     const content = await RichTextUtils.GoogleDocs.Export(this._editorView.state);
                     const response = await GoogleApiClientUtils.Docs.write({ reference, content, mode });
                     response && (this.dataDoc[GoogleRef] = response.documentId);
-                    const pushSuccess = response !== undefined && !("errors" in response);
+                    const pushSuccess = response !== undefined && !('errors' in response);
                     dataDoc.googleDocUnchanged = pushSuccess;
                     DocumentButtonBar.Instance.startPushOutcome(pushSuccess);
                 }
@@ -1003,15 +1069,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 if (exportState && reference) {
                     const content: GoogleApiClientUtils.Docs.Content = {
                         text: exportState.text,
-                        requests: []
+                        requests: [],
                     };
                     GoogleApiClientUtils.Docs.write({ reference, content, mode });
                 }
             };
-            UndoManager.AddEvent({ undo, redo, prop: "" });
+            UndoManager.AddEvent({ undo, redo, prop: '' });
             redo();
         });
-    }
+    };
 
     pullFromGoogleDoc = async (handler: PullHandler) => {
         const dataDoc = this.dataDoc;
@@ -1021,7 +1087,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             exportState = await RichTextUtils.GoogleDocs.Import(documentId, dataDoc);
         }
         exportState && UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
-    }
+    };
 
     updateState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
         let pullSuccess = false;
@@ -1036,13 +1102,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             }, 0);
             dataDoc.title = exportState.title;
-            this.dataDoc["title-custom"] = true;
+            this.dataDoc['title-custom'] = true;
             dataDoc.googleDocUnchanged = true;
         } else {
             delete dataDoc[GoogleRef];
         }
         DocumentButtonBar.Instance.startPullOutcome(pullSuccess);
-    }
+    };
 
     checkState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
         if (exportState && this._editorView) {
@@ -1052,56 +1118,61 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             dataDoc.googleDocUnchanged = unchanged;
             DocumentButtonBar.Instance.setPullState(unchanged);
         }
-    }
+    };
 
     clipboardTextSerializer = (slice: Slice): string => {
-        let text = "", separated = true;
-        const from = 0, to = slice.content.size;
-        slice.content.nodesBetween(from, to, (node, pos) => {
-            if (node.isText) {
-                text += node.text!.slice(Math.max(from, pos) - pos, to - pos);
-                separated = false;
-            } else if (!separated && node.isBlock) {
-                text += "\n";
-                separated = true;
-            } else if (node.type.name === "hard_break") {
-                text += "\n";
-            }
-        }, 0);
+        let text = '',
+            separated = true;
+        const from = 0,
+            to = slice.content.size;
+        slice.content.nodesBetween(
+            from,
+            to,
+            (node, pos) => {
+                if (node.isText) {
+                    text += node.text!.slice(Math.max(from, pos) - pos, to - pos);
+                    separated = false;
+                } else if (!separated && node.isBlock) {
+                    text += '\n';
+                    separated = true;
+                } else if (node.type.name === 'hard_break') {
+                    text += '\n';
+                }
+            },
+            0
+        );
         return text;
-    }
+    };
 
     handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => {
         const cbe = event as ClipboardEvent;
-        const pdfDocId = cbe.clipboardData?.getData("dash/pdfOrigin");
-        const pdfRegionId = cbe.clipboardData?.getData("dash/pdfRegion");
+        const pdfDocId = cbe.clipboardData?.getData('dash/pdfOrigin');
+        const pdfRegionId = cbe.clipboardData?.getData('dash/pdfRegion');
         return pdfDocId && pdfRegionId && this.addPdfReference(pdfDocId, pdfRegionId, slice) ? true : false;
-    }
+    };
 
     addPdfReference = (pdfDocId: string, pdfRegionId: string, slice?: Slice) => {
         const view = this._editorView!;
         if (pdfDocId && pdfRegionId) {
             DocServer.GetRefField(pdfDocId).then(pdfDoc => {
                 DocServer.GetRefField(pdfRegionId).then(pdfRegion => {
-                    if ((pdfDoc instanceof Doc) && (pdfRegion instanceof Doc)) {
+                    if (pdfDoc instanceof Doc && pdfRegion instanceof Doc) {
                         setTimeout(async () => {
                             const targetField = Doc.LayoutFieldKey(pdfDoc);
-                            const targetAnnotations = await DocListCastAsync(pdfDoc[DataSym][targetField + "-annotations"]);// bcz: better to have the PDF's view handle updating its own annotations
+                            const targetAnnotations = await DocListCastAsync(pdfDoc[DataSym][targetField + '-annotations']); // bcz: better to have the PDF's view handle updating its own annotations
                             if (targetAnnotations) targetAnnotations.push(pdfRegion);
-                            else Doc.AddDocToList(pdfDoc[DataSym], targetField + "-annotations", pdfRegion);
+                            else Doc.AddDocToList(pdfDoc[DataSym], targetField + '-annotations', pdfRegion);
                         });
 
-                        const link = DocUtils.MakeLink({ doc: this.rootDoc }, { doc: pdfRegion }, "PDF pasted");
+                        const link = DocUtils.MakeLink({ doc: this.rootDoc }, { doc: pdfRegion }, 'PDF pasted');
                         if (link) {
                             const linkId = link[Id];
-                            const quote = view.state.schema.nodes.blockquote.create();
-                            quote.content = addMarkToFrag(slice?.content || view.state.doc.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId));
+                            const quote = view.state.schema.nodes.blockquote.create({ content: addMarkToFrag(slice?.content || view.state.doc.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId)) });
                             const newSlice = new Slice(Fragment.from(quote), slice?.openStart || 0, slice?.openEnd || 0);
                             if (slice) {
-                                view.dispatch(view.state.tr.replaceSelection(newSlice).scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste"));
+                                view.dispatch(view.state.tr.replaceSelection(newSlice).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
                             } else {
                                 selectAll(view.state, (tx: Transaction) => view.dispatch(tx.replaceSelection(newSlice).scrollIntoView()));
-
                             }
                         }
                     }
@@ -1111,31 +1182,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
         return false;
 
-
         function addMarkToFrag(frag: Fragment, marker: (node: Node) => Node) {
             const nodes: Node[] = [];
             frag.forEach(node => nodes.push(marker(node)));
             return Fragment.fromArray(nodes);
         }
 
-
         function addLinkMark(node: Node, title: string, linkId: string) {
             if (!node.isText) {
                 const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title, linkId));
                 return node.copy(content);
             }
             const marks = [...node.marks];
-            const linkIndex = marks.findIndex(mark => mark.type.name === "link");
+            const linkIndex = marks.findIndex(mark => mark.type.name === 'link');
             const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }];
-            const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true });
+            const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: 'add:right', title, docref: true });
             marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
             return node.mark(marks);
         }
-    }
+    };
 
     isActiveTab(el: Element | null | undefined) {
         while (el && el !== document.body) {
-            if (getComputedStyle(el).display === "none") return false;
+            if (getComputedStyle(el).display === 'none') return false;
             el = el.parentNode as any;
         }
         return true;
@@ -1147,7 +1216,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             view(newView) {
                 runInAction(() => self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView));
                 return new RichTextMenuPlugin({ editorProps: this.props });
-            }
+            },
         });
     }
     _didScroll = false;
@@ -1159,7 +1228,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this._editorView?.destroy();
             this._editorView = new EditorView(this.ProseRef, {
                 state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
-                handleScrollToSelection: (editorView) => {
+                handleScrollToSelection: editorView => {
                     const docPos = editorView.coordsAtPos(editorView.state.selection.to);
                     const viewRect = self._ref.current!.getBoundingClientRect();
                     const scrollRef = self._scrollRef.current;
@@ -1179,13 +1248,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 },
                 dispatchTransaction: this.dispatchTransaction,
                 nodeViews: {
-                    dashComment(node: any, view: any, getPos: any) { return new DashDocCommentView(node, view, getPos); },
-                    dashDoc(node: any, view: any, getPos: any) { return new DashDocView(node, view, getPos, self); },
-                    dashField(node: any, view: any, getPos: any) { return new DashFieldView(node, view, getPos, self); },
-                    equation(node: any, view: any, getPos: any) { return new EquationView(node, view, getPos, self); },
-                    summary(node: any, view: any, getPos: any) { return new SummaryView(node, view, getPos); },
-                    ordered_list(node: any, view: any, getPos: any) { return new OrderedListView(); },
-                    footnote(node: any, view: any, getPos: any) { return new FootnoteView(node, view, getPos); }
+                    dashComment(node: any, view: any, getPos: any) {
+                        return new DashDocCommentView(node, view, getPos);
+                    },
+                    dashDoc(node: any, view: any, getPos: any) {
+                        return new DashDocView(node, view, getPos, self);
+                    },
+                    dashField(node: any, view: any, getPos: any) {
+                        return new DashFieldView(node, view, getPos, self);
+                    },
+                    equation(node: any, view: any, getPos: any) {
+                        return new EquationView(node, view, getPos, self);
+                    },
+                    summary(node: any, view: any, getPos: any) {
+                        return new SummaryView(node, view, getPos);
+                    },
+                    //ordered_list(node: any, view: any, getPos: any) { return new OrderedListView(); },
+                    footnote(node: any, view: any, getPos: any) {
+                        return new FootnoteView(node, view, getPos);
+                    },
                 },
                 clipboardTextSerializer: this.clipboardTextSerializer,
                 handlePaste: this.handlePaste,
@@ -1196,9 +1277,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 if (startupText) {
                     dispatch(state.tr.insertText(startupText));
                 }
-                const textAlign = StrCast(this.dataDoc["text-align"], StrCast(Doc.UserDoc().textAlign, "left"));
-                if (textAlign !== "left") {
-                    selectAll(this._editorView.state, (tr) => {
+                const textAlign = StrCast(this.dataDoc['text-align'], StrCast(Doc.UserDoc().textAlign, 'left'));
+                if (textAlign !== 'left') {
+                    selectAll(this._editorView.state, tr => {
                         this._editorView!.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
                     });
                 }
@@ -1209,14 +1290,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()));
         if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
             const selLoadChar = FormattedTextBox.SelectOnLoadChar;
-            FormattedTextBox.SelectOnLoad = "";
+            FormattedTextBox.SelectOnLoad = '';
             this.props.select(false);
             if (selLoadChar && this._editorView) {
                 const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
                 const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
                 const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-                const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks);
+                const tr = this._editorView.state.tr
+                    .setStoredMarks(storedMarks)
+                    .insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size)
+                    .setStoredMarks(storedMarks);
                 this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
             } else if (this._editorView && curText && !FormattedTextBox.DontSelectInitialText) {
                 selectAll(this._editorView.state, this._editorView?.dispatch);
@@ -1229,14 +1313,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         selectOnLoad && this._editorView!.focus();
         // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
         if (this._editorView && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) {
-            this._editorView.state.storedMarks = [...(this._editorView.state.storedMarks ?? []),
-            schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }),
-            ...(Doc.UserDoc().fontColor !== "transparent" && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
-            ...(Doc.UserDoc().fontStyle === "italics" ? [schema.mark(schema.marks.em)] : []),
-            ...(Doc.UserDoc().textDecoration === "underline" ? [schema.mark(schema.marks.underline)] : []),
-            ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
-            ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
-            ...(Doc.UserDoc().fontWeight === "bold" ? [schema.mark(schema.marks.strong)] : [])];
+            this._editorView.state.storedMarks = [
+                ...(this._editorView.state.storedMarks ?? []),
+                schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }),
+                ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
+                ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
+                ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
+                ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
+                ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
+                ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
+            ];
         }
     }
 
@@ -1247,13 +1333,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this.unhighlightSearchTerms();
         this._editorView?.destroy();
         RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined);
-        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
+        FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = 'none');
     }
 
     onPointerDown = (e: React.PointerEvent): void => {
         if (this.Document.forceActive) e.stopPropagation();
         this.tryUpdateScrollHeight(); // if a doc a fitwidth doc is being viewed in different context (eg freeform & lightbox), then it will have conflicting heights.  so when the doc is clicked on, we want to make sure it has the appropriate height for the selected view.
-        if ((e.target as any).tagName === "AUDIOTAG") {
+        if ((e.target as any).tagName === 'AUDIOTAG') {
             e.preventDefault();
             e.stopPropagation();
             const timecode = Number((e.target as any)?.dataset?.timecode);
@@ -1264,10 +1350,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     const func = () => {
                         const docView = DocumentManager.Instance.getDocumentView(audiodoc);
                         if (!docView) {
-                            this.props.addDocTab(audiodoc, "add:bottom");
+                            this.props.addDocTab(audiodoc, 'add:bottom');
                             setTimeout(func);
-                        }
-                        else docView.ComponentView?.playFrom?.(timecode, Cast(anchor.timecodeToHide, "number", null)); // bcz: would be nice to find the next audio tag in the doc and play until that
+                        } else docView.ComponentView?.playFrom?.(timecode, Cast(anchor.timecodeToHide, 'number', null)); // bcz: would be nice to find the next audio tag in the doc and play until that
                     };
                     func();
                 }
@@ -1283,29 +1368,30 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this._downEvent = true;
         FormattedTextBoxComment.textBox = this;
         if (e.button === 0 && (this.props.rootSelected(true) || this.props.isSelected(true)) && !e.altKey && !e.ctrlKey && !e.metaKey) {
-            if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar
+            if (e.clientX < this.ProseRef!.getBoundingClientRect().right) {
+                // stop propagation if not in sidebar
                 // bcz: Change. drag selecting requires that preventDefault is NOT called.  This used to happen in DocumentView,
                 //      but that's changed, so this shouldn't be needed.
                 //e.stopPropagation();  // if the text box is selected, then it consumes all down events
-                document.addEventListener("pointerup", this.onSelectEnd);
-                document.addEventListener("pointermove", this.onSelectMove);
+                document.addEventListener('pointerup', this.onSelectEnd);
+                document.addEventListener('pointermove', this.onSelectMove);
             }
         }
         if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
             e.preventDefault();
         }
-    }
+    };
     onSelectMove = (e: PointerEvent) => e.stopPropagation();
     onSelectEnd = (e: PointerEvent) => {
-        document.removeEventListener("pointerup", this.onSelectEnd);
-        document.removeEventListener("pointermove", this.onSelectMove);
-    }
+        document.removeEventListener('pointerup', this.onSelectEnd);
+        document.removeEventListener('pointermove', this.onSelectMove);
+    };
     onPointerUp = (e: React.PointerEvent): void => {
         if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate) this.setupAnchorMenu();
         if (!this._downEvent) return;
         this._downEvent = false;
         if ((e.nativeEvent as any).formattedHandled) {
-            console.log("handled");
+            console.log('handled');
         }
         if (!(e.nativeEvent as any).formattedHandled && this.props.isContentActive(true)) {
             const editor = this._editorView!;
@@ -1320,13 +1406,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (e.buttons === 1 && this.props.isSelected(true) && !e.altKey) {
             e.stopPropagation();
         }
-    }
+    };
     @action
     onDoubleClick = (e: React.MouseEvent): void => {
         FormattedTextBoxComment.textBox = this;
         if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) {
-            if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar
-                e.stopPropagation();  // if the text box is selected, then it consumes all click events
+            if (e.clientX < this.ProseRef!.getBoundingClientRect().right) {
+                // stop propagation if not in sidebar
+                e.stopPropagation(); // if the text box is selected, then it consumes all click events
             }
         }
         if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
@@ -1339,18 +1426,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (e.buttons === 1 && this.props.isSelected(true) && !e.altKey) {
             e.stopPropagation();
         }
-    }
+    };
     setFocus = () => {
         const pos = this._editorView?.state.selection.$from.pos || 1;
         (this.ProseRef?.children?.[0] as any).focus();
         setTimeout(() => this._editorView?.dispatch(this._editorView?.state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos))));
-    }
+    };
     @action
     onFocused = (e: React.FocusEvent): void => {
         //applyDevTools.applyDevTools(this._editorView);
         FormattedTextBox.Focused = this;
         this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
-    }
+    };
     @observable public static Focused: FormattedTextBox | undefined;
 
     onClick = (e: React.MouseEvent): void => {
@@ -1358,7 +1445,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this._forceDownNode = undefined;
             return;
         }
-        if (!this._forceUncollapse || (this._editorView!.root as any).getSelection().isCollapsed) { // this is a hack to allow the cursor to be placed at the end of a document when the document ends in an inline dash comment.  Apparently Chrome on Windows has a bug/feature which breaks this when clicking after the end of the text.
+        if (!this._forceUncollapse || (this._editorView!.root as any).getSelection().isCollapsed) {
+            // this is a hack to allow the cursor to be placed at the end of a document when the document ends in an inline dash comment.  Apparently Chrome on Windows has a bug/feature which breaks this when clicking after the end of the text.
             const pcords = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
             const node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos); // get what prosemirror thinks the clicked node is (if it's null, then we didn't click on any text)
             if (pcords && node?.type === this._editorView!.state.schema.nodes.dashComment) {
@@ -1368,13 +1456,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             if (!node && this.ProseRef) {
                 const lastNode = this.ProseRef.children[this.ProseRef.children.length - 1].children[this.ProseRef.children[this.ProseRef.children.length - 1].children.length - 1]; // get the last prosemirror div
                 const boundsRect = lastNode?.getBoundingClientRect();
-                if (e.clientX > boundsRect.left && e.clientX < boundsRect.right &&
-                    e.clientY > boundsRect.bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
+                if (e.clientX > boundsRect.left && e.clientX < boundsRect.right && e.clientY > boundsRect.bottom) {
+                    // if we clicked below the last prosemirror div, then set the selection to be the end of the document
                     this._editorView?.focus();
                     this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, this._editorView!.state.doc.content.size)));
                 }
-            } else if ([this._editorView!.state.schema.nodes.ordered_list, this._editorView!.state.schema.nodes.listItem].includes(node?.type) &&
-                node !== (this._editorView!.state.selection as NodeSelection)?.node && pcords) {
+            } else if (node && [this._editorView!.state.schema.nodes.ordered_list, this._editorView!.state.schema.nodes.listItem].includes(node.type) && node !== (this._editorView!.state.selection as NodeSelection)?.node && pcords) {
                 this._editorView!.dispatch(this._editorView!.state.tr.setSelection(NodeSelection.create(this._editorView!.state.doc, pcords.pos)));
             }
         }
@@ -1382,7 +1469,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             e.stopPropagation();
             return;
         }
-        if (this.props.isSelected(true)) { // if text box is selected, then it consumes all click events
+        if (this.props.isSelected(true)) {
+            // if text box is selected, then it consumes all click events
             (e.nativeEvent as any).formattedHandled = true;
             if (this.ProseRef?.children[0] !== e.nativeEvent.target) e.stopPropagation(); // if you double click on text, then it will be selected instead of sending a double click to DocumentView & opening a lightbox.  Also,if a text box has isLinkButton, this will prevent link following if you've selected the document to edit it.
             // e.stopPropagation();  // bcz: not sure why this was here.  We need to allow the DocumentView to get clicks to process doubleClicks (see above comment)
@@ -1390,7 +1478,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
         this._forceUncollapse = !(this._editorView!.root as any).getSelection().isCollapsed;
         this._forceDownNode = (this._editorView!.state.selection as NodeSelection)?.node;
-    }
+    };
     // this hackiness handles clicking on the list item bullets to do expand/collapse.  the bullets are ::before pseudo elements so there's no real way to hit test against them.
     hitBulletTargets(x: number, y: number, collapse: boolean, highlightOnly: boolean, downNode: Node | undefined = undefined, selectOrderedList: boolean = false) {
         this._forceUncollapse = false;
@@ -1420,7 +1508,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, clickPos.pos)));
                     }
                 }
-                addStyleSheetRule(FormattedTextBox._bulletStyleSheet, olistNode.attrs.mapStyle + olistNode.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
+                addStyleSheetRule(FormattedTextBox._bulletStyleSheet, olistNode.attrs.mapStyle + olistNode.attrs.bulletStyle + ':hover:before', { background: 'lightgray' });
             }
         }
     }
@@ -1432,20 +1520,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         // are nested prosemirrors.  We only want the lowest level prosemirror to be invoked.
         if (view.mouseDown) {
             const originalUpHandler = view.mouseDown.up;
-            view.root.removeEventListener("mouseup", originalUpHandler);
+            view.root.removeEventListener('mouseup', originalUpHandler);
             view.mouseDown.up = (e: MouseEvent) => {
                 if (!(e as any).formattedHandled) {
                     originalUpHandler(e);
                     (e as any).formattedHandled = true;
                 } else {
-                    console.log("prehandled");
+                    console.log('prehandled');
                 }
             };
-            view.root.addEventListener("mouseup", view.mouseDown.up);
+            view.root.addEventListener('mouseup', view.mouseDown.up);
         }
-    }
+    };
     startUndoTypingBatch() {
-        !this._undoTyping && (this._undoTyping = UndoManager.StartBatch("undoTyping"));
+        !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('undoTyping'));
     }
     public endUndoTypingBatch() {
         const wasUndoing = this._undoTyping;
@@ -1461,33 +1549,39 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) {
             RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
         }
-        FormattedTextBox._hadSelection = window.getSelection()?.toString() !== "";
+        FormattedTextBox._hadSelection = window.getSelection()?.toString() !== '';
         this.endUndoTypingBatch();
 
         FormattedTextBox.LiveTextUndo?.end();
         FormattedTextBox.LiveTextUndo = undefined;
 
         const state = this._editorView!.state;
-        const curText = state.doc.textBetween(0, state.doc.content.size, " \n");
-        if (this.layoutDoc.sidebarViewType === "translation" && !this.fieldKey.includes("translation") && curText.endsWith(" ") && curText !== this._lastText) {
+        const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
+        if (this.layoutDoc.sidebarViewType === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) {
             try {
-                translateGoogleApi(curText, { from: "en", to: "es", }).then((result1: any) => {
-                    setTimeout(() => translateGoogleApi(result1[0], { from: "es", to: "en", }).then((result: any) => {
-                        this.dataDoc[this.fieldKey + "-translation"] = result1 + "\r\n\r\n" + result[0];
-                    }), 1000);
+                translateGoogleApi(curText, { from: 'en', to: 'es' }).then((result1: any) => {
+                    setTimeout(
+                        () =>
+                            translateGoogleApi(result1[0], { from: 'es', to: 'en' }).then((result: any) => {
+                                this.dataDoc[this.fieldKey + '-translation'] = result1 + '\r\n\r\n' + result[0];
+                            }),
+                        1000
+                    );
                 });
-            } catch (e: any) { console.log(e.message); }
+            } catch (e: any) {
+                console.log(e.message);
+            }
             this._lastText = curText;
         }
-        if (StrCast(this.rootDoc.title).startsWith("@") && !this.dataDoc["title-custom"]) {
+        if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc['title-custom']) {
             UndoManager.RunInBatch(() => {
-                this.dataDoc["title-custom"] = true;
-                this.dataDoc.showTitle = "title";
+                this.dataDoc['title-custom'] = true;
+                this.dataDoc.showTitle = 'title';
                 const tr = this._editorView!.state.tr;
                 this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.rootDoc.title).length + 2))).deleteSelection());
-            }, "titler");
+            }, 'titler');
         }
-    }
+    };
 
     onKeyDown = (e: React.KeyboardEvent) => {
         if (e.altKey) {
@@ -1495,7 +1589,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             return;
         }
         const state = this._editorView!.state;
-        if (!state.selection.empty && e.key === "%") {
+        if (!state.selection.empty && e.key === '%') {
             this._rules!.EnteringStyle = true;
             e.preventDefault();
             e.stopPropagation();
@@ -1508,32 +1602,34 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         e.stopPropagation();
         for (var i = state.selection.from; i <= state.selection.to; i++) {
             const node = state.doc.resolve(i);
-            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark &&
-                mark.attrs.userid !== Doc.CurrentUserEmail) &&
-                [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
+            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
                 e.preventDefault();
             }
         }
         switch (e.key) {
-            case "Escape":
+            case 'Escape':
                 this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
                 (document.activeElement as any).blur?.();
                 SelectionManager.DeselectAll();
                 RichTextMenu.Instance.updateMenu(undefined, undefined, undefined);
                 return;
-            case "Enter": this.insertTime();
-            case "Tab": e.preventDefault(); break;
-            default: if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
-            case " ":
-                [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) && this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({}))
-                    .addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
+            case 'Enter':
+                this.insertTime();
+            case 'Tab':
+                e.preventDefault();
+                break;
+            default:
+                if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
+            case ' ':
+                [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) &&
+                    this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
         }
         this.startUndoTypingBatch();
-    }
+    };
     ondrop = (e: React.DragEvent) => {
         this._editorView!.dispatch(updateBullets(this._editorView!.state.tr, this._editorView!.state.schema));
         e.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
-    }
+    };
     onScroll = (e: React.UIEvent) => {
         if (!LinkDocPreview.LinkInfo && this._scrollRef.current) {
             if (!this.props.dontSelectOnLoad) {
@@ -1542,15 +1638,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 this._ignoreScroll = false;
             }
         }
-    }
+    };
     tryUpdateScrollHeight = () => {
         const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
         const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
         if (children) {
-            const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
+            const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace('px', '')), margins);
             const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
-            if (this.props.setHeight && scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
-                const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
+            if (this.props.setHeight && scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {
+                // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+                const setScrollHeight = () => (this.rootDoc[this.fieldKey + '-scrollHeight'] = scrollHeight);
                 if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
                     setScrollHeight();
                 } else {
@@ -1558,7 +1655,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             }
         }
-    }
+    };
     fitContentsToBox = () => this.props.Document._fitContentsToBox;
     sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
@@ -1566,40 +1663,50 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         // console.log("printting allSideBarDocs");
         // console.log(this.allSidebarDocs);
         return this.addDocument(doc, sidebarKey);
-    }
+    };
     sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey);
     sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey);
-    setSidebarHeight = (height: number) => this.rootDoc[this.SidebarKey + "-height"] = height;
-    sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
-    sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.scaling?.() || 1), 0).scale(1 / NumCast(this.layoutDoc._viewScale, 1));
+    setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '-height'] = height);
+    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    sidebarScreenToLocal = () =>
+        this.props
+            .ScreenToLocalTransform()
+            .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.scaling?.() || 1), 0)
+            .scale(1 / NumCast(this.layoutDoc._viewScale, 1));
 
     @computed get audioHandle() {
-        return <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
-            <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? "red" : "blue", transitionDelay: "0.6s", opacity: this._recording ? 1 : 0.25, }} icon={"microphone"} size="sm" />
-        </div>;
+        return (
+            <div className="formattedTextBox-dictation" onClick={action(e => (this._recording = !this._recording))}>
+                <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? 'red' : 'blue', transitionDelay: '0.6s', opacity: this._recording ? 1 : 0.25 }} icon={'microphone'} size="sm" />
+            </div>
+        );
     }
     @computed get sidebarHandle() {
         TraceMobx();
         const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
         const color = !annotated ? Colors.WHITE : Colors.BLACK;
-        const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ":annotated" : ""));
+        const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor + (annotated ? ':annotated' : ''));
 
-        return (!annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging())) ? (null) :
-            <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
+        return !annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging()) ? null : (
+            <div
+                className="formattedTextBox-sidebar-handle"
+                onPointerDown={this.sidebarDown}
                 style={{
                     left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 17px))`,
                     backgroundColor: backgroundColor,
                     color: color,
-                    opacity: annotated ? 1 : undefined
-                }} >
-                <FontAwesomeIcon icon={"comment-alt"} />
-            </div>;
+                    opacity: annotated ? 1 : undefined,
+                }}>
+                <FontAwesomeIcon icon={'comment-alt'} />
+            </div>
+        );
     }
     @computed get sidebarCollection() {
         const renderComponent = (tag: string) => {
-            const ComponentTag = tag === "freeform" ? CollectionFreeFormView : tag === "translation" ? FormattedTextBox : CollectionStackingView;
-            return ComponentTag === CollectionStackingView ?
-                <SidebarAnnos ref={this._sidebarRef}
+            const ComponentTag = tag === 'freeform' ? CollectionFreeFormView : tag === 'translation' ? FormattedTextBox : CollectionStackingView;
+            return ComponentTag === CollectionStackingView ? (
+                <SidebarAnnos
+                    ref={this._sidebarRef}
                     {...this.props}
                     fieldKey={this.fieldKey}
                     rootDoc={this.rootDoc}
@@ -1613,16 +1720,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
-                /> :
+                />
+            ) : (
                 <ComponentTag
-                    {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                     NativeWidth={returnZero}
                     NativeHeight={returnZero}
                     PanelHeight={this.props.PanelHeight}
                     PanelWidth={this.sidebarWidth}
                     xPadding={0}
                     yPadding={0}
-                    scaleField={this.SidebarKey + "-scale"}
+                    scaleField={this.SidebarKey + '-scale'}
                     isAnnotationOverlay={false}
                     select={emptyFunction}
                     scaling={this.sidebarContentScaling}
@@ -1636,48 +1744,54 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     setHeight={this.setSidebarHeight}
                     fitContentsToBox={this.fitContentsToBox}
                     noSidebar={true}
-                    fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
+                    fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`}
+                />
+            );
         };
-        return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.ActiveTool !== InkTool.None ? "-inking" : "")}
-            style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
-            {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
-        </div>;
+        return (
+            <div className={'formattedTextBox-sidebar' + (CurrentUserUtils.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
+            </div>
+        );
     }
     render() {
         TraceMobx();
         const selected = this.props.isSelected() || this.Document.forceActive;
         const active = this.props.isContentActive();
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
-        const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
+        const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
         const interactive = (CurrentUserUtils.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
         const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
-        const selPad = ((selected && !this.layoutDoc._singleLine) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0);
-        const selPaddingClass = selected && !this.layoutDoc._singleLine && paddingY >= 10 ? "-selected" : "";
-        const styleFromString = this.styleFromLayoutString(scale);   // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
-        return (styleFromString?.height === "0px" ? (null) :
-            <div className="formattedTextBox-cont"
+        const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
+        const selPaddingClass = selected && !this.layoutDoc._singleLine && paddingY >= 10 ? '-selected' : '';
+        const styleFromString = this.styleFromLayoutString(scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
+        return styleFromString?.height === '0px' ? null : (
+            <div
+                className="formattedTextBox-cont"
                 onWheel={e => this.props.isContentActive() && e.stopPropagation()}
                 style={{
                     transform: this.props.dontScale ? undefined : `scale(${scale})`,
-                    transformOrigin: this.props.dontScale ? undefined : "top left",
+                    transformOrigin: this.props.dontScale ? undefined : 'top left',
                     width: this.props.dontScale ? undefined : `${100 / scale}%`,
                     height: this.props.dontScale ? undefined : `${100 / scale}%`,
                     // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
-                    ...styleFromString
+                    ...styleFromString,
                 }}>
-                <div className={`formattedTextBox-cont`} ref={this._ref}
+                <div
+                    className={`formattedTextBox-cont`}
+                    ref={this._ref}
                     style={{
-                        overflow: this.autoHeight ? "hidden" : undefined,
-                        height: this.props.height || (this.autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? "max-content" : undefined),
+                        overflow: this.autoHeight ? 'hidden' : undefined,
+                        height: this.props.height || (this.autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
                         background: this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
                         color: this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
                         fontSize: this.props.fontSize ? this.props.fontSize : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
-                        fontWeight: Cast(this.layoutDoc._fontWeight, "string", null) as any,
-                        fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
-                        pointerEvents: interactive ? undefined : "none",
+                        fontWeight: Cast(this.layoutDoc._fontWeight, 'string', null) as any,
+                        fontFamily: StrCast(this.layoutDoc._fontFamily, 'inherit'),
+                        pointerEvents: interactive ? undefined : 'none',
                     }}
                     onContextMenu={this.specificContextMenu}
                     onKeyDown={this.onKeyDown}
@@ -1688,31 +1802,35 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     onPointerUp={this.onPointerUp}
                     onPointerDown={this.onPointerDown}
                     onMouseUp={this.onMouseUp}
-                    onDoubleClick={this.onDoubleClick}
-                >
-                    <div className={`formattedTextBox-outer${selected ? "-selected" : ""}`} ref={this._scrollRef}
+                    onDoubleClick={this.onDoubleClick}>
+                    <div
+                        className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
+                        ref={this._scrollRef}
                         style={{
-                            width: this.props.dontSelectOnLoad ? "100%" : `calc(100% - ${this.sidebarWidthPercent})`,
-                            pointerEvents: !active && !SnappingManager.GetIsDragging() ? "none" : undefined,
-                            overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
+                            width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
+                            pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+                            overflow: this.layoutDoc._singleLine ? 'hidden' : undefined,
                         }}
-                        onScroll={this.onScroll} onDrop={this.ondrop} >
-                        <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selPaddingClass}`} ref={this.createDropTarget}
+                        onScroll={this.onScroll}
+                        onDrop={this.ondrop}>
+                        <div
+                            className={minimal ? 'formattedTextBox-minimal' : `formattedTextBox-inner${rounded}${selPaddingClass}`}
+                            ref={this.createDropTarget}
                             style={{
                                 padding: StrCast(this.layoutDoc._textBoxPadding),
                                 paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
                                 paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
                                 paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
                                 paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
-                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (this.layoutDoc.isLinkButton ? "none" : undefined) : undefined
+                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (this.layoutDoc.isLinkButton ? 'none' : undefined) : undefined,
                             }}
                         />
                     </div>
-                    {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection}
-                    {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || this.Document._singleLine ? (null) : this.sidebarHandle}
-                    {!this.layoutDoc._showAudio ? (null) : this.audioHandle}
+                    {this.props.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
+                    {this.props.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
+                    {!this.layoutDoc._showAudio ? null : this.audioHandle}
                 </div>
-            </div >
+            </div>
         );
     }
 }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 3e673c0b2..bdf59863b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -1,18 +1,25 @@
-import { Mark, ResolvedPos } from "prosemirror-model";
-import { EditorState } from "prosemirror-state";
-import { EditorView } from "prosemirror-view";
-import { Doc } from "../../../../fields/Doc";
-import { DocServer } from "../../../DocServer";
-import { LinkDocPreview } from "../LinkDocPreview";
-import { FormattedTextBox } from "./FormattedTextBox";
+import { Mark, ResolvedPos } from 'prosemirror-model';
+import { EditorState } from 'prosemirror-state';
+import { EditorView } from 'prosemirror-view';
+import { Doc } from '../../../../fields/Doc';
+import { DocServer } from '../../../DocServer';
+import { LinkDocPreview } from '../LinkDocPreview';
+import { FormattedTextBox } from './FormattedTextBox';
 import './FormattedTextBoxComment.scss';
-import { schema } from "./schema_rts";
+import { schema } from './schema_rts';
 
-export function findOtherUserMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.attrs.userid && m.attrs.userid !== Doc.CurrentUserEmail); }
-export function findUserMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.attrs.userid); }
-export function findLinkMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.type === schema.marks.autoLinkAnchor || m.type === schema.marks.linkAnchor); }
-export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) {
-    let before = 0, nbef = rpos.nodeBefore;
+export function findOtherUserMark(marks: readonly Mark[]): Mark | undefined {
+    return marks.find(m => m.attrs.userid && m.attrs.userid !== Doc.CurrentUserEmail);
+}
+export function findUserMark(marks: readonly Mark[]): Mark | undefined {
+    return marks.find(m => m.attrs.userid);
+}
+export function findLinkMark(marks: readonly Mark[]): Mark | undefined {
+    return marks.find(m => m.type === schema.marks.autoLinkAnchor || m.type === schema.marks.linkAnchor);
+}
+export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: readonly Mark[]) => Mark | undefined) {
+    let before = 0,
+        nbef = rpos.nodeBefore;
     while (nbef && finder(nbef.marks)) {
         before += nbef.nodeSize;
         rpos = view.state.doc.resolve(rpos.pos - nbef.nodeSize);
@@ -20,8 +27,9 @@ export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (ma
     }
     return before;
 }
-export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) {
-    let after = 0, naft = rpos.nodeAfter;
+export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: readonly Mark[]) => Mark | undefined) {
+    let after = 0,
+        naft = rpos.nodeAfter;
     while (naft && finder(naft.marks)) {
         after += naft.nodeSize;
         rpos = view.state.doc.resolve(rpos.pos + naft.nodeSize);
@@ -32,7 +40,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
 
 // this view appears when clicking on text that has a hyperlink which is configured to show a preview of its target.
 // this will also display metadata information about text when the view is configured to display things like other people who authored text.
-// 
+//
 export class FormattedTextBoxComment {
     static tooltip: HTMLElement;
     static tooltipText: HTMLElement;
@@ -43,11 +51,11 @@ export class FormattedTextBoxComment {
 
     constructor(view: any) {
         if (!FormattedTextBoxComment.tooltip) {
-            const tooltip = FormattedTextBoxComment.tooltip = document.createElement("div");
-            const tooltipText = FormattedTextBoxComment.tooltipText = document.createElement("div");
-            tooltip.className = "FormattedTextBox-tooltip";
-            tooltipText.className = "FormattedTextBox-tooltipText";
-            tooltip.style.display = "none";
+            const tooltip = (FormattedTextBoxComment.tooltip = document.createElement('div'));
+            const tooltipText = (FormattedTextBoxComment.tooltipText = document.createElement('div'));
+            tooltip.className = 'FormattedTextBox-tooltip';
+            tooltipText.className = 'FormattedTextBox-tooltipText';
+            tooltip.style.display = 'none';
             tooltip.appendChild(tooltipText);
             tooltip.onpointerdown = (e: PointerEvent) => {
                 const { textBox, startUserMarkRegion, endUserMarkRegion, userMark } = FormattedTextBoxComment;
@@ -55,38 +63,47 @@ export class FormattedTextBoxComment {
                 e.stopPropagation();
                 e.preventDefault();
             };
-            document.getElementById("root")?.appendChild(tooltip);
+            document.getElementById('root')?.appendChild(tooltip);
         }
     }
     public static Hide() {
         FormattedTextBoxComment.textBox = undefined;
-        FormattedTextBoxComment.tooltip.style.display = "none";
+        FormattedTextBoxComment.tooltip.style.display = 'none';
     }
     public static saveMarkRegion(textBox: any, start: number, end: number, mark: Mark) {
         FormattedTextBoxComment.textBox = textBox;
         FormattedTextBoxComment.startUserMarkRegion = start;
         FormattedTextBoxComment.endUserMarkRegion = end;
         FormattedTextBoxComment.userMark = mark;
-        FormattedTextBoxComment.tooltip.style.display = "";
+        FormattedTextBoxComment.tooltip.style.display = '';
     }
 
     static showCommentbox(view: EditorView, nbef: number) {
         const state = view.state;
         // These are in screen coordinates
-        const start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
+        const start = view.coordsAtPos(state.selection.from - nbef),
+            end = view.coordsAtPos(state.selection.from - nbef);
         // The box in which the tooltip is positioned, to use as base
-        const box = (document.getElementsByClassName("mainView-container") as any)[0].getBoundingClientRect();
+        const box = (document.getElementsByClassName('mainView-container') as any)[0].getBoundingClientRect();
         // Find a center-ish x position from the selection endpoints (when crossing lines, end may be more to the left)
         const left = Math.max((start.left + end.left) / 2, start.left + 3);
-        FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
-        FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
-        FormattedTextBoxComment.tooltip.style.display = "";
+        FormattedTextBoxComment.tooltip.style.left = left - box.left + 'px';
+        FormattedTextBoxComment.tooltip.style.bottom = box.bottom - start.top + 'px';
+        FormattedTextBoxComment.tooltip.style.display = '';
     }
 
-    static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = "", linkDoc: string = "") {
+    static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = '', linkDoc: string = '') {
         FormattedTextBoxComment.textBox = textBox;
-        if ((hrefs || !lastState?.doc.eq(view.state.doc) || !lastState?.selection.eq(view.state.selection))) {
-            FormattedTextBoxComment.setupPreview(view, textBox, hrefs?.trim().split(" ").filter(h => h), linkDoc);
+        if (hrefs || !lastState?.doc.eq(view.state.doc) || !lastState?.selection.eq(view.state.selection)) {
+            FormattedTextBoxComment.setupPreview(
+                view,
+                textBox,
+                hrefs
+                    ?.trim()
+                    .split(' ')
+                    .filter(h => h),
+                linkDoc
+            );
         }
     }
 
@@ -104,25 +121,27 @@ export class FormattedTextBoxComment {
                 FormattedTextBoxComment.saveMarkRegion(textBox, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
             }
             if (mark && child && ((nbef && naft) || !noselection)) {
-                FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " on " + (new Date(mark.attrs.modified * 1000)).toLocaleString();
+                FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + ' on ' + new Date(mark.attrs.modified * 1000).toLocaleString();
                 FormattedTextBoxComment.showCommentbox(view, nbef);
             } else FormattedTextBoxComment.Hide();
         }
 
-        // this checks if the selection is a hyperlink.  If so, it displays the target doc's text for internal links, and the url of the target for external links. 
+        // this checks if the selection is a hyperlink.  If so, it displays the target doc's text for internal links, and the url of the target for external links.
         if (state.selection.$from && hrefs?.length) {
             const nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
             const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef;
-            nbef && naft && LinkDocPreview.SetLinkInfo({
-                docProps: textBox.props,
-                linkSrc: textBox.rootDoc,
-                linkDoc: linkDoc ? DocServer.GetCachedRefField(linkDoc) as Doc : undefined,
-                location: ((pos) => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))),
-                hrefs,
-                showHeader: true
-            });
+            nbef &&
+                naft &&
+                LinkDocPreview.SetLinkInfo({
+                    docProps: textBox.props,
+                    linkSrc: textBox.rootDoc,
+                    linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined,
+                    location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))),
+                    hrefs,
+                    showHeader: true,
+                });
         }
     }
 
-    destroy() { }
-}
\ No newline at end of file
+    destroy() {}
+}
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index c66cb502e..31552cf1b 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -1,17 +1,17 @@
-import { chainCommands, deleteSelection, exitCode, joinBackward, joinDown, joinUp, lift, newlineInCode, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from "prosemirror-commands";
-import { redo, undo } from "prosemirror-history";
-import { Schema } from "prosemirror-model";
-import { splitListItem, wrapInList } from "prosemirror-schema-list";
-import { EditorState, TextSelection, Transaction } from "prosemirror-state";
-import { liftTarget } from "prosemirror-transform";
-import { AclAugment, AclSelfEdit, Doc } from "../../../../fields/Doc";
-import { GetEffectiveAcl } from "../../../../fields/util";
-import { Utils } from "../../../../Utils";
-import { Docs } from "../../../documents/Documents";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { liftListItem, sinkListItem } from "./prosemirrorPatches.js";
-
-const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
+import { chainCommands, deleteSelection, exitCode, joinBackward, joinDown, joinUp, lift, newlineInCode, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from 'prosemirror-commands';
+import { redo, undo } from 'prosemirror-history';
+import { Schema } from 'prosemirror-model';
+import { splitListItem, wrapInList } from 'prosemirror-schema-list';
+import { EditorState, TextSelection, Transaction } from 'prosemirror-state';
+import { liftTarget } from 'prosemirror-transform';
+import { AclAugment, AclSelfEdit, Doc } from '../../../../fields/Doc';
+import { GetEffectiveAcl } from '../../../../fields/util';
+import { Utils } from '../../../../Utils';
+import { Docs } from '../../../documents/Documents';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { liftListItem, sinkListItem } from './prosemirrorPatches.js';
+
+const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false;
 
 export type KeyMap = { [key: string]: any };
 
@@ -20,12 +20,12 @@ export let updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?:
     tx2.doc.descendants((node: any, offset: any, index: any) => {
         if ((from === undefined || to === undefined || (from <= offset + node.nodeSize && to >= offset)) && (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item)) {
             const path = (tx2.doc.resolve(offset) as any).path;
-            let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0);
+            let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty('type') && c.type === schema.nodes.ordered_list ? 1 : 0), 0);
             if (node.type === schema.nodes.ordered_list) {
                 if (depth === 0 && !assignedMapStyle) mapStyle = node.attrs.mapStyle;
                 depth++;
             }
-            tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle, bulletStyle: depth, }, node.marks);
+            tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle, bulletStyle: depth }, node.marks);
         }
     });
     return tx2;
@@ -45,7 +45,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     const canEdit = (state: any) => {
         switch (GetEffectiveAcl(props.DataDoc)) {
-            case AclAugment: return false;
+            case AclAugment:
+                return false;
             case AclSelfEdit:
                 for (var i = state.selection.from; i < state.selection.to; i++) {
                     const marks = state.doc.resolve(i)?.marks?.();
@@ -58,95 +59,102 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         return true;
     };
 
-    const toggleEditableMark = (mark: any) => (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && toggleMark(mark)(state, dispatch);
+    const toggleEditableMark = (mark: any) => (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && toggleMark(mark)(state, dispatch);
 
     //History commands
-    bind("Mod-z", undo);
-    bind("Shift-Mod-z", redo);
-    !mac && bind("Mod-y", redo);
+    bind('Mod-z', undo);
+    bind('Shift-Mod-z', redo);
+    !mac && bind('Mod-y', redo);
 
     //Commands to modify Mark
-    bind("Mod-b", toggleEditableMark(schema.marks.strong));
-    bind("Mod-B", toggleEditableMark(schema.marks.strong));
+    bind('Mod-b', toggleEditableMark(schema.marks.strong));
+    bind('Mod-B', toggleEditableMark(schema.marks.strong));
 
-    bind("Mod-e", toggleEditableMark(schema.marks.em));
-    bind("Mod-E", toggleEditableMark(schema.marks.em));
+    bind('Mod-e', toggleEditableMark(schema.marks.em));
+    bind('Mod-E', toggleEditableMark(schema.marks.em));
 
-    bind("Mod-*", toggleEditableMark(schema.marks.code));
+    bind('Mod-*', toggleEditableMark(schema.marks.code));
 
-    bind("Mod-u", toggleEditableMark(schema.marks.underline));
-    bind("Mod-U", toggleEditableMark(schema.marks.underline));
+    bind('Mod-u', toggleEditableMark(schema.marks.underline));
+    bind('Mod-U', toggleEditableMark(schema.marks.underline));
 
     //Commands for lists
-    bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any));
+    bind('Ctrl-i', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any));
 
-    bind("Ctrl-Tab", () => props.onKey?.(event, props) ? true : true);
-    bind("Alt-Tab", () => props.onKey?.(event, props) ? true : true);
-    bind("Meta-Tab", () => props.onKey?.(event, props) ? true : true);
-    bind("Meta-Enter", () => props.onKey?.(event, props) ? true : true);
-    bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+    bind('Ctrl-Tab', () => (props.onKey?.(event, props) ? true : true));
+    bind('Alt-Tab', () => (props.onKey?.(event, props) ? true : true));
+    bind('Meta-Tab', () => (props.onKey?.(event, props) ? true : true));
+    bind('Meta-Enter', () => (props.onKey?.(event, props) ? true : true));
+    bind('Tab', (state: EditorState, dispatch: (tx: Transaction) => void) => {
         if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
         const ref = state.selection;
         const range = ref.$from.blockRange(ref.$to);
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
-        if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
-            const tx3 = updateBullets(tx2, schema);
-            marks && tx3.ensureMarks([...marks]);
-            marks && tx3.setStoredMarks([...marks]);
-            dispatch(tx3);
-        })) { // couldn't sink into an existing list, so wrap in a new one
-            const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
-            if (!wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
+        if (
+            !sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
                 const tx3 = updateBullets(tx2, schema);
-                // when promoting to a list, assume list will format things so don't copy the stored marks.
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
                 dispatch(tx3);
-            })) {
-                console.log("bullet promote fail");
+            })
+        ) {
+            // couldn't sink into an existing list, so wrap in a new one
+            const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
+            if (
+                !wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
+                    const tx3 = updateBullets(tx2, schema);
+                    // when promoting to a list, assume list will format things so don't copy the stored marks.
+                    marks && tx3.ensureMarks([...marks]);
+                    marks && tx3.setStoredMarks([...marks]);
+                    dispatch(tx3);
+                })
+            ) {
+                console.log('bullet promote fail');
             }
         }
     });
 
-    bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+    bind('Shift-Tab', (state: EditorState, dispatch: (tx: Transaction) => void) => {
         if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
 
-        if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
-            const tx3 = updateBullets(tx2, schema);
-            marks && tx3.ensureMarks([...marks]);
-            marks && tx3.setStoredMarks([...marks]);
-            dispatch(tx3);
-        })) {
-            console.log("bullet demote fail");
+        if (
+            !liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
+                const tx3 = updateBullets(tx2, schema);
+                marks && tx3.ensureMarks([...marks]);
+                marks && tx3.setStoredMarks([...marks]);
+                dispatch(tx3);
+            })
+        ) {
+            console.log('bullet demote fail');
         }
     });
 
     //Command to create a new Tab with a PDF of all the command shortcuts
-    bind("Mod-/", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        const newDoc = Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 });
-        props.addDocTab(newDoc, "add:right");
+    bind('Mod-/', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+        const newDoc = Docs.Create.PdfDocument(Utils.prepend('/assets/cheat-sheet.pdf'), { _width: 300, _height: 300 });
+        props.addDocTab(newDoc, 'add:right');
     });
 
     //Commands to modify BlockType
-    bind("Ctrl->", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state as any, dispatch as any)));
-    bind("Alt-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state as any, dispatch as any));
-    bind("Shift-Ctrl-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state as any, dispatch as any));
+    bind('Ctrl->', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state && wrapIn(schema.nodes.blockquote)(state as any, dispatch as any)));
+    bind('Alt-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state as any, dispatch as any));
+    bind('Shift-Ctrl-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state as any, dispatch as any));
 
-    bind("Ctrl-m", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() }))));
+    bind('Ctrl-m', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: 'math' + Utils.GenerateGuid() }))));
 
     for (let i = 1; i <= 6; i++) {
-        bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state as any, dispatch as any));
+        bind('Shift-Ctrl-' + i, (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state as any, dispatch as any));
     }
 
     //Command to create a horizontal break line
     const hr = schema.nodes.horizontal_rule;
-    bind("Mod-_", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()));
+    bind('Mod-_', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()));
 
     //Command to unselect all
-    bind("Escape", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+    bind('Escape', (state: EditorState, dispatch: (tx: Transaction) => void) => {
         dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
         (document.activeElement as any).blur?.();
         SelectionManager.DeselectAll();
@@ -158,24 +166,29 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         return tx;
     };
 
-
-    bind("Alt-Enter", () => props.onKey?.(event, props) ? true : true);
-    bind("Ctrl-Enter", () => props.onKey?.(event, props) ? true : true);
+    bind('Alt-Enter', () => (props.onKey?.(event, props) ? true : true));
+    bind('Ctrl-Enter', () => (props.onKey?.(event, props) ? true : true));
 
     // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
-    bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
+    bind('Backspace', (state: EditorState, dispatch: (tx: Transaction) => void) => {
         if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
 
-        if (!deleteSelection(state, (tx: Transaction<S>) => {
-            dispatch(updateBullets(tx, schema));
-        })) {
-            if (!joinBackward(state, (tx: Transaction<S>) => {
+        if (
+            !deleteSelection(state, (tx: Transaction) => {
                 dispatch(updateBullets(tx, schema));
-            })) {
-                if (!selectNodeBackward(state, (tx: Transaction<S>) => {
+            })
+        ) {
+            if (
+                !joinBackward(state, (tx: Transaction) => {
                     dispatch(updateBullets(tx, schema));
-                })) {
+                })
+            ) {
+                if (
+                    !selectNodeBackward(state, (tx: Transaction) => {
+                        dispatch(updateBullets(tx, schema));
+                    })
+                ) {
                     return false;
                 }
             }
@@ -185,8 +198,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     //newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
     //command to break line
-    bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
-
+    bind('Enter', (state: EditorState, dispatch: (tx: Transaction) => void) => {
         if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
 
@@ -200,25 +212,29 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         }
 
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
-        const cr = state.selection.$from.node().textContent.endsWith("\n");
+        const cr = state.selection.$from.node().textContent.endsWith('\n');
         if (cr || !newlineInCode(state, dispatch as any)) {
-            if (!splitListItem(schema.nodes.list_item)(state as any, (tx2: Transaction) => {
-                const tx3 = updateBullets(tx2, schema);
-                marks && tx3.ensureMarks([...marks]);
-                marks && tx3.setStoredMarks([...marks]);
-                dispatch(tx3);
-            })) {
+            if (
+                !splitListItem(schema.nodes.list_item)(state as any, (tx2: Transaction) => {
+                    const tx3 = updateBullets(tx2, schema);
+                    marks && tx3.ensureMarks([...marks]);
+                    marks && tx3.setStoredMarks([...marks]);
+                    dispatch(tx3);
+                })
+            ) {
                 const fromattrs = state.selection.$from.node().attrs;
-                if (!splitBlockKeepMarks(state, (tx3: Transaction) => {
-                    const tonode = tx3.selection.$to.node();
-                    if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) {
-                        const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks);
-                        splitMetadata(marks, tx4);
-                        if (!liftListItem(schema.nodes.list_item)(tx4, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
-                            dispatch(tx4);
-                        }
-                    } else dispatch(tx3.insertText("\r\n"));
-                })) {
+                if (
+                    !splitBlockKeepMarks(state, (tx3: Transaction) => {
+                        const tonode = tx3.selection.$to.node();
+                        if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) {
+                            const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks);
+                            splitMetadata(marks, tx4);
+                            if (!liftListItem(schema.nodes.list_item)(tx4, dispatch as (tx: Transaction) => void)) {
+                                dispatch(tx4);
+                            }
+                        } else dispatch(tx3.insertText('\r\n'));
+                    })
+                ) {
                     return false;
                 }
             }
@@ -227,16 +243,16 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     });
 
     //Command to create a blank space
-    bind("Space", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+    bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => {
         if (!canEdit(state)) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         dispatch(splitMetadata(marks, state.tr));
         return false;
     });
 
-    bind("Alt-ArrowUp", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && joinUp(state, dispatch as any));
-    bind("Alt-ArrowDown", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && joinDown(state, dispatch as any));
-    bind("Mod-BracketLeft", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && lift(state, dispatch as any));
+    bind('Alt-ArrowUp', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinUp(state, dispatch as any));
+    bind('Alt-ArrowDown', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinDown(state, dispatch as any));
+    bind('Mod-BracketLeft', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && lift(state, dispatch as any));
 
     const cmd = chainCommands(exitCode, (state, dispatch) => {
         if (dispatch) {
@@ -246,8 +262,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         return false;
     });
 
-    bind("Shift-Enter", cmd);
+    bind('Shift-Enter', cmd);
 
     return keys;
 }
-
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 98343a261..22ca76b2e 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,36 +1,35 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { lift, wrapIn } from "prosemirror-commands";
-import { Mark, MarkType, Node as ProsNode, ResolvedPos } from "prosemirror-model";
-import { wrapInList } from "prosemirror-schema-list";
-import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
-import { EditorView } from "prosemirror-view";
-import { Doc } from "../../../../fields/Doc";
-import { Cast, StrCast } from "../../../../fields/Types";
-import { DocServer } from "../../../DocServer";
-import { LinkManager } from "../../../util/LinkManager";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
-import { FieldViewProps } from "../FieldView";
-import { FormattedTextBox, FormattedTextBoxProps } from "./FormattedTextBox";
-import { updateBullets } from "./ProsemirrorExampleTransfer";
-import "./RichTextMenu.scss";
-import { schema } from "./schema_rts";
-const { toggleMark } = require("prosemirror-commands");
-
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { lift, wrapIn } from 'prosemirror-commands';
+import { Mark, MarkType, Node as ProsNode, ResolvedPos } from 'prosemirror-model';
+import { wrapInList } from 'prosemirror-schema-list';
+import { EditorState, NodeSelection, TextSelection } from 'prosemirror-state';
+import { EditorView } from 'prosemirror-view';
+import { Doc } from '../../../../fields/Doc';
+import { Cast, StrCast } from '../../../../fields/Types';
+import { DocServer } from '../../../DocServer';
+import { LinkManager } from '../../../util/LinkManager';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { FieldViewProps } from '../FieldView';
+import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox';
+import { updateBullets } from './ProsemirrorExampleTransfer';
+import './RichTextMenu.scss';
+import { schema } from './schema_rts';
+const { toggleMark } = require('prosemirror-commands');
 
 @observer
-export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
+export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable static Instance: RichTextMenu;
     public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
     private _linkToRef = React.createRef<HTMLInputElement>();
 
     @observable public view?: EditorView;
-    public editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
+    public editorProps: (FieldViewProps & FormattedTextBoxProps) | undefined;
 
     public _brushMap: Map<string, Set<Mark>> = new Map();
 
@@ -43,21 +42,21 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     @observable private _subscriptActive: boolean = false;
     @observable private _superscriptActive: boolean = false;
 
-    @observable private _activeFontSize: string = "13px";
-    @observable private _activeFontFamily: string = "";
-    @observable private activeListType: string = "";
-    @observable private _activeAlignment: string = "left";
+    @observable private _activeFontSize: string = '13px';
+    @observable private _activeFontFamily: string = '';
+    @observable private activeListType: string = '';
+    @observable private _activeAlignment: string = 'left';
 
     @observable private brushMarks: Set<Mark> = new Set();
     @observable private showBrushDropdown: boolean = false;
 
-    @observable private _activeFontColor: string = "black";
+    @observable private _activeFontColor: string = 'black';
     @observable private showColorDropdown: boolean = false;
 
-    @observable private activeHighlightColor: string = "transparent";
+    @observable private activeHighlightColor: string = 'transparent';
     @observable private showHighlightDropdown: boolean = false;
 
-    @observable private currentLink: string | undefined = "";
+    @observable private currentLink: string | undefined = '';
     @observable private showLinkDropdown: boolean = false;
 
     _reaction: IReactionDisposer | undefined;
@@ -72,24 +71,44 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     }
 
     componentDidMount() {
-        this._reaction = reaction(() => SelectionManager.Views(),
-            () => this._delayHide && !(this._delayHide = false) && this.fadeOut(true));
+        this._reaction = reaction(
+            () => SelectionManager.Views(),
+            () => this._delayHide && !(this._delayHide = false) && this.fadeOut(true)
+        );
     }
     componentWillUnmount() {
         this._reaction?.();
     }
 
-    @computed get noAutoLink() { return this._noLinkActive; }
-    @computed get bold() { return this._boldActive; }
-    @computed get underline() { return this._underlineActive; }
-    @computed get italics() { return this._italicsActive; }
-    @computed get strikeThrough() { return this._strikethroughActive; }
-    @computed get fontColor() { return this._activeFontColor; }
-    @computed get fontFamily() { return this._activeFontFamily; }
-    @computed get fontSize() { return this._activeFontSize; }
-    @computed get textAlign() { return this._activeAlignment; }
+    @computed get noAutoLink() {
+        return this._noLinkActive;
+    }
+    @computed get bold() {
+        return this._boldActive;
+    }
+    @computed get underline() {
+        return this._underlineActive;
+    }
+    @computed get italics() {
+        return this._italicsActive;
+    }
+    @computed get strikeThrough() {
+        return this._strikethroughActive;
+    }
+    @computed get fontColor() {
+        return this._activeFontColor;
+    }
+    @computed get fontFamily() {
+        return this._activeFontFamily;
+    }
+    @computed get fontSize() {
+        return this._activeFontSize;
+    }
+    @computed get textAlign() {
+        return this._activeAlignment;
+    }
 
-    public delayHide = () => this._delayHide = true;
+    public delayHide = () => (this._delayHide = true);
 
     @action
     public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any) {
@@ -118,16 +137,16 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
         this.activeListType = this.getActiveListStyle();
         this._activeAlignment = this.getActiveAlignment();
-        this._activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
-        this._activeFontSize = !activeSizes.length ? StrCast(this.TextView.Document.fontSize, StrCast(Doc.UserDoc().fontSize, "10px")) : activeSizes[0];
-        this._activeFontColor = !activeColors.length ? "black" : activeColors.length > 0 ? String(activeColors[0]) : "...";
-        this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length > 0 ? String(activeHighlights[0]) : "...";
+        this._activeFontFamily = !activeFamilies.length ? 'Arial' : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
+        this._activeFontSize = !activeSizes.length ? StrCast(this.TextView.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0];
+        this._activeFontColor = !activeColors.length ? 'black' : activeColors.length > 0 ? String(activeColors[0]) : '...';
+        this.activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
 
         // update link in current selection
         this.getTextLinkTargetTitle().then(targetTitle => this.setCurrentLink(targetTitle));
     }
 
-    setMark = (mark: Mark, state: EditorState<any>, dispatch: any, dontToggle: boolean = false) => {
+    setMark = (mark: Mark, state: EditorState, dispatch: any, dontToggle: boolean = false) => {
         if (mark) {
             const node = (state.selection as NodeSelection).node;
             if (node?.type === schema.nodes.ordered_list) {
@@ -140,7 +159,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             } else if (dontToggle) {
                 toggleMark(mark.type, mark.attrs)(state, (tx: any) => {
                     const { from, $from, to, empty } = tx.selection;
-                    if (!tx.doc.rangeHasMark(from, to, mark.type)) { // hack -- should have just set the mark in the first place
+                    if (!tx.doc.rangeHasMark(from, to, mark.type)) {
+                        // hack -- should have just set the mark in the first place
                         toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
                     } else dispatch(tx);
                 });
@@ -148,7 +168,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                 toggleMark(mark.type, mark.attrs)(state, dispatch);
             }
         }
-    }
+    };
 
     // finds font sizes and families in selection
     getActiveAlignment() {
@@ -156,11 +176,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             const path = (this.view.state.selection.$from as any).path;
             for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) {
                 if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) {
-                    return path[i].attrs.align || "left";
+                    return path[i].attrs.align || 'left';
                 }
             }
         }
-        return "left";
+        return 'left';
     }
 
     // finds font sizes and families in selection
@@ -176,7 +196,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                 return this.view.state.selection.$from.nodeAfter?.attrs.mapStyle;
             }
         }
-        return "";
+        return '';
     }
 
     // finds font sizes and families in selection
@@ -190,7 +210,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         if (this.TextView.props.isSelected(true)) {
             const state = this.view.state;
             const pos = this.view.state.selection.$from;
-            const marks: Mark<any>[] = [...(state.storedMarks ?? [])];
+            const marks: Mark[] = [...(state.storedMarks ?? [])];
             if (state.selection.empty) {
                 const ref_node = this.reference_node(pos);
                 marks.push(...(ref_node !== this.view.state.doc && ref_node?.isText ? Array.from(ref_node.marks) : []));
@@ -209,10 +229,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         return { activeFamilies, activeSizes, activeColors, activeHighlights };
     }
 
-    getMarksInSelection(state: EditorState<any>) {
+    getMarksInSelection(state: EditorState) {
         const found = new Set<Mark>();
         const { from, to } = state.selection as TextSelection;
-        state.doc.nodesBetween(from, to, (node) => node.marks.forEach(m => found.add(m)));
+        state.doc.nodesBetween(from, to, node => node.marks.forEach(m => found.add(m)));
         return found;
     }
 
@@ -234,14 +254,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                 }
                 return false;
             });
-        }
-        else {
+        } else {
             const pos = this.view.state.selection.$from;
             const ref_node: ProsNode | null = this.reference_node(pos);
             if (ref_node !== null && ref_node !== this.view.state.doc) {
                 if (ref_node.isText) {
-                }
-                else {
+                } else {
                     return [];
                 }
                 activeMarks = markGroup.filter(mark_type => {
@@ -275,13 +293,27 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
         activeMarks.forEach(mark => {
             switch (mark.name) {
-                case "noAutoLinkAnchor": this._noLinkActive = true; break;
-                case "strong": this._boldActive = true; break;
-                case "em": this._italicsActive = true; break;
-                case "underline": this._underlineActive = true; break;
-                case "strikethrough": this._strikethroughActive = true; break;
-                case "subscript": this._subscriptActive = true; break;
-                case "superscript": this._superscriptActive = true; break;
+                case 'noAutoLinkAnchor':
+                    this._noLinkActive = true;
+                    break;
+                case 'strong':
+                    this._boldActive = true;
+                    break;
+                case 'em':
+                    this._italicsActive = true;
+                    break;
+                case 'underline':
+                    this._underlineActive = true;
+                    break;
+                case 'strikethrough':
+                    this._strikethroughActive = true;
+                    break;
+                case 'subscript':
+                    this._subscriptActive = true;
+                    break;
+                case 'superscript':
+                    this._superscriptActive = true;
+                    break;
             }
         });
     }
@@ -293,14 +325,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             this.TextView.autoLink();
             this.view.focus();
         }
-    }
+    };
     toggleBold = () => {
         if (this.view) {
             const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
             this.setMark(mark, this.view.state, this.view.dispatch, false);
             this.view.focus();
         }
-    }
+    };
 
     toggleUnderline = () => {
         if (this.view) {
@@ -308,7 +340,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             this.setMark(mark, this.view.state, this.view.dispatch, false);
             this.view.focus();
         }
-    }
+    };
 
     toggleItalics = () => {
         if (this.view) {
@@ -316,13 +348,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             this.setMark(mark, this.view.state, this.view.dispatch, false);
             this.view.focus();
         }
-    }
-
+    };
 
     setFontSize = (fontSize: string) => {
         if (this.view) {
-            if (this.view.state.selection.from === 1 && this.view.state.selection.empty &&
-                (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === fontSize))) {
+            if (this.view.state.selection.from === 1 && this.view.state.selection.empty && (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === fontSize))) {
                 this.TextView.dataDoc.fontSize = fontSize;
                 this.view.focus();
                 this.updateMenu(this.view, undefined, this.props);
@@ -333,7 +363,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                 this.updateMenu(this.view, undefined, this.props);
             }
         }
-    }
+    };
 
     setFontFamily = (family: string) => {
         if (this.view) {
@@ -342,7 +372,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             this.view.focus();
             this.updateMenu(this.view, undefined, this.props);
         }
-    }
+    };
 
     setHighlight(color: String, view: EditorView, dispatch: any) {
         const highlightMark = view.state.schema.mark(view.state.schema.marks.marker, { highlight: color });
@@ -362,8 +392,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
     // TODO: remove doesn't work
     // remove all node type and apply the passed-in one to the selected text
-    changeListType = (nodeType: Node | undefined) => {
-        if (!this.view || (nodeType as any)?.attrs.mapStyle === "") return;
+    changeListType = (mapStyle: string) => {
+        const active = this.view?.state && RichTextMenu.Instance.getActiveListStyle();
+        const nodeType = this.view?.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle });
+        if (!this.view || nodeType?.attrs.mapStyle === '') return;
 
         const nextIsOL = this.view.state.selection.$from.nodeAfter?.type === schema.nodes.ordered_list;
         let inList: any = undefined;
@@ -377,17 +409,19 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         }
 
         const marks = this.view.state.storedMarks || (this.view.state.selection.$to.parentOffset && this.view.state.selection.$from.marks());
-        if (inList || !wrapInList(schema.nodes.ordered_list)(this.view.state, (tx2: any) => {
-            const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, this.view!.state.selection.from - 1, this.view!.state.selection.to + 1);
-            marks && tx3.ensureMarks([...marks]);
-            marks && tx3.setStoredMarks([...marks]);
+        if (
+            inList ||
+            !wrapInList(schema.nodes.ordered_list)(this.view.state, (tx2: any) => {
+                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, this.view!.state.selection.from - 1, this.view!.state.selection.to + 1);
+                marks && tx3.ensureMarks([...marks]);
+                marks && tx3.setStoredMarks([...marks]);
 
-            this.view!.dispatch(tx2);
-        })) {
+                this.view!.dispatch(tx2);
+            })
+        ) {
             const tx2 = this.view.state.tr;
             if (nodeType && (inList || nextIsOL)) {
-                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, inList ? fromList : this.view.state.selection.from,
-                    inList ? fromList + inList.nodeSize : this.view.state.selection.to);
+                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, inList ? fromList : this.view.state.selection.from, inList ? fromList + inList.nodeSize : this.view.state.selection.to);
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
                 this.view.dispatch(tx3);
@@ -395,9 +429,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         }
         this.view.focus();
         this.updateMenu(this.view, undefined, this.props);
-    }
+    };
 
-    insertSummarizer(state: EditorState<any>, dispatch: any) {
+    insertSummarizer(state: EditorState, dispatch: any) {
         if (state.selection.empty) return false;
         const mark = state.schema.marks.summarize.create();
         const tr = state.tr;
@@ -408,7 +442,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         return true;
     }
 
-    align = (view: EditorView, dispatch: any, alignment: "left" | "right" | "center") => {
+    align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => {
         if (this.TextView.props.isSelected(true)) {
             var tr = view.state.tr;
             view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => {
@@ -422,9 +456,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             view.focus();
             dispatch?.(tr);
         }
-    }
+    };
 
-    insetParagraph(state: EditorState<any>, dispatch: any) {
+    insetParagraph(state: EditorState, dispatch: any) {
         var tr = state.tr;
         state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
             if (node.type === schema.nodes.paragraph || node.type === schema.nodes.heading) {
@@ -437,7 +471,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         dispatch?.(tr);
         return true;
     }
-    outsetParagraph(state: EditorState<any>, dispatch: any) {
+    outsetParagraph(state: EditorState, dispatch: any) {
         var tr = state.tr;
         state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
             if (node.type === schema.nodes.paragraph || node.type === schema.nodes.heading) {
@@ -451,7 +485,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         return true;
     }
 
-    indentParagraph(state: EditorState<any>, dispatch: any) {
+    indentParagraph(state: EditorState, dispatch: any) {
         var tr = state.tr;
         const heading = false;
         state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
@@ -467,7 +501,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         return true;
     }
 
-    hangingIndentParagraph(state: EditorState<any>, dispatch: any) {
+    hangingIndentParagraph(state: EditorState, dispatch: any) {
         var tr = state.tr;
         state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
             if (node.type === schema.nodes.paragraph || node.type === schema.nodes.heading) {
@@ -482,7 +516,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         return true;
     }
 
-    insertBlockquote(state: EditorState<any>, dispatch: any) {
+    insertBlockquote(state: EditorState, dispatch: any) {
         const path = (state.selection.$from as any).path;
         if (path.length > 6 && path[path.length - 6].type === schema.nodes.blockquote) {
             lift(state, dispatch);
@@ -492,20 +526,22 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         return true;
     }
 
-    insertHorizontalRule(state: EditorState<any>, dispatch: any) {
+    insertHorizontalRule(state: EditorState, dispatch: any) {
         dispatch(state.tr.replaceSelectionWith(state.schema.nodes.horizontal_rule.create()).scrollIntoView());
         return true;
     }
 
-    @action toggleBrushDropdown() { this.showBrushDropdown = !this.showBrushDropdown; }
+    @action toggleBrushDropdown() {
+        this.showBrushDropdown = !this.showBrushDropdown;
+    }
 
     // todo: add brushes to brushMap to save with a style name
     onBrushNameKeyPress = (e: React.KeyboardEvent) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             RichTextMenu.Instance.brushMarks && RichTextMenu.Instance._brushMap.set(this._brushNameRef.current!.value, RichTextMenu.Instance.brushMarks);
-            this._brushNameRef.current!.style.background = "lightGray";
+            this._brushNameRef.current!.style.background = 'lightGray';
         }
-    }
+    };
     _brushNameRef = React.createRef<HTMLInputElement>();
 
     @action
@@ -514,7 +550,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     }
 
     @action
-    fillBrush(state: EditorState<any>, dispatch: any) {
+    fillBrush(state: EditorState, dispatch: any) {
         if (!this.view) return;
 
         if (!Array.from(this.brushMarks.keys()).length) {
@@ -522,68 +558,81 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             if (selected_marks.size >= 0) {
                 this.brushMarks = selected_marks;
             }
-        }
-        else {
+        } else {
             const { from, to, $from } = this.view.state.selection;
             if (!this.view.state.selection.empty && $from && $from.nodeAfter) {
                 if (to - from > 0) {
                     this.view.dispatch(this.view.state.tr.removeMark(from, to));
-                    Array.from(this.brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => {
-                        this.setMark(mark, this.view!.state, this.view!.dispatch);
-                    });
+                    Array.from(this.brushMarks)
+                        .filter(m => m.type !== schema.marks.user_mark)
+                        .forEach((mark: Mark) => {
+                            this.setMark(mark, this.view!.state, this.view!.dispatch);
+                        });
                 }
             }
         }
     }
 
-    get TextView() { return (this.view as any)?.TextView as FormattedTextBox; }
-    get TextViewFieldKey() { return this.TextView?.props.fieldKey; }
-
-
-
-    @action setActiveHighlight(color: string) { this.activeHighlightColor = color; }
+    get TextView() {
+        return (this.view as any)?.TextView as FormattedTextBox;
+    }
+    get TextViewFieldKey() {
+        return this.TextView?.props.fieldKey;
+    }
 
+    @action setActiveHighlight(color: string) {
+        this.activeHighlightColor = color;
+    }
 
-    @action setCurrentLink(link: string) { this.currentLink = link; }
+    @action setCurrentLink(link: string) {
+        this.currentLink = link;
+    }
 
     createLinkButton() {
         const self = this;
 
         function onLinkChange(e: React.ChangeEvent<HTMLInputElement>) {
             self.TextView?.endUndoTypingBatch();
-            UndoManager.RunInBatch(() => self.setCurrentLink(e.target.value), "link change");
+            UndoManager.RunInBatch(() => self.setCurrentLink(e.target.value), 'link change');
         }
 
-        const link = this.currentLink ? this.currentLink : "";
+        const link = this.currentLink ? this.currentLink : '';
 
-        const button = <Tooltip title={<div className="dash-tooltip">set hyperlink</div>} placement="bottom">
-            <button className="antimodeMenu-button color-preview-button">
-                <FontAwesomeIcon icon="link" size="lg" />
-            </button>
-        </Tooltip>;
+        const button = (
+            <Tooltip title={<div className="dash-tooltip">set hyperlink</div>} placement="bottom">
+                <button className="antimodeMenu-button color-preview-button">
+                    <FontAwesomeIcon icon="link" size="lg" />
+                </button>
+            </Tooltip>
+        );
 
-        const dropdownContent =
+        const dropdownContent = (
             <div className="dropdown link-menu">
                 <p>Linked to:</p>
                 <input value={link} ref={this._linkToRef} placeholder="Enter URL" onChange={onLinkChange} />
-                <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "add:right")}>Apply hyperlink</button>
+                <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, 'add:right')}>
+                    Apply hyperlink
+                </button>
                 <div className="divider" />
-                <button className="remove-button" onPointerDown={e => this.deleteLink()}>Remove link</button>
-            </div>;
+                <button className="remove-button" onPointerDown={e => this.deleteLink()}>
+                    Remove link
+                </button>
+            </div>
+        );
 
-        return <ButtonDropdown view={this.view} key={"link button"} button={button} dropdownContent={dropdownContent} openDropdownOnButton={true} link={true} />;
+        return <ButtonDropdown view={this.view} key={'link button'} button={button} dropdownContent={dropdownContent} openDropdownOnButton={true} link={true} />;
     }
 
     async getTextLinkTargetTitle() {
         if (!this.view) return;
 
         const node = this.view.state.selection.$from.nodeAfter;
-        const link = node && node.marks.find(m => m.type.name === "link");
+        const link = node && node.marks.find(m => m.type.name === 'link');
         if (link) {
             const href = link.attrs.allAnchors.length > 0 ? link.attrs.allAnchors[0].href : undefined;
             if (href) {
                 if (href.indexOf(Doc.localServerPath()) === 0) {
-                    const linkclicked = href.replace(Doc.localServerPath(), "").split("?")[0];
+                    const linkclicked = href.replace(Doc.localServerPath(), '').split('?')[0];
                     if (linkclicked) {
                         const linkDoc = await DocServer.GetRefField(linkclicked);
                         if (linkDoc instanceof Doc) {
@@ -612,8 +661,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     // TODO: should check for valid URL
     @undoBatch
     makeLinkToURL = (target: string, lcoation: string) => {
-        ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target);
-    }
+        ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, 'onRadd:rightight', target, target);
+    };
 
     @undoBatch
     @action
@@ -624,13 +673,15 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                 const allAnchors = linkAnchor.attrs.allAnchors.slice();
                 this.TextView.RemoveAnchorFromSelection(allAnchors);
                 // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected.
-                allAnchors.filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0).forEach((aref: any) => {
-                    const anchorId = aref.href.replace(Doc.localServerPath(), "").split("?")[0];
-                    anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc));
-                });
+                allAnchors
+                    .filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0)
+                    .forEach((aref: any) => {
+                        const anchorId = aref.href.replace(Doc.localServerPath(), '').split('?')[0];
+                        anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc));
+                    });
             }
         }
-    }
+    };
 
     linkExtend($start: ResolvedPos, href: string) {
         const mark = this.view!.state.schema.marks.linkAnchor;
@@ -651,7 +702,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         return { from: startPos, to: endPos };
     }
 
-    reference_node(pos: ResolvedPos<any>): ProsNode | null {
+    reference_node(pos: ResolvedPos): ProsNode | null {
         if (!this.view) return null;
 
         let ref_node: ProsNode = this.view.state.doc;
@@ -671,7 +722,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                         ref_node = node;
                         skip = true;
                     }
-
                 });
             }
         }
@@ -755,21 +805,19 @@ interface ButtonDropdownProps {
     openDropdownOnButton?: boolean;
     link?: boolean;
     pdf?: boolean;
-
 }
 
 @observer
 export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
-
     @observable private showDropdown: boolean = false;
     private ref: HTMLDivElement | null = null;
 
     componentDidMount() {
-        document.addEventListener("pointerdown", this.onBlur);
+        document.addEventListener('pointerdown', this.onBlur);
     }
 
     componentWillUnmount() {
-        document.removeEventListener("pointerdown", this.onBlur);
+        document.removeEventListener('pointerdown', this.onBlur);
     }
 
     @action
@@ -785,7 +833,7 @@ export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
         e.preventDefault();
         e.stopPropagation();
         this.toggleDropdown();
-    }
+    };
 
     onBlur = (e: PointerEvent) => {
         setTimeout(() => {
@@ -793,37 +841,40 @@ export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
                 this.setShowDropdown(false);
             }
         }, 0);
-    }
-
+    };
 
     render() {
         return (
-            <div className="button-dropdown-wrapper" ref={node => this.ref = node}>
-                {!this.props.pdf ?
+            <div className="button-dropdown-wrapper" ref={node => (this.ref = node)}>
+                {!this.props.pdf ? (
                     <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.props.openDropdownOnButton ? this.onDropdownClick : undefined}>
                         {this.props.button}
-                        <div style={{ marginTop: "-8.5", position: "relative" }} onPointerDown={!this.props.openDropdownOnButton ? this.onDropdownClick : undefined}>
+                        <div style={{ marginTop: '-8.5', position: 'relative' }} onPointerDown={!this.props.openDropdownOnButton ? this.onDropdownClick : undefined}>
                             <FontAwesomeIcon icon="caret-down" size="sm" />
                         </div>
                     </div>
-                    :
+                ) : (
                     <>
                         {this.props.button}
                         <button className="dropdown-button antimodeMenu-button" key="antimodebutton" onPointerDown={this.onDropdownClick}>
                             <FontAwesomeIcon icon="caret-down" size="sm" />
                         </button>
-                    </>}
-                {this.showDropdown ? this.props.dropdownContent : (null)}
+                    </>
+                )}
+                {this.showDropdown ? this.props.dropdownContent : null}
             </div>
         );
     }
 }
 
-
 interface RichTextMenuPluginProps {
     editorProps: any;
 }
 export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps> {
-    render() { return null; }
-    update(view: EditorView, lastState: EditorState | undefined) { RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps); }
-}
\ No newline at end of file
+    render() {
+        return null;
+    }
+    update(view: EditorView, lastState: EditorState | undefined) {
+        RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps);
+    }
+}
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 8851d52e4..1916b94bf 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -1,17 +1,17 @@
-import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from "prosemirror-inputrules";
-import { NodeSelection, TextSelection } from "prosemirror-state";
-import { DataSym, Doc } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { ComputedField } from "../../../../fields/ScriptField";
-import { NumCast, StrCast } from "../../../../fields/Types";
-import { normalizeEmail } from "../../../../fields/util";
-import { returnFalse, Utils } from "../../../../Utils";
-import { DocServer } from "../../../DocServer";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { FormattedTextBox } from "./FormattedTextBox";
-import { wrappingInputRule } from "./prosemirrorPatches";
-import { RichTextMenu } from "./RichTextMenu";
-import { schema } from "./schema_rts";
+import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from 'prosemirror-inputrules';
+import { NodeSelection, TextSelection } from 'prosemirror-state';
+import { DataSym, Doc } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { ComputedField } from '../../../../fields/ScriptField';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { normalizeEmail } from '../../../../fields/util';
+import { returnFalse, Utils } from '../../../../Utils';
+import { DocServer } from '../../../DocServer';
+import { Docs, DocUtils } from '../../../documents/Documents';
+import { FormattedTextBox } from './FormattedTextBox';
+import { wrappingInputRule } from './prosemirrorPatches';
+import { RichTextMenu } from './RichTextMenu';
+import { schema } from './schema_rts';
 
 export class RichTextRules {
     public Document: Doc;
@@ -34,9 +34,9 @@ export class RichTextRules {
             wrappingInputRule(
                 /^1\.\s$/,
                 schema.nodes.ordered_list,
-                () => ({ mapStyle: "decimal", bulletStyle: 1 }),
+                () => ({ mapStyle: 'decimal', bulletStyle: 1 }),
                 (match: any, node: any) => node.childCount + node.attrs.order === +match[1],
-                ((type: any) => ({ type: type, attrs: { mapStyle: "decimal", bulletStyle: 1 } })) as any
+                ((type: any) => ({ type: type, attrs: { mapStyle: 'decimal', bulletStyle: 1 } })) as any
             ),
 
             // A. create alphabetical ordered list
@@ -45,360 +45,324 @@ export class RichTextRules {
                 schema.nodes.ordered_list,
                 // match => {
                 () => {
-                    return ({ mapStyle: "multi", bulletStyle: 1 });
+                    return { mapStyle: 'multi', bulletStyle: 1 };
                     // return ({ order: +match[1] })
                 },
                 (match: any, node: any) => {
                     return node.childCount + node.attrs.order === +match[1];
                 },
-                ((type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } })) as any
+                ((type: any) => ({ type: type, attrs: { mapStyle: 'multi', bulletStyle: 1 } })) as any
             ),
 
             // * + - create bullet list
-            wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.ordered_list,
+            wrappingInputRule(
+                /^\s*([-+*])\s$/,
+                schema.nodes.ordered_list,
                 // match => {
-                () => ({ mapStyle: "bullet" }),  //  ({ order: +match[1] })
+                () => ({ mapStyle: 'bullet' }), //  ({ order: +match[1] })
                 (match: any, node: any) => node.childCount + node.attrs.order === +match[1],
-                ((type: any) => ({ type: type, attrs: { mapStyle: "bullet" } })) as any
+                ((type: any) => ({ type: type, attrs: { mapStyle: 'bullet' } })) as any
             ),
 
             // ``` create code block
             textblockTypeInputRule(/^```$/, schema.nodes.code_block),
 
-            // %<font-size> set the font size  
-            new InputRule(
-                new RegExp(/%([0-9]+)\s$/),
-                (state, match, start, end) => {
-                    const size = Number(match[1]);
-                    return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size }));
-                }),
+            // %<font-size> set the font size
+            new InputRule(new RegExp(/%([0-9]+)\s$/), (state, match, start, end) => {
+                const size = Number(match[1]);
+                return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size }));
+            }),
 
             //Create annotation to a field on the text document
-            new InputRule(
-                new RegExp(/>>$/),
-                (state, match, start, end) => {
-                    const textDoc = this.Document[DataSym];
-                    const numInlines = NumCast(textDoc.inlineTextCount);
-                    textDoc.inlineTextCount = numInlines + 1;
-                    const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to
-                    const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation
-                    const textDocInline = Docs.Create.TextDocument("", { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9px", title: "inline comment" });
-                    textDocInline.title = inlineFieldKey; // give the annotation its own title
-                    textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
-                    textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
-                    textDocInline.proto = textDoc;  // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
-                    textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
-                    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 sm = state.storedMarks || undefined;
-                    const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).insertText(" ", start + 2).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
-                        state.tr;
-                    return replaced;
-                }),
-
+            new InputRule(new RegExp(/>>$/), (state, match, start, end) => {
+                const textDoc = this.Document[DataSym];
+                const numInlines = NumCast(textDoc.inlineTextCount);
+                textDoc.inlineTextCount = numInlines + 1;
+                const inlineFieldKey = 'inline' + numInlines; // which field on the text document this annotation will write to
+                const inlineLayoutKey = 'layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
+                const textDocInline = Docs.Create.TextDocument('', { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: '9px', title: 'inline comment' });
+                textDocInline.title = inlineFieldKey; // give the annotation its own title
+                textDocInline['title-custom'] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
+                textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
+                textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
+                textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
+                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 sm = state.storedMarks || undefined;
+                const replaced = node
+                    ? state.tr
+                          .insert(start, newNode)
+                          .replaceRangeWith(start + 1, end + 1, dashDoc)
+                          .insertText(' ', start + 2)
+                          .setStoredMarks([...node.marks, ...(sm ? sm : [])])
+                    : state.tr;
+                return replaced;
+            }),
 
             // set the First-line indent node type for the selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
-            new InputRule(
-                new RegExp(/(%d|d)$/),
-                (state, match, start, end) => {
-                    if (!match[0].startsWith("%") && !this.EnteringStyle) return null;
-                    const pos = (state.doc.resolve(start) as any);
-                    for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
-                        const node = pos.node(depth);
-                        if (node.type === schema.nodes.paragraph) {
-                            const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, indent: node.attrs.indent === 25 ? undefined : 25 });
-                            const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
-                            return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
-                        }
+            new InputRule(new RegExp(/(%d|d)$/), (state, match, start, end) => {
+                if (!match[0].startsWith('%') && !this.EnteringStyle) return null;
+                const pos = state.doc.resolve(start) as any;
+                for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+                    const node = pos.node(depth);
+                    if (node.type === schema.nodes.paragraph) {
+                        const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, indent: node.attrs.indent === 25 ? undefined : 25 });
+                        const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+                        return match[0].startsWith('%') ? result.deleteRange(start, end) : result;
                     }
-                    return null;
-                }),
+                }
+                return null;
+            }),
 
             // set the Hanging indent node type for the current selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
-            new InputRule(
-                new RegExp(/(%h|h)$/),
-                (state, match, start, end) => {
-                    if (!match[0].startsWith("%") && !this.EnteringStyle) return null;
-                    const pos = (state.doc.resolve(start) as any);
-                    for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
-                        const node = pos.node(depth);
-                        if (node.type === schema.nodes.paragraph) {
-                            const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, indent: node.attrs.indent === -25 ? undefined : -25 });
-                            const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
-                            return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
-                        }
+            new InputRule(new RegExp(/(%h|h)$/), (state, match, start, end) => {
+                if (!match[0].startsWith('%') && !this.EnteringStyle) return null;
+                const pos = state.doc.resolve(start) as any;
+                for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+                    const node = pos.node(depth);
+                    if (node.type === schema.nodes.paragraph) {
+                        const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, indent: node.attrs.indent === -25 ? undefined : -25 });
+                        const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+                        return match[0].startsWith('%') ? result.deleteRange(start, end) : result;
                     }
-                    return null;
-                }),
+                }
+                return null;
+            }),
 
             // set the Quoted indent node type for the current selection's paragraph (assumes % was used to initiate an EnteringStyle mode)
-            new InputRule(
-                new RegExp(/(%q|q)$/),
-                (state, match, start, end) => {
-                    if (!match[0].startsWith("%") && !this.EnteringStyle) return null;
-                    const pos = (state.doc.resolve(start) as any);
-                    if (state.selection instanceof NodeSelection && state.selection.node.type === schema.nodes.ordered_list) {
-                        const node = state.selection.node;
-                        return state.tr.setNodeMarkup(pos.pos, node.type, { ...node.attrs, indent: node.attrs.indent === 30 ? undefined : 30 });
-                    }
-                    for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
-                        const node = pos.node(depth);
-                        if (node.type === schema.nodes.paragraph) {
-                            const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, inset: node.attrs.inset === 30 ? undefined : 30 });
-                            const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
-                            return match[0].startsWith("%") ? result.deleteRange(start, end) : result;
-                        }
+            new InputRule(new RegExp(/(%q|q)$/), (state, match, start, end) => {
+                if (!match[0].startsWith('%') && !this.EnteringStyle) return null;
+                const pos = state.doc.resolve(start) as any;
+                if (state.selection instanceof NodeSelection && state.selection.node.type === schema.nodes.ordered_list) {
+                    const node = state.selection.node;
+                    return state.tr.setNodeMarkup(pos.pos, node.type, { ...node.attrs, indent: node.attrs.indent === 30 ? undefined : 30 });
+                }
+                for (let depth = pos.path.length / 3 - 1; depth >= 0; depth--) {
+                    const node = pos.node(depth);
+                    if (node.type === schema.nodes.paragraph) {
+                        const replaced = state.tr.setNodeMarkup(pos.pos - pos.parentOffset - 1, node.type, { ...node.attrs, inset: node.attrs.inset === 30 ? undefined : 30 });
+                        const result = replaced.setSelection(new TextSelection(replaced.doc.resolve(start)));
+                        return match[0].startsWith('%') ? result.deleteRange(start, end) : result;
                     }
-                    return null;
-                }),
+                }
+                return null;
+            }),
 
             // center justify text
-            new InputRule(
-                new RegExp(/%\^/),
-                (state, match, start, end) => {
-                    const resolved = state.doc.resolve(start) as any;
-                    if (resolved?.parent.type.name === "paragraph") {
-                        return state.tr.deleteRange(start, end).setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: "center" }, resolved.parent.marks);
-                    } else {
-                        const node = resolved.nodeAfter;
-                        const sm = state.storedMarks || undefined;
-                        const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
-                            state.tr;
-                        return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
-                    }
-                }),
+            new InputRule(new RegExp(/%\^/), (state, match, start, end) => {
+                const resolved = state.doc.resolve(start) as any;
+                if (resolved?.parent.type.name === 'paragraph') {
+                    return state.tr.deleteRange(start, end).setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'center' }, resolved.parent.marks);
+                } else {
+                    const node = resolved.nodeAfter;
+                    const sm = state.storedMarks || undefined;
+                    const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: 'center' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
+                    return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
+                }
+            }),
 
             // left justify text
-            new InputRule(
-                new RegExp(/%\[/),
-                (state, match, start, end) => {
-                    const resolved = state.doc.resolve(start) as any;
-                    if (resolved?.parent.type.name === "paragraph") {
-                        return state.tr.deleteRange(start, end).setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: "left" }, resolved.parent.marks);
-                    } else {
-                        const node = resolved.nodeAfter;
-                        const sm = state.storedMarks || undefined;
-                        const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
-                            state.tr;
-                        return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
-                    }
-                }),
+            new InputRule(new RegExp(/%\[/), (state, match, start, end) => {
+                const resolved = state.doc.resolve(start) as any;
+                if (resolved?.parent.type.name === 'paragraph') {
+                    return state.tr.deleteRange(start, end).setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'left' }, resolved.parent.marks);
+                } else {
+                    const node = resolved.nodeAfter;
+                    const sm = state.storedMarks || undefined;
+                    const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: 'left' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
+                    return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
+                }
+            }),
 
             // right justify text
-            new InputRule(
-                new RegExp(/%\]/),
-                (state, match, start, end) => {
-                    const resolved = state.doc.resolve(start) as any;
-                    if (resolved?.parent.type.name === "paragraph") {
-                        return state.tr.deleteRange(start, end).setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: "right" }, resolved.parent.marks);
-                    } else {
-                        const node = resolved.nodeAfter;
-                        const sm = state.storedMarks || undefined;
-                        const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
-                            state.tr;
-                        return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
-                    }
-                }),
-
+            new InputRule(new RegExp(/%\]/), (state, match, start, end) => {
+                const resolved = state.doc.resolve(start) as any;
+                if (resolved?.parent.type.name === 'paragraph') {
+                    return state.tr.deleteRange(start, end).setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'right' }, resolved.parent.marks);
+                } else {
+                    const node = resolved.nodeAfter;
+                    const sm = state.storedMarks || undefined;
+                    const replaced = node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: 'right' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
+                    return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2)));
+                }
+            }),
 
             // %f create footnote
-            new InputRule(
-                new RegExp(/%f$/),
-                (state, match, start, end) => {
-                    const newNode = schema.nodes.footnote.create({});
-                    const tr = state.tr;
-                    tr.deleteRange(start, end).replaceSelectionWith(newNode); // replace insertion with a footnote.
-                    return tr.setSelection(new NodeSelection( // select the footnote node to open its display
-                        tr.doc.resolve(  // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node)
-                            tr.selection.anchor - (tr.selection.$anchor.nodeBefore?.nodeSize || 0))));
-                }),
+            new InputRule(new RegExp(/%f$/), (state, match, start, end) => {
+                const newNode = schema.nodes.footnote.create({});
+                const tr = state.tr;
+                tr.deleteRange(start, end).replaceSelectionWith(newNode); // replace insertion with a footnote.
+                return tr.setSelection(
+                    new NodeSelection( // select the footnote node to open its display
+                        tr.doc.resolve(
+                            // get the location of the footnote node by subtracting the nodesize of the footnote from the current insertion point anchor (which will be immediately after the footnote node)
+                            tr.selection.anchor - (tr.selection.$anchor.nodeBefore?.nodeSize || 0)
+                        )
+                    )
+                );
+            }),
 
             // activate a style by name using prefix '%<color name>'
-            new InputRule(
-                new RegExp(/%[a-z]+$/),
-                (state, match, start, end) => {
-
-                    const color = match[0].substring(1, match[0].length);
-                    const marks = RichTextMenu.Instance._brushMap.get(color);
+            new InputRule(new RegExp(/%[a-z]+$/), (state, match, start, end) => {
+                const color = match[0].substring(1, match[0].length);
+                const marks = RichTextMenu.Instance._brushMap.get(color);
 
-                    if (marks) {
-                        const tr = state.tr.deleteRange(start, end);
-                        return marks ? Array.from(marks).reduce((tr, m) => tr.addStoredMark(m), tr) : tr;
-                    }
+                if (marks) {
+                    const tr = state.tr.deleteRange(start, end);
+                    return marks ? Array.from(marks).reduce((tr, m) => tr.addStoredMark(m), tr) : tr;
+                }
 
-                    const isValidColor = (strColor: string) => {
-                        const s = new Option().style;
-                        s.color = strColor;
-                        return s.color === strColor.toLowerCase(); // 'false' if color wasn't assigned
-                    };
+                const isValidColor = (strColor: string) => {
+                    const s = new Option().style;
+                    s.color = strColor;
+                    return s.color === strColor.toLowerCase(); // 'false' if color wasn't assigned
+                };
 
-                    if (isValidColor(color)) {
-                        return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontColor.create({ color: color }));
-                    }
+                if (isValidColor(color)) {
+                    return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontColor.create({ color: color }));
+                }
 
-                    return null;
-                }),
+                return null;
+            }),
 
             // stop using active style
-            new InputRule(
-                new RegExp(/%%$/),
-                (state, match, start, end) => {
-
-                    const tr = state.tr.deleteRange(start, end);
-                    const marks = state.tr.selection.$anchor.nodeBefore?.marks;
-
-                    return marks ? Array.from(marks).filter(m => m !== state.schema.marks.user_mark).reduce((tr, m) => tr.removeStoredMark(m), tr) : tr;
-                }),
-
-            // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document 
-            // [[<fieldKey> : <Doc>]]   
-            // [[:Doc]] => hyperlink   
-            // [[fieldKey]] => show field   
+            new InputRule(new RegExp(/%%$/), (state, match, start, end) => {
+                const tr = state.tr.deleteRange(start, end);
+                const marks = state.tr.selection.$anchor.nodeBefore?.marks;
+
+                return marks
+                    ? Array.from(marks)
+                          .filter(m => m.type !== state.schema.marks.user_mark)
+                          .reduce((tr, m) => tr.removeStoredMark(m), tr)
+                    : tr;
+            }),
+
+            // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
+            // [[<fieldKey> : <Doc>]]
+            // [[:Doc]] => hyperlink
+            // [[fieldKey]] => show field
             // [[fieldKey=value]] => show field and also set its value
             // [[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 value = match[2]?.substring(1);
-                    if (!fieldKey) {
-                        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: rawdocid.replace(/^:/, ""), _width: 500, _height: 500, }, docid);
-                                DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, "portal to:portal from", undefined);
-
-                                const fstate = this.TextBox.EditorView?.state;
-                                if (fstate && selection) {
-                                    this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection))));
-                                }
-                            });
-                            return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
-                        }
-                        return state.tr;
+            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 value = match[2]?.substring(1);
+                if (!fieldKey) {
+                    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: rawdocid.replace(/^:/, ''), _width: 500, _height: 500 }, docid);
+                            DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, 'portal to:portal from', undefined);
+
+                            const fstate = this.TextBox.EditorView?.state;
+                            if (fstate && selection) {
+                                this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection))));
+                            }
+                        });
+                        return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
                     }
-                    if (value !== "" && value !== undefined) {
-                        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 });
-                    return state.tr.deleteRange(start, end).insert(start, fieldView);
-                }),
-
+                    return state.tr;
+                }
+                if (value !== '' && value !== undefined) {
+                    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 });
+                return state.tr.deleteRange(start, end).insert(start, fieldView);
+            }),
 
-            // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document 
+            // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
             // wiki:title
-            new InputRule(
-                new RegExp(/wiki:([a-zA-Z_@:\.\?\-0-9]+ )$/),
-                (state, match, start, end) => {
-                    const title = match[1];
-                    this.TextBox.EditorView?.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))));
-
-                    this.TextBox.makeLinkAnchor(undefined, "add:right", `https://en.wikipedia.org/wiki/${title.trim()}`, "wikipedia reference");
-
-                    const fstate = this.TextBox.EditorView?.state;
-                    if (fstate) {
-                        const tr = fstate?.tr.deleteRange(start, start + 5);
-                        return tr.setSelection(new TextSelection(tr.doc.resolve(end - 5))).insertText(" ");
-                    }
-                    return state.tr;
-                }),
+            new InputRule(new RegExp(/wiki:([a-zA-Z_@:\.\?\-0-9]+ )$/), (state, match, start, end) => {
+                const title = match[1];
+                this.TextBox.EditorView?.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))));
+
+                this.TextBox.makeLinkAnchor(undefined, 'add:right', `https://en.wikipedia.org/wiki/${title.trim()}`, 'wikipedia reference');
+
+                const fstate = this.TextBox.EditorView?.state;
+                if (fstate) {
+                    const tr = fstate?.tr.deleteRange(start, start + 5);
+                    return tr.setSelection(new TextSelection(tr.doc.resolve(end - 5))).insertText(' ');
+                }
+                return state.tr;
+            }),
 
-            // create an inline view of a document {{ <layoutKey> : <Doc> }}  
-            // {{:Doc}} => show default view of document   
-            // {{<layout>}} => show layout for this doc   
+            // create an inline view of a document {{ <layoutKey> : <Doc> }}
+            // {{:Doc}} => show default view of document
+            // {{<layout>}} => show layout for this doc
             // {{<layout> : Doc}} => show layout for another doc
-            new InputRule(
-                new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_@\.\? \-0-9]+)?\}\}$/),
-                (state, match, start, end) => {
-                    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 => {
+            new InputRule(new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_@\.\? \-0-9]+)?\}\}$/), (state, match, start, end) => {
+                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 => {
                         if (!(docx instanceof Doc && docx)) {
                             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 sm = state.storedMarks || undefined;
-                    return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
-                }),
+                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 sm = state.storedMarks || undefined;
+                return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
+            }),
 
             // create an inline view of a tag stored under the '#' field
-            new InputRule(
-                new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/),
-                (state, match, start, end) => {
-                    const tag = match[1];
-                    if (!tag) return state.tr;
-                    this.Document[DataSym]["#" + tag] = "#" + tag;
-                    const tags = StrCast(this.Document[DataSym].tags, ":");
-                    if (!tags.includes(`#${tag}:`)) {
-                        this.Document[DataSym].tags = `${tags + "#" + tag + ':'}`;
-                    }
-                    const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag });
-                    return state.tr.deleteRange(start, end).insert(start, fieldView).insertText(" ");
-                }),
-
+            new InputRule(new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/), (state, match, start, end) => {
+                const tag = match[1];
+                if (!tag) return state.tr;
+                this.Document[DataSym]['#' + tag] = '#' + tag;
+                const tags = StrCast(this.Document[DataSym].tags, ':');
+                if (!tags.includes(`#${tag}:`)) {
+                    this.Document[DataSym].tags = `${tags + '#' + tag + ':'}`;
+                }
+                const fieldView = state.schema.nodes.dashField.create({ fieldKey: '#' + tag });
+                return state.tr.deleteRange(start, end).insert(start, fieldView).insertText(' ');
+            }),
 
             // # heading
-            textblockTypeInputRule(
-                new RegExp(/^(#{1,6})\s$/),
-                schema.nodes.heading,
-                match => {
-                    return ({ level: match[1].length });
-                }
-            ),
+            textblockTypeInputRule(new RegExp(/^(#{1,6})\s$/), schema.nodes.heading, match => {
+                return { level: match[1].length };
+            }),
 
             // set the Todo user-tag on the current selection (assumes % was used to initiate an EnteringStyle mode)
-            new InputRule(
-                new RegExp(/[ti!x]$/),
-                (state, match, start, end) => {
-
-                    if (state.selection.to === state.selection.from || !this.EnteringStyle) return null;
-
-                    const tag = match[0] === "t" ? "todo" : match[0] === "i" ? "ignore" : match[0] === "x" ? "disagree" : match[0] === "!" ? "important" : "??";
-                    const node = (state.doc.resolve(start) as any).nodeAfter;
+            new InputRule(new RegExp(/[ti!x]$/), (state, match, start, end) => {
+                if (state.selection.to === state.selection.from || !this.EnteringStyle) return null;
 
-                    if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
+                const tag = match[0] === 't' ? 'todo' : match[0] === 'i' ? 'ignore' : match[0] === 'x' ? 'disagree' : match[0] === '!' ? 'important' : '??';
+                const node = (state.doc.resolve(start) as any).nodeAfter;
 
-                    return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
-                }),
+                if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag);
 
-            new InputRule(
-                new RegExp(/%\(/),
-                (state, match, start, end) => {
-                    const node = (state.doc.resolve(start) as any).nodeAfter;
-                    const sm = state.storedMarks || [];
-                    const mark = state.schema.marks.summarizeInclusive.create();
+                return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) : state.tr;
+            }),
 
-                    sm.push(mark);
-                    const selected = state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).addMark(start, end, mark);
-                    const content = selected.selection.content();
-                    const replaced = node ? selected.replaceRangeWith(start, end,
-                        schema.nodes.summary.create({ visibility: true, text: content, textslice: content.toJSON() })) :
-                        state.tr;
+            new InputRule(new RegExp(/%\(/), (state, match, start, end) => {
+                const node = (state.doc.resolve(start) as any).nodeAfter;
+                const sm = state.storedMarks?.slice() || [];
+                const mark = state.schema.marks.summarizeInclusive.create();
 
-                    return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1))).setStoredMarks([...node.marks, ...sm]);
-                }),
+                sm.push(mark);
+                const selected = state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).addMark(start, end, mark);
+                const content = selected.selection.content();
+                const replaced = node ? selected.replaceRangeWith(start, end, schema.nodes.summary.create({ visibility: true, text: content, textslice: content.toJSON() })) : state.tr;
 
-            new InputRule(
-                new RegExp(/%\)/),
-                (state, match, start, end) => {
-                    return state.tr.deleteRange(start, end).removeStoredMark(state.schema.marks.summarizeInclusive.create());
-                }),
+                return replaced.setSelection(new TextSelection(replaced.doc.resolve(end + 1))).setStoredMarks([...node.marks, ...sm]);
+            }),
 
-        ]
+            new InputRule(new RegExp(/%\)/), (state, match, start, end) => {
+                return state.tr.deleteRange(start, end).removeStoredMark(state.schema.marks.summarizeInclusive.create());
+            }),
+        ],
     };
 }
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index c017db034..01acc3de9 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -1,35 +1,49 @@
-import { TextSelection } from "prosemirror-state";
-import { Fragment, Node, Slice } from "prosemirror-model";
+import { TextSelection } from 'prosemirror-state';
+import { Fragment, Node, Slice } from 'prosemirror-model';
 import * as ReactDOM from 'react-dom';
-import React = require("react");
+import React = require('react');
 
 // an elidable textblock that collapses when its '<-' is clicked and expands when its '...' anchor is clicked.
 // this node actively edits prosemirror (as opposed to just changing how things are rendered) and thus doesn't
 // really need a react view.  However, it would be cleaner to figure out how to do this just as a react rendering
 // method instead of changing prosemirror's text when the expand/elide buttons are clicked.
 export class SummaryView {
-    _fieldWrapper: HTMLSpanElement; // container for label and value
+    dom: HTMLSpanElement; // container for label and value
 
     constructor(node: any, view: any, getPos: any) {
         const self = this;
-        this._fieldWrapper = document.createElement("span");
-        this._fieldWrapper.className = this.className(node.attrs.visibility);
-        this._fieldWrapper.onpointerdown = function (e: any) { self.onPointerDown(e, node, view, getPos); };
-        this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
-        this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
+        this.dom = document.createElement('span');
+        this.dom.className = this.className(node.attrs.visibility);
+        this.dom.onpointerdown = function (e: any) {
+            self.onPointerDown(e, node, view, getPos);
+        };
+        this.dom.onkeypress = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onkeydown = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onkeyup = function (e: any) {
+            e.stopPropagation();
+        };
+        this.dom.onmousedown = function (e: any) {
+            e.stopPropagation();
+        };
 
         const js = node.toJSON;
-        node.toJSON = function () { return js.apply(this, arguments); };
+        node.toJSON = function () {
+            return js.apply(this, arguments);
+        };
 
-        ReactDOM.render(<SummaryViewInternal />, this._fieldWrapper);
-        (this as any).dom = this._fieldWrapper;
+        ReactDOM.render(<SummaryViewInternal />, this.dom);
+        (this as any).dom = this.dom;
     }
 
-    className = (visible: boolean) => "formattedTextBox-summarizer" + (visible ? "" : "-collapsed");
-    destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
-    selectNode() { }
+    className = (visible: boolean) => 'formattedTextBox-summarizer' + (visible ? '' : '-collapsed');
+    destroy() {
+        ReactDOM.unmountComponentAtNode(this.dom);
+    }
+    selectNode() {}
 
     updateSummarizedText(start: any, view: any) {
         const mtype = view.state.schema.marks.summarize;
@@ -44,8 +58,7 @@ export class SummaryView {
                     if (node.marks.find((m: any) => m.type === mtype || m.type === mtypeInc)) {
                         visited.add(node);
                         endPos = i + node.nodeSize - 1;
-                    }
-                    else skip = true;
+                    } else skip = true;
                 }
             });
         }
@@ -56,26 +69,28 @@ export class SummaryView {
         const visible = !node.attrs.visibility;
         const attrs = { ...node.attrs, visibility: visible };
         let textSelection = TextSelection.create(view.state.doc, getPos() + 1);
-        if (!visible) { // update summarized text and save in attrs
+        if (!visible) {
+            // update summarized text and save in attrs
             textSelection = this.updateSummarizedText(getPos() + 1, view);
             attrs.text = textSelection.content();
             attrs.textslice = attrs.text.toJSON();
         }
-        view.dispatch(view.state.tr.
-            setSelection(textSelection). // select the current summarized text (or where it will be if its collapsed)
-            replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text). // collapse/expand it
-            setNodeMarkup(getPos(), undefined, attrs)); // update the attrs
+        view.dispatch(
+            view.state.tr
+                .setSelection(textSelection) // select the current summarized text (or where it will be if its collapsed)
+                .replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text) // collapse/expand it
+                .setNodeMarkup(getPos(), undefined, attrs)
+        ); // update the attrs
         e.preventDefault();
         e.stopPropagation();
-        this._fieldWrapper.className = this.className(visible);
-    }
+        this.dom.className = this.className(visible);
+    };
 }
 
-interface ISummaryView {
-}
+interface ISummaryView {}
 // currently nothing needs to be rendered for the internal view of a summary.
 export class SummaryViewInternal extends React.Component<ISummaryView> {
     render() {
         return <> </>;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 2fde5c7ba..00c41e187 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -1,66 +1,70 @@
-import React = require("react");
-import { DOMOutputSpecArray, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
-import { Doc } from "../../../../fields/Doc";
+import React = require('react');
+import { DOMOutputSpec, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from 'prosemirror-model';
+import { Doc } from '../../../../fields/Doc';
 
-
-const emDOM: DOMOutputSpecArray = ["em", 0];
-const strongDOM: DOMOutputSpecArray = ["strong", 0];
-const codeDOM: DOMOutputSpecArray = ["code", 0];
+const emDOM: DOMOutputSpec = ['em', 0];
+const strongDOM: DOMOutputSpec = ['strong', 0];
+const codeDOM: DOMOutputSpec = ['code', 0];
 
 // :: Object [Specs](#model.MarkSpec) for the marks in the schema.
 export const marks: { [index: string]: MarkSpec } = {
     splitter: {
         attrs: {
-            id: { default: "" }
+            id: { default: '' },
         },
         toDOM(node: any) {
-            return ["div", { className: "dummy" }, 0];
-        }
+            return ['div', { className: 'dummy' }, 0];
+        },
     },
 
-
     // :: MarkSpec an autoLinkAnchor. These are automatically generated anchors to "published" documents based on the anchor text matching the
-    // published document's title. 
+    // published document's title.
     // NOTE: unlike linkAnchors, the autoLinkAnchor's href's indicate the target anchor of the hyperlink and NOT the source.  This is because
-    // automatic links do not create a text selection Marker document for the source anchor, but use the text document itself.  Since 
+    // automatic links do not create a text selection Marker document for the source anchor, but use the text document itself.  Since
     // multiple automatic links can be created each having the same source anchor (the whole document), the target href of the link is needed to
     // disambiguate links from one another.
     // Rendered and parsed as an `<a>`
     // element.
     autoLinkAnchor: {
         attrs: {
-            allAnchors: { default: [] as { href: string, title: string, anchorId: string }[] },
+            allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] },
             location: { default: null },
             title: { default: null },
         },
         inclusive: false,
-        parseDOM: [{
-            tag: "a[href]", getAttrs(dom: any) {
-                return {
-                    location: dom.getAttribute("location"),
-                    title: dom.getAttribute("title")
-                };
-            }
-        }],
+        parseDOM: [
+            {
+                tag: 'a[href]',
+                getAttrs(dom: any) {
+                    return {
+                        location: dom.getAttribute('location'),
+                        title: dom.getAttribute('title'),
+                    };
+                },
+            },
+        ],
         toDOM(node: any) {
-            const targethrefs = node.attrs.allAnchors.reduce((p: string, item: { href: string, title: string, anchorId: string }) => p ? p + " " + item.href : item.href, "");
-            const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string, title: string, anchorId: string }) => p ? p + " " + item.anchorId : item.anchorId, "");
-            return ["a", { class: anchorids, "data-targethrefs": targethrefs, "data-linkdoc": node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0];
-        }
+            const targethrefs = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.href : item.href), '');
+            const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.anchorId : item.anchorId), '');
+            return ['a', { class: anchorids, 'data-targethrefs': targethrefs, 'data-linkdoc': node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0];
+        },
     },
     noAutoLinkAnchor: {
         attrs: {},
         inclusive: false,
-        parseDOM: [{
-            tag: "div", getAttrs(dom: any) {
-                return {
-                    noAutoLink: dom.getAttribute("data-noAutoLink"),
-                };
-            }
-        }],
+        parseDOM: [
+            {
+                tag: 'div',
+                getAttrs(dom: any) {
+                    return {
+                        noAutoLink: dom.getAttribute('data-noAutoLink'),
+                    };
+                },
+            },
+        ],
         toDOM(node: any) {
-            return ["span", { "data-noAutoLink": "true" }, 0];
-        }
+            return ['span', { 'data-noAutoLink': 'true' }, 0];
+        },
     },
     // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each linkAnchor specifies an href to the URL of the source selection Marker text,
     // and a title for use in menus and hover. `title`
@@ -68,31 +72,46 @@ export const marks: { [index: string]: MarkSpec } = {
     // element.
     linkAnchor: {
         attrs: {
-            allAnchors: { default: [] as { href: string, title: string, anchorId: string }[] },
+            allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] },
             location: { default: null },
             title: { default: null },
-            docref: { default: false } // flags whether the linked text comes from a document within Dash.  If so, an attribution label is appended after the text
+            docref: { default: false }, // flags whether the linked text comes from a document within Dash.  If so, an attribution label is appended after the text
         },
         inclusive: false,
-        parseDOM: [{
-            tag: "a[href]", getAttrs(dom: any) {
-                return {
-                    location: dom.getAttribute("location"),
-                    title: dom.getAttribute("title")
-                };
-            }
-        }],
+        parseDOM: [
+            {
+                tag: 'a[href]',
+                getAttrs(dom: any) {
+                    return {
+                        location: dom.getAttribute('location'),
+                        title: dom.getAttribute('title'),
+                    };
+                },
+            },
+        ],
         toDOM(node: any) {
-            const targethrefs = node.attrs.allAnchors.reduce((p: string, item: { href: string, title: string, anchorId: string }) => p ? p + " " + item.href : item.href, "");
-            const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string, title: string, anchorId: string }) => p ? p + " " + item.anchorId : item.anchorId, "");
-            return node.attrs.docref && node.attrs.title ?
-                ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", {
-                    ...node.attrs,
-                    class: "prosemirror-attribution",
-                    href: node.attrs.allAnchors[0].href,
-                }, node.attrs.title], ["br"]] :
-                //node.attrs.allLinks.length === 1 ?
-                ["a", { class: anchorids, "data-targethrefs": targethrefs, title: node.attrs.title, location: node.attrs.location, style: `text-decoration: underline` }, 0];
+            const targethrefs = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.href : item.href), '');
+            const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.anchorId : item.anchorId), '');
+            return node.attrs.docref && node.attrs.title
+                ? [
+                      'div',
+                      ['span', `"`],
+                      ['span', 0],
+                      ['span', `"`],
+                      ['br'],
+                      [
+                          'a',
+                          {
+                              ...node.attrs,
+                              class: 'prosemirror-attribution',
+                              href: node.attrs.allAnchors[0].href,
+                          },
+                          node.attrs.title,
+                      ],
+                      ['br'],
+                  ]
+                : //node.attrs.allLinks.length === 1 ?
+                  ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, location: node.attrs.location, style: `text-decoration: underline` }, 0];
             // ["div", { class: "prosemirror-anchor" },
             //     ["span", { class: "prosemirror-linkBtn" },
             //         ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
@@ -102,254 +121,273 @@ export const marks: { [index: string]: MarkSpec } = {
             //         ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
             //     )]
             // ];
-        }
+        },
     },
 
     /** FONT SIZES */
     pFontSize: {
-        attrs: { fontSize: { default: "10px" } },
-        parseDOM: [{
-            tag: "span", getAttrs(dom: any) {
-                return { fontSize: dom.style.fontSize ? dom.style.fontSize.toString() : "" };
-            }
-        }],
-        toDOM: (node) => node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize};` }] : ['span', 0]
+        attrs: { fontSize: { default: '10px' } },
+        parseDOM: [
+            {
+                tag: 'span',
+                getAttrs(dom: any) {
+                    return { fontSize: dom.style.fontSize ? dom.style.fontSize.toString() : '' };
+                },
+            },
+        ],
+        toDOM: node => (node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize};` }] : ['span', 0]),
     },
 
     /* FONTS */
     pFontFamily: {
-        attrs: { family: { default: "" } },
-        parseDOM: [{
-            tag: "span", getAttrs(dom: any) {
-                const cstyle = getComputedStyle(dom);
-                if (cstyle.font) {
-                    if (cstyle.font.indexOf("Times New Roman") !== -1) return { family: "Times New Roman" };
-                    if (cstyle.font.indexOf("Arial") !== -1) return { family: "Arial" };
-                    if (cstyle.font.indexOf("Georgia") !== -1) return { family: "Georgia" };
-                    if (cstyle.font.indexOf("Comic Sans") !== -1) return { family: "Comic Sans MS" };
-                    if (cstyle.font.indexOf("Tahoma") !== -1) return { family: "Tahoma" };
-                    if (cstyle.font.indexOf("Crimson") !== -1) return { family: "Crimson Text" };
-                }
-            }
-        }],
-        toDOM: (node) => node.attrs.family ? ['span', { style: `font-family: "${node.attrs.family}";` }] : ['span', 0]
+        attrs: { family: { default: '' } },
+        parseDOM: [
+            {
+                tag: 'span',
+                getAttrs(dom: any) {
+                    const cstyle = getComputedStyle(dom);
+                    if (cstyle.font) {
+                        if (cstyle.font.indexOf('Times New Roman') !== -1) return { family: 'Times New Roman' };
+                        if (cstyle.font.indexOf('Arial') !== -1) return { family: 'Arial' };
+                        if (cstyle.font.indexOf('Georgia') !== -1) return { family: 'Georgia' };
+                        if (cstyle.font.indexOf('Comic Sans') !== -1) return { family: 'Comic Sans MS' };
+                        if (cstyle.font.indexOf('Tahoma') !== -1) return { family: 'Tahoma' };
+                        if (cstyle.font.indexOf('Crimson') !== -1) return { family: 'Crimson Text' };
+                    }
+                    return { family: '' };
+                },
+            },
+        ],
+        toDOM: node => (node.attrs.family ? ['span', { style: `font-family: "${node.attrs.family}";` }] : ['span', 0]),
     },
     // :: MarkSpec Coloring on text. Has `color` attribute that defined the color of the marked text.
     pFontColor: {
-        attrs: { color: { default: "" } },
+        attrs: { color: { default: '' } },
         inclusive: true,
-        parseDOM: [{
-            tag: "span", getAttrs(dom: any) {
-                return { color: dom.getAttribute("color") };
-            }
-        }],
-        toDOM: (node) => node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', 0]
+        parseDOM: [
+            {
+                tag: 'span',
+                getAttrs(dom: any) {
+                    return { color: dom.getAttribute('color') };
+                },
+            },
+        ],
+        toDOM: node => (node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', 0]),
     },
 
     marker: {
         attrs: {
-            highlight: { default: "transparent" }
+            highlight: { default: 'transparent' },
         },
         inclusive: true,
-        parseDOM: [{
-            tag: "span", getAttrs(dom: any) {
-                return { highlight: dom.getAttribute("backgroundColor") };
-            }
-        }],
+        parseDOM: [
+            {
+                tag: 'span',
+                getAttrs(dom: any) {
+                    return { highlight: dom.getAttribute('backgroundColor') };
+                },
+            },
+        ],
         toDOM(node: any) {
             return node.attrs.highlight ? ['span', { style: 'background-color:' + node.attrs.highlight }] : ['span', { style: 'background-color: transparent' }];
-        }
+        },
     },
 
     // :: MarkSpec An emphasis mark. Rendered as an `<em>` element.
     // Has parse rules that also match `<i>` and `font-style: italic`.
     em: {
-        parseDOM: [{ tag: "i" }, { tag: "em" }, { style: "font-style: italic" }],
-        toDOM() { return emDOM; }
+        parseDOM: [{ tag: 'i' }, { tag: 'em' }, { style: 'font-style: italic' }],
+        toDOM() {
+            return emDOM;
+        },
     },
 
     // :: MarkSpec A strong mark. Rendered as `<strong>`, parse rules
     // also match `<b>` and `font-weight: bold`.
     strong: {
-        parseDOM: [{ tag: "strong" },
-        { tag: "b" },
-        { style: "font-weight" }],
-        toDOM() { return strongDOM; }
+        parseDOM: [{ tag: 'strong' }, { tag: 'b' }, { style: 'font-weight' }],
+        toDOM() {
+            return strongDOM;
+        },
     },
 
     strikethrough: {
-        parseDOM: [
-            { tag: 'strike' },
-            { style: 'text-decoration=line-through' },
-            { style: 'text-decoration-line=line-through' }
+        parseDOM: [{ tag: 'strike' }, { style: 'text-decoration=line-through' }, { style: 'text-decoration-line=line-through' }],
+        toDOM: () => [
+            'span',
+            {
+                style: 'text-decoration-line:line-through',
+            },
         ],
-        toDOM: () => ['span', {
-            style: 'text-decoration-line:line-through'
-        }]
     },
 
     subscript: {
         excludes: 'superscript',
-        parseDOM: [
-            { tag: 'sub' },
-            { style: 'vertical-align=sub' }
-        ],
-        toDOM: () => ['sub']
+        parseDOM: [{ tag: 'sub' }, { style: 'vertical-align=sub' }],
+        toDOM: () => ['sub'],
     },
 
     superscript: {
         excludes: 'subscript',
-        parseDOM: [
-            { tag: 'sup' },
-            { style: 'vertical-align=super' }
-        ],
-        toDOM: () => ['sup']
+        parseDOM: [{ tag: 'sup' }, { style: 'vertical-align=super' }],
+        toDOM: () => ['sup'],
     },
 
     mbulletType: {
         attrs: {
-            bulletType: { default: "decimal" }
+            bulletType: { default: 'decimal' },
         },
         toDOM(node: any) {
-            return ['span', {
-                style: `background: ${node.attrs.bulletType === "decimal" ? "yellow" : node.attrs.bulletType === "upper-alpha" ? "blue" : "green"}`
-            }];
-        }
+            return [
+                'span',
+                {
+                    style: `background: ${node.attrs.bulletType === 'decimal' ? 'yellow' : node.attrs.bulletType === 'upper-alpha' ? 'blue' : 'green'}`,
+                },
+            ];
+        },
     },
 
     metadata: {
         toDOM() {
             return ['span', { style: 'font-size:75%; background:rgba(100, 100, 100, 0.2); ' }];
-        }
+        },
     },
     metadataKey: {
         toDOM() {
             return ['span', { style: 'font-style:italic; ' }];
-        }
+        },
     },
     metadataVal: {
         toDOM() {
             return ['span'];
-        }
+        },
     },
 
     summarizeInclusive: {
         parseDOM: [
             {
-                tag: "span",
+                tag: 'span',
                 getAttrs: (p: any) => {
-                    if (typeof (p) !== "string") {
+                    if (typeof p !== 'string') {
                         const style = getComputedStyle(p);
-                        if (style.textDecoration === "underline") return null;
-                        if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
-                            p.parentElement.outerHTML.indexOf("text-decoration-style: solid") !== -1) {
+                        if (style.textDecoration === 'underline') return null;
+                        if (p.parentElement.outerHTML.indexOf('text-decoration: underline') !== -1 && p.parentElement.outerHTML.indexOf('text-decoration-style: solid') !== -1) {
                             return null;
                         }
                     }
                     return false;
-                }
+                },
             },
         ],
         inclusive: true,
         toDOM() {
-            return ['span', {
-                style: 'text-decoration: underline; text-decoration-style: solid; text-decoration-color: rgba(204, 206, 210, 0.92)'
-            }];
-        }
+            return [
+                'span',
+                {
+                    style: 'text-decoration: underline; text-decoration-style: solid; text-decoration-color: rgba(204, 206, 210, 0.92)',
+                },
+            ];
+        },
     },
 
     summarize: {
         inclusive: false,
         parseDOM: [
             {
-                tag: "span",
+                tag: 'span',
                 getAttrs: (p: any) => {
-                    if (typeof (p) !== "string") {
+                    if (typeof p !== 'string') {
                         const style = getComputedStyle(p);
-                        if (style.textDecoration === "underline") return null;
-                        if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
-                            p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1) {
+                        if (style.textDecoration === 'underline') return null;
+                        if (p.parentElement.outerHTML.indexOf('text-decoration: underline') !== -1 && p.parentElement.outerHTML.indexOf('text-decoration-style: dotted') !== -1) {
                             return null;
                         }
                     }
                     return false;
-                }
+                },
             },
         ],
         toDOM() {
-            return ['span', {
-                style: 'text-decoration: underline; text-decoration-style: dotted; text-decoration-color: rgba(204, 206, 210, 0.92)'
-            }];
-        }
+            return [
+                'span',
+                {
+                    style: 'text-decoration: underline; text-decoration-style: dotted; text-decoration-color: rgba(204, 206, 210, 0.92)',
+                },
+            ];
+        },
     },
 
     underline: {
         parseDOM: [
             {
-                tag: "span",
+                tag: 'span',
                 getAttrs: (p: any) => {
-                    if (typeof (p) !== "string") {
+                    if (typeof p !== 'string') {
                         const style = getComputedStyle(p);
-                        if (style.textDecoration === "underline" || p.parentElement.outerHTML.indexOf("text-decoration-style:line") !== -1) {
+                        if (style.textDecoration === 'underline' || p.parentElement.outerHTML.indexOf('text-decoration-style:line') !== -1) {
                             return null;
                         }
                     }
                     return false;
-                }
-            }
+                },
+            },
             // { style: "text-decoration=underline" }
         ],
-        toDOM: () => ['span', {
-            style: 'text-decoration:underline;text-decoration-style:line'
-        }]
+        toDOM: () => [
+            'span',
+            {
+                style: 'text-decoration:underline;text-decoration-style:line',
+            },
+        ],
     },
 
     search_highlight: {
         attrs: {
-            selected: { default: false }
+            selected: { default: false },
         },
         parseDOM: [{ style: 'background: yellow' }],
         toDOM(node: any) {
-            return ['span', { style: `background: ${node.attrs.selected ? "orange" : "yellow"}` }];
-        }
+            return ['span', { style: `background: ${node.attrs.selected ? 'orange' : 'yellow'}` }];
+        },
     },
 
     // the id of the user who entered the text
     user_mark: {
         attrs: {
-            userid: { default: "" },
-            modified: { default: "when?" }, // 1 second intervals since 1970
+            userid: { default: '' },
+            modified: { default: 'when?' }, // 1 second intervals since 1970
         },
-        excludes: "user_mark",
-        group: "inline",
+        excludes: 'user_mark',
+        group: 'inline',
         toDOM(node: any) {
-            const uid = node.attrs.userid.replace(".", "").replace("@", "");
+            const uid = node.attrs.userid.replace('.', '').replace('@', '');
             const min = Math.round(node.attrs.modified / 12);
             const hr = Math.round(min / 60);
             const day = Math.round(hr / 60 / 24);
-            const remote = node.attrs.userid !== Doc.CurrentUserEmail ? " UM-remote" : "";
-            return ['span', { class: "UM-" + uid + remote + " UM-min-" + min + " UM-hr-" + hr + " UM-day-" + day }, 0];
-        }
+            const remote = node.attrs.userid !== Doc.CurrentUserEmail ? ' UM-remote' : '';
+            return ['span', { class: 'UM-' + uid + remote + ' UM-min-' + min + ' UM-hr-' + hr + ' UM-day-' + day }, 0];
+        },
     },
     // the id of the user who entered the text
     user_tag: {
         attrs: {
-            userid: { default: "" },
-            modified: { default: "when?" }, // 1 second intervals since 1970
-            tag: { default: "" }
+            userid: { default: '' },
+            modified: { default: 'when?' }, // 1 second intervals since 1970
+            tag: { default: '' },
         },
-        group: "inline",
+        group: 'inline',
         inclusive: false,
         toDOM(node: any) {
-            const uid = node.attrs.userid.replace(".", "").replace("@", "");
-            return ['span', { class: "UT-" + uid + " UT-" + node.attrs.tag }, 0];
-        }
+            const uid = node.attrs.userid.replace('.', '').replace('@', '');
+            return ['span', { class: 'UT-' + uid + ' UT-' + node.attrs.tag }, 0];
+        },
     },
 
-
     // :: MarkSpec Code font mark. Represented as a `<code>` element.
     code: {
-        parseDOM: [{ tag: "code" }],
-        toDOM() { return codeDOM; }
+        parseDOM: [{ tag: 'code' }],
+        toDOM() {
+            return codeDOM;
+        },
     },
 };
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 2fe0a67cb..5142b7da6 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -1,15 +1,18 @@
-import React = require("react");
-import { DOMOutputSpecArray, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
-import { bulletList, listItem, orderedList } from 'prosemirror-schema-list';
-import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from "./ParagraphNodeSpec";
+import React = require('react');
+import { DOMOutputSpec, Node, NodeSpec } from 'prosemirror-model';
+import { listItem, orderedList } from 'prosemirror-schema-list';
+import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from './ParagraphNodeSpec';
 
-const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
-    preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
+const blockquoteDOM: DOMOutputSpec = ['blockquote', 0],
+    hrDOM: DOMOutputSpec = ['hr'],
+    preDOM: DOMOutputSpec = ['pre', ['code', 0]],
+    brDOM: DOMOutputSpec = ['br'],
+    ulDOM: DOMOutputSpec = ['ul', 0];
 
 function formatAudioTime(time: number) {
     time = Math.round(time);
     const hours = Math.floor(time / 60 / 60);
-    const minutes = Math.floor(time / 60) - (hours * 60);
+    const minutes = Math.floor(time / 60) - hours * 60;
     const seconds = time % 60;
 
     return minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
@@ -19,67 +22,70 @@ function formatAudioTime(time: number) {
 export const nodes: { [index: string]: NodeSpec } = {
     // :: NodeSpec The top level document node.
     doc: {
-        content: "block+"
+        content: 'block+',
     },
 
     paragraph: ParagraphNodeSpec,
 
     audiotag: {
-        group: "block",
+        group: 'block',
         attrs: {
             timeCode: { default: 0 },
-            audioId: { default: "" },
-            textId: { default: "" }
+            audioId: { default: '' },
+            textId: { default: '' },
         },
         toDOM(node) {
-            return ['audiotag',
+            return [
+                'audiotag',
                 {
                     class: node.attrs.textId,
                     // style: see FormattedTextBox.scss
-                    "data-timecode": node.attrs.timeCode,
-                    "data-audioid": node.attrs.audioId,
-                    "data-textid": node.attrs.textId,
+                    'data-timecode': node.attrs.timeCode,
+                    'data-audioid': node.attrs.audioId,
+                    'data-textid': node.attrs.textId,
                 },
-                formatAudioTime(node.attrs.timeCode.toString())
+                formatAudioTime(node.attrs.timeCode.toString()),
             ];
         },
         parseDOM: [
             {
-                tag: "audiotag", getAttrs(dom: any) {
+                tag: 'audiotag',
+                getAttrs(dom: any) {
                     return {
-                        timeCode: dom.getAttribute("data-timecode"),
-                        audioId: dom.getAttribute("data-audioid"),
-                        textId: dom.getAttribute("data-textid")
+                        timeCode: dom.getAttribute('data-timecode'),
+                        audioId: dom.getAttribute('data-audioid'),
+                        textId: dom.getAttribute('data-textid'),
                     };
-                }
+                },
             },
-        ]
+        ],
     },
 
     footnote: {
-        group: "inline",
-        content: "inline*",
+        group: 'inline',
+        content: 'inline*',
         inline: true,
         attrs: {
-            visibility: { default: false }
+            visibility: { default: false },
         },
         // This makes the view treat the node as a leaf, even though it
         // technically has content
         atom: true,
-        toDOM: () => ["footnote", 0],
-        parseDOM: [{ tag: "footnote" }]
+        toDOM: () => ['footnote', 0],
+        parseDOM: [{ tag: 'footnote' }],
     },
 
     // :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
     blockquote: {
-        content: "block*",
-        group: "block",
+        content: 'block*',
+        group: 'block',
         defining: true,
-        parseDOM: [{ tag: "blockquote" }],
-        toDOM() { return blockquoteDOM; }
+        parseDOM: [{ tag: 'blockquote' }],
+        toDOM() {
+            return blockquoteDOM;
+        },
     },
 
-
     // blockquote: {
     //     ...ParagraphNodeSpec,
     //     defining: true,
@@ -97,9 +103,11 @@ export const nodes: { [index: string]: NodeSpec } = {
 
     // :: NodeSpec A horizontal rule (`<hr>`).
     horizontal_rule: {
-        group: "block",
-        parseDOM: [{ tag: "hr" }],
-        toDOM() { return hrDOM; }
+        group: 'block',
+        parseDOM: [{ tag: 'hr' }],
+        toDOM() {
+            return hrDOM;
+        },
     },
 
     // :: NodeSpec A heading textblock, with a `level` attribute that
@@ -112,12 +120,14 @@ export const nodes: { [index: string]: NodeSpec } = {
             level: { default: 1 },
         },
         defining: true,
-        parseDOM: [{ tag: "h1", attrs: { level: 1 } },
-        { tag: "h2", attrs: { level: 2 } },
-        { tag: "h3", attrs: { level: 3 } },
-        { tag: "h4", attrs: { level: 4 } },
-        { tag: "h5", attrs: { level: 5 } },
-        { tag: "h6", attrs: { level: 6 } }],
+        parseDOM: [
+            { tag: 'h1', attrs: { level: 1 } },
+            { tag: 'h2', attrs: { level: 2 } },
+            { tag: 'h3', attrs: { level: 3 } },
+            { tag: 'h4', attrs: { level: 4 } },
+            { tag: 'h5', attrs: { level: 5 } },
+            { tag: 'h6', attrs: { level: 6 } },
+        ],
         toDOM(node) {
             const dom = toParagraphDOM(node) as any;
             const level = node.attrs.level || 1;
@@ -129,36 +139,38 @@ export const nodes: { [index: string]: NodeSpec } = {
             const level = Number(dom.nodeName.substring(1)) || 1;
             attrs.level = level;
             return attrs;
-        }
+        },
     },
 
     // :: NodeSpec A code listing. Disallows marks or non-text inline
     // nodes by default. Represented as a `<pre>` element with a
     // `<code>` element inside of it.
     code_block: {
-        content: "inline*",
-        marks: "_",
-        group: "block",
+        content: 'inline*',
+        marks: '_',
+        group: 'block',
         code: true,
         defining: true,
-        parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
-        toDOM() { return preDOM; }
+        parseDOM: [{ tag: 'pre', preserveWhitespace: 'full' }],
+        toDOM() {
+            return preDOM;
+        },
     },
 
     // :: NodeSpec The text node.
     text: {
-        group: "inline"
+        group: 'inline',
     },
 
     dashComment: {
         attrs: {
-            docid: { default: "" },
+            docid: { default: '' },
         },
         inline: true,
-        group: "inline",
+        group: 'inline',
         toDOM(node) {
             const attrs = { style: `width: 40px` };
-            return ["span", { ...node.attrs, ...attrs }, "←"];
+            return ['span', { ...node.attrs, ...attrs }, '←'];
         },
     },
 
@@ -169,10 +181,10 @@ export const nodes: { [index: string]: NodeSpec } = {
             text: { default: undefined },
             textslice: { default: undefined },
         },
-        group: "inline",
+        group: 'inline',
         toDOM(node) {
             const attrs = { style: `width: 40px` };
-            return ["span", { ...node.attrs, ...attrs }];
+            return ['span', { ...node.attrs, ...attrs }];
         },
     },
 
@@ -187,27 +199,30 @@ export const nodes: { [index: string]: NodeSpec } = {
             width: { default: 100 },
             alt: { default: null },
             title: { default: null },
-            float: { default: "left" },
-            location: { default: "add:right" },
-            docid: { default: "" }
+            float: { default: 'left' },
+            location: { default: 'add:right' },
+            docid: { default: '' },
         },
-        group: "inline",
+        group: 'inline',
         draggable: true,
-        parseDOM: [{
-            tag: "img[src]", getAttrs(dom: any) {
-                return {
-                    src: dom.getAttribute("src"),
-                    title: dom.getAttribute("title"),
-                    alt: dom.getAttribute("alt"),
-                    width: Math.min(100, Number(dom.getAttribute("width"))),
-                };
-            }
-        }],
+        parseDOM: [
+            {
+                tag: 'img[src]',
+                getAttrs(dom: any) {
+                    return {
+                        src: dom.getAttribute('src'),
+                        title: dom.getAttribute('title'),
+                        alt: dom.getAttribute('alt'),
+                        width: Math.min(100, Number(dom.getAttribute('width'))),
+                    };
+                },
+            },
+        ],
         // TODO if we don't define toDom, dragging the image crashes. Why?
         toDOM(node) {
             const attrs = { style: `width: ${node.attrs.width}` };
-            return ["img", { ...node.attrs, ...attrs }];
-        }
+            return ['img', { ...node.attrs, ...attrs }];
+        },
     },
 
     dashDoc: {
@@ -216,82 +231,87 @@ export const nodes: { [index: string]: NodeSpec } = {
             width: { default: 200 },
             height: { default: 100 },
             title: { default: null },
-            float: { default: "right" },
+            float: { default: 'right' },
             hidden: { default: false }, // whether dashComment node has toggle the dashDoc's display off
-            fieldKey: { default: "" },
-            docid: { default: "" },
-            alias: { default: "" }
+            fieldKey: { default: '' },
+            docid: { default: '' },
+            alias: { default: '' },
         },
-        group: "inline",
+        group: 'inline',
         draggable: false,
         toDOM(node) {
             const attrs = { style: `width: ${node.attrs.width}, height: ${node.attrs.height}` };
-            return ["div", { ...node.attrs, ...attrs }];
-        }
+            return ['div', { ...node.attrs, ...attrs }];
+        },
     },
 
     dashField: {
         inline: true,
         attrs: {
-            fieldKey: { default: "" },
-            docid: { default: "" },
-            hideKey: { default: false }
+            fieldKey: { default: '' },
+            docid: { default: '' },
+            hideKey: { default: false },
         },
-        group: "inline",
+        group: 'inline',
         draggable: false,
         toDOM(node) {
             const attrs = { style: `width: ${node.attrs.width}, height: ${node.attrs.height}` };
-            return ["div", { ...node.attrs, ...attrs }];
-        }
+            return ['div', { ...node.attrs, ...attrs }];
+        },
     },
 
     equation: {
         inline: true,
         attrs: {
-            fieldKey: { default: "" },
+            fieldKey: { default: '' },
         },
         atom: true,
-        group: "inline",
+        group: 'inline',
         draggable: false,
         toDOM(node) {
             const attrs = { style: `width: ${node.attrs.width}, height: ${node.attrs.height}` };
-            return ["div", { ...node.attrs, ...attrs }];
-        }
+            return ['div', { ...node.attrs, ...attrs }];
+        },
     },
 
     video: {
         inline: true,
         attrs: {
             src: {},
-            width: { default: "100px" },
+            width: { default: '100px' },
             alt: { default: null },
-            title: { default: null }
+            title: { default: null },
         },
-        group: "inline",
+        group: 'inline',
         draggable: true,
-        parseDOM: [{
-            tag: "video[src]", getAttrs(dom: any) {
-                return {
-                    src: dom.getAttribute("src"),
-                    title: dom.getAttribute("title"),
-                    alt: dom.getAttribute("alt"),
-                    width: Math.min(100, Number(dom.getAttribute("width"))),
-                };
-            }
-        }],
+        parseDOM: [
+            {
+                tag: 'video[src]',
+                getAttrs(dom: any) {
+                    return {
+                        src: dom.getAttribute('src'),
+                        title: dom.getAttribute('title'),
+                        alt: dom.getAttribute('alt'),
+                        width: Math.min(100, Number(dom.getAttribute('width'))),
+                    };
+                },
+            },
+        ],
         toDOM(node) {
             const attrs = { style: `width: ${node.attrs.width}` };
-            return ["video", { ...node.attrs, ...attrs }];
-        }
+            return ['video', { ...node.attrs, ...attrs }];
+        },
     },
 
     // :: NodeSpec A hard line break, represented in the DOM as `<br>`.
     hard_break: {
         inline: true,
-        group: "inline",
+        group: 'inline',
         selectable: false,
-        parseDOM: [{ tag: "br" }],
-        toDOM() { return brDOM; }
+        parseDOM: [{ tag: 'br' }],
+        toDOM() {
+            return brDOM;
+        },
     },
 
     ordered_list: {
@@ -300,85 +320,108 @@ export const nodes: { [index: string]: NodeSpec } = {
         group: 'block',
         attrs: {
             bulletStyle: { default: 0 },
-            mapStyle: { default: "decimal" },// "decimal", "multi", "bullet"
-            fontColor: { default: "inherit" },
+            mapStyle: { default: 'decimal' }, // "decimal", "multi", "bullet"
+            fontColor: { default: 'inherit' },
             fontSize: { default: undefined },
             fontFamily: { default: undefined },
             visibility: { default: true },
-            indent: { default: undefined }
+            indent: { default: undefined },
         },
         parseDOM: [
             {
-                tag: "ul", getAttrs(dom: any) {
+                tag: 'ul',
+                getAttrs(dom: any) {
                     return {
-                        bulletStyle: dom.getAttribute("data-bulletStyle"),
-                        mapStyle: dom.getAttribute("data-mapStyle"),
+                        bulletStyle: dom.getAttribute('data-bulletStyle'),
+                        mapStyle: dom.getAttribute('data-mapStyle'),
                         fontColor: dom.style.color,
-                        fontSize: dom.style["font-size"],
-                        fontFamily: dom.style["font-family"],
-                        indent: dom.style["margin-left"]
+                        fontSize: dom.style['font-size'],
+                        fontFamily: dom.style['font-family'],
+                        indent: dom.style['margin-left'],
                     };
-                }
+                },
             },
             {
-                style: 'list-style-type=disc', getAttrs(dom: any) {
-                    return { mapStyle: "bullet" };
-                }
+                style: 'list-style-type=disc',
+                getAttrs(dom: any) {
+                    return { mapStyle: 'bullet' };
+                },
             },
             {
-                tag: "ol", getAttrs(dom: any) {
+                tag: 'ol',
+                getAttrs(dom: any) {
                     return {
-                        bulletStyle: dom.getAttribute("data-bulletStyle"),
-                        mapStyle: dom.getAttribute("data-mapStyle"),
+                        bulletStyle: dom.getAttribute('data-bulletStyle'),
+                        mapStyle: dom.getAttribute('data-mapStyle'),
                         fontColor: dom.style.color,
-                        fontSize: dom.style["font-size"],
-                        fontFamily: dom.style["font-family"],
-                        indent: dom.style["margin-left"]
+                        fontSize: dom.style['font-size'],
+                        fontFamily: dom.style['font-family'],
+                        indent: dom.style['margin-left'],
                     };
-                }
-            }],
-        toDOM(node: Node<any>) {
-            const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
-            const fsize = node.attrs.fontSize ? `font-size: ${node.attrs.fontSize};` : "";
-            const ffam = node.attrs.fontFamily ? `font-family:${node.attrs.fontFamily};` : "";
-            const fcol = node.attrs.fontColor ? `color: ${node.attrs.fontColor};` : "";
-            const marg = node.attrs.indent ? `margin-left: ${node.attrs.indent};` : "";
-            if (node.attrs.mapStyle === "bullet") {
-                return ['ul', {
-                    "data-mapStyle": node.attrs.mapStyle,
-                    "data-bulletStyle": node.attrs.bulletStyle,
-                    style: `${fsize} ${ffam} ${fcol} ${marg}`
-                }, 0];
+                },
+            },
+        ],
+        toDOM(node: Node) {
+            const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : '';
+            const fsize = node.attrs.fontSize ? `font-size: ${node.attrs.fontSize};` : '';
+            const ffam = node.attrs.fontFamily ? `font-family:${node.attrs.fontFamily};` : '';
+            const fcol = node.attrs.fontColor ? `color: ${node.attrs.fontColor};` : '';
+            const marg = node.attrs.indent ? `margin-left: ${node.attrs.indent};` : '';
+            if (node.attrs.mapStyle === 'bullet') {
+                return [
+                    'ul',
+                    {
+                        'data-mapStyle': node.attrs.mapStyle,
+                        'data-bulletStyle': node.attrs.bulletStyle,
+                        style: `${fsize} ${ffam} ${fcol} ${marg}`,
+                    },
+                    0,
+                ];
             }
-            return node.attrs.visibility ?
-                ['ol', {
-                    class: `${map}-ol`,
-                    "data-mapStyle": node.attrs.mapStyle,
-                    "data-bulletStyle": node.attrs.bulletStyle,
-                    style: `list-style: none; ${fsize} ${ffam} ${fcol} ${marg}`
-                }, 0] :
-                ['ol', { class: `${map}-ol`, style: `list-style: none;` }];
-        }
+            return node.attrs.visibility
+                ? [
+                      'ol',
+                      {
+                          class: `${map}-ol`,
+                          'data-mapStyle': node.attrs.mapStyle,
+                          'data-bulletStyle': node.attrs.bulletStyle,
+                          style: `list-style: none; ${fsize} ${ffam} ${fcol} ${marg}`,
+                      },
+                      0,
+                  ]
+                : ['ol', { class: `${map}-ol`, style: `list-style: none;` }];
+        },
     },
 
     list_item: {
         ...listItem,
         attrs: {
             bulletStyle: { default: 0 },
-            mapStyle: { default: "decimal" }, // "decimal", "multi", "bullet"
-            visibility: { default: true }
+            mapStyle: { default: 'decimal' }, // "decimal", "multi", "bullet"
+            visibility: { default: true },
         },
         content: '(paragraph|audiotag)+ | ((paragraph|audiotag)+ ordered_list)',
-        parseDOM: [{
-            tag: "li", getAttrs(dom: any) {
-                return { mapStyle: dom.getAttribute("data-mapStyle"), bulletStyle: dom.getAttribute("data-bulletStyle") };
-            }
-        }],
+        parseDOM: [
+            {
+                tag: 'li',
+                getAttrs(dom: any) {
+                    return { mapStyle: dom.getAttribute('data-mapStyle'), bulletStyle: dom.getAttribute('data-bulletStyle') };
+                },
+            },
+        ],
         toDOM(node: any) {
-            const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
-            return ["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, node.attrs.visibility ? 0 :
-                ["span", { style: `position: relative; width: 100%; height: 1.5em; overflow: hidden; display: ${node.attrs.mapStyle !== "bullet" ? "inline-block" : "list-item"}; text-overflow: ellipsis; white-space: pre` },
-                    `${node.firstChild?.textContent}...`]];
-        }
+            const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : '';
+            return [
+                'li',
+                { class: `${map}`, 'data-mapStyle': node.attrs.mapStyle, 'data-bulletStyle': node.attrs.bulletStyle },
+                node.attrs.visibility
+                    ? 0
+                    : [
+                          'span',
+                          { style: `position: relative; width: 100%; height: 1.5em; overflow: hidden; display: ${node.attrs.mapStyle !== 'bullet' ? 'inline-block' : 'list-item'}; text-overflow: ellipsis; white-space: pre` },
+                          `${node.firstChild?.textContent}...`,
+                      ],
+            ];
+        },
     },
-};
\ No newline at end of file
+};
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index b30ca644d..31a50301a 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1,59 +1,54 @@
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { saveAs } from "file-saver";
-import { action, computed, observable, ObservableMap, runInAction } from "mobx";
-import { computedFn } from "mobx-utils";
-import { alias, map, serializable } from "serializr";
-import { DocServer } from "../client/DocServer";
-import { DocumentType } from "../client/documents/DocumentTypes";
-import { CurrentUserUtils } from "../client/util/CurrentUserUtils";
-import { LinkManager } from "../client/util/LinkManager";
-import { scriptingGlobal, ScriptingGlobals } from "../client/util/ScriptingGlobals";
-import { SelectionManager } from "../client/util/SelectionManager";
-import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
-import { UndoManager } from "../client/util/UndoManager";
-import { DashColor, incrementTitleCopy, intersectRect, Utils } from "../Utils";
-import { DateField } from "./DateField";
-import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from "./FieldSymbols";
-import { List } from "./List";
-import { ObjectField } from "./ObjectField";
-import { PrefetchProxy, ProxyField } from "./Proxy";
-import { FieldId, RefField } from "./RefField";
-import { RichTextField } from "./RichTextField";
-import { listSpec } from "./Schema";
-import { ComputedField, ScriptField } from "./ScriptField";
-import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
-import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField";
-import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util";
-import JSZip = require("jszip");
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { saveAs } from 'file-saver';
+import { action, computed, observable, ObservableMap, runInAction } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { alias, map, serializable } from 'serializr';
+import { DocServer } from '../client/DocServer';
+import { DocumentType } from '../client/documents/DocumentTypes';
+import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
+import { LinkManager } from '../client/util/LinkManager';
+import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
+import { SelectionManager } from '../client/util/SelectionManager';
+import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper';
+import { UndoManager } from '../client/util/UndoManager';
+import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils';
+import { DateField } from './DateField';
+import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols';
+import { List } from './List';
+import { ObjectField } from './ObjectField';
+import { PrefetchProxy, ProxyField } from './Proxy';
+import { FieldId, RefField } from './RefField';
+import { RichTextField } from './RichTextField';
+import { listSpec } from './Schema';
+import { ComputedField, ScriptField } from './ScriptField';
+import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
+import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from './URLField';
+import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from './util';
+import JSZip = require('jszip');
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
         const onDelegate = Object.keys(doc).includes(key);
         const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-        return !Field.IsField(field) ? "" : (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field));
+        return !Field.IsField(field) ? '' : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field));
     }
     export function toScriptString(field: Field): string {
-        if (typeof field === "string") return `"${field}"`;
-        if (typeof field === "number" || typeof field === "boolean") return String(field);
-        if (field === undefined || field === null) return "null";
+        if (typeof field === 'string') return `"${field}"`;
+        if (typeof field === 'number' || typeof field === 'boolean') return String(field);
+        if (field === undefined || field === null) return 'null';
         return field[ToScriptString]();
     }
     export function toString(field: Field): string {
-        if (typeof field === "string") return field;
-        if (typeof field === "number" || typeof field === "boolean") return String(field);
+        if (typeof field === 'string') return field;
+        if (typeof field === 'number' || typeof field === 'boolean') return String(field);
         if (field instanceof ObjectField) return field[ToString]();
         if (field instanceof RefField) return field[ToString]();
-        return "";
+        return '';
     }
     export function IsField(field: any): field is Field;
     export function IsField(field: any, includeUndefined: true): field is Field | undefined;
     export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined {
-        return (typeof field === "string")
-            || (typeof field === "number")
-            || (typeof field === "boolean")
-            || (field instanceof ObjectField)
-            || (field instanceof RefField)
-            || (includeUndefined && field === undefined);
+        return typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean' || field instanceof ObjectField || field instanceof RefField || (includeUndefined && field === undefined);
     }
     export function Copy(field: any) {
         return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field;
@@ -77,40 +72,50 @@ export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) {
     return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue);
 }
 
-export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> { return Cast(field, Doc); }
-
-export function NumListCast(field: FieldResult) { return Cast(field, listSpec("number"), []); }
-export function StrListCast(field: FieldResult) { return Cast(field, listSpec("string"), []); }
-export function DocListCast(field: FieldResult) { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; }
-export function DocListCastOrNull(field: FieldResult) { return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; }
-
-export const WidthSym = Symbol("Width");
-export const HeightSym = Symbol("Height");
-export const DataSym = Symbol("Data");
-export const LayoutSym = Symbol("Layout");
-export const FieldsSym = Symbol("Fields");
-export const AclSym = Symbol("Acl");
-export const DirectLinksSym = Symbol("DirectLinks");
-export const AclUnset = Symbol("AclUnset");
-export const AclPrivate = Symbol("AclOwnerOnly");
-export const AclReadonly = Symbol("AclReadOnly");
-export const AclAugment = Symbol("AclAugment");
-export const AclSelfEdit = Symbol("AclSelfEdit");
-export const AclEdit = Symbol("AclEdit");
-export const AclAdmin = Symbol("AclAdmin");
-export const UpdatingFromServer = Symbol("UpdatingFromServer");
-export const Initializing = Symbol("Initializing");
-export const ForceServerWrite = Symbol("ForceServerWrite");
-export const CachedUpdates = Symbol("Cached updates");
+export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> {
+    return Cast(field, Doc);
+}
+
+export function NumListCast(field: FieldResult) {
+    return Cast(field, listSpec('number'), []);
+}
+export function StrListCast(field: FieldResult) {
+    return Cast(field, listSpec('string'), []);
+}
+export function DocListCast(field: FieldResult) {
+    return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[];
+}
+export function DocListCastOrNull(field: FieldResult) {
+    return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined;
+}
+
+export const WidthSym = Symbol('Width');
+export const HeightSym = Symbol('Height');
+export const DataSym = Symbol('Data');
+export const LayoutSym = Symbol('Layout');
+export const FieldsSym = Symbol('Fields');
+export const AclSym = Symbol('Acl');
+export const DirectLinksSym = Symbol('DirectLinks');
+export const AclUnset = Symbol('AclUnset');
+export const AclPrivate = Symbol('AclOwnerOnly');
+export const AclReadonly = Symbol('AclReadOnly');
+export const AclAugment = Symbol('AclAugment');
+export const AclSelfEdit = Symbol('AclSelfEdit');
+export const AclEdit = Symbol('AclEdit');
+export const AclAdmin = Symbol('AclAdmin');
+export const UpdatingFromServer = Symbol('UpdatingFromServer');
+export const Initializing = Symbol('Initializing');
+export const ForceServerWrite = Symbol('ForceServerWrite');
+export const CachedUpdates = Symbol('Cached updates');
 
 const AclMap = new Map<string, symbol>([
-    ["None", AclUnset],
+    ['None', AclUnset],
     [SharingPermissions.None, AclPrivate],
     [SharingPermissions.View, AclReadonly],
     [SharingPermissions.Augment, AclAugment],
     [SharingPermissions.SelfEdit, AclSelfEdit],
     [SharingPermissions.Edit, AclEdit],
-    [SharingPermissions.Admin, AclAdmin]
+    [SharingPermissions.Admin, AclAdmin],
 ]);
 
 // caches the document access permissions for the current user.
@@ -120,7 +125,7 @@ export function updateCachedAcls(doc: Doc) {
     const permissions: { [key: string]: symbol } = {};
 
     doc[UpdatingFromServer] = true;
-    Object.keys(doc).filter(key => key.startsWith("acl") && (permissions[key] = AclMap.get(StrCast(doc[key]))!));
+    Object.keys(doc).filter(key => key.startsWith('acl') && (permissions[key] = AclMap.get(StrCast(doc[key]))!));
     doc[UpdatingFromServer] = false;
 
     if (Object.keys(permissions).length) {
@@ -134,7 +139,7 @@ export function updateCachedAcls(doc: Doc) {
 }
 
 @scriptingGlobal
-@Deserializable("Doc", updateCachedAcls).withFields(["id"])
+@Deserializable('Doc', updateCachedAcls).withFields(['id'])
 export class Doc extends RefField {
     constructor(id?: FieldId, forceSave?: boolean) {
         super(id);
@@ -146,24 +151,26 @@ export class Doc extends RefField {
             ownKeys: target => {
                 const obj = {} as any;
                 if (GetEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fieldKeys);
-                runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__);
+                runInAction(() => (obj.__LAYOUT__ = target.__LAYOUT__));
                 return Object.keys(obj);
             },
             getOwnPropertyDescriptor: (target, prop) => {
-                if (prop.toString() === "__LAYOUT__") {
+                if (prop.toString() === '__LAYOUT__') {
                     return Reflect.getOwnPropertyDescriptor(target, prop);
                 }
                 if (prop in target.__fieldKeys) {
                     return {
-                        configurable: true,//TODO Should configurable be true?
+                        configurable: true, //TODO Should configurable be true?
                         enumerable: true,
-                        value: 0//() => target.__fields[prop])
+                        value: 0, //() => target.__fields[prop])
                     };
                 }
                 return Reflect.getOwnPropertyDescriptor(target, prop);
             },
             deleteProperty: deleteProperty,
-            defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); },
+            defineProperty: () => {
+                throw new Error("Currently properties can't be defined on documents using Object.defineProperty");
+            },
         });
         this[SelfProxy] = doc;
         if (!id || forceSave) {
@@ -175,24 +182,30 @@ export class Doc extends RefField {
     proto: Opt<Doc>;
     [key: string]: FieldResult;
 
-    @serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize })))
-    private get __fields() { return this.___fields; }
+    @serializable(alias('fields', map(autoObject(), { afterDeserialize: afterDocDeserialize })))
+    private get __fields() {
+        return this.___fields;
+    }
     private set __fields(value) {
         this.___fields = value;
         for (const key in value) {
             const field = value[key];
-            (field !== undefined) && (this.__fieldKeys[key] = true);
+            field !== undefined && (this.__fieldKeys[key] = true);
             if (!(field instanceof ObjectField)) continue;
             field[Parent] = this[Self];
             field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]);
         }
     }
-    private get __fieldKeys() { return this.___fieldKeys; }
-    private set __fieldKeys(value) { this.___fieldKeys = value; }
+    private get __fieldKeys() {
+        return this.___fieldKeys;
+    }
+    private set __fieldKeys(value) {
+        this.___fieldKeys = value;
+    }
 
     @observable private ___fields: any = {};
     @observable private ___fieldKeys: any = {};
-    @observable public [AclSym]: { [key: string]: symbol };
+    @observable public [AclSym]: { [key: string]: symbol } = {};
     @observable public [DirectLinksSym]: Set<Doc> = new Set();
 
     private [UpdatingFromServer]: boolean = false;
@@ -201,7 +214,7 @@ export class Doc extends RefField {
 
     private [Update] = (diff: any) => {
         (!this[UpdatingFromServer] || this[ForceServerWrite]) && DocServer.UpdateField(this[Id], diff);
-    }
+    };
 
     private [Self] = this;
     private [SelfProxy]: any;
@@ -209,42 +222,52 @@ export class Doc extends RefField {
     public [WidthSym] = () => NumCast(this[SelfProxy]._width);
     public [HeightSym] = () => NumCast(this[SelfProxy]._height);
     public [ToScriptString] = () => `idToDoc("${this[Self][Id]}")`;
-    public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? "-inaccessible-" : this[SelfProxy].title})`;
-    public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
+    public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? '-inaccessible-' : this[SelfProxy].title})`;
+    public get [LayoutSym]() {
+        return this[SelfProxy].__LAYOUT__;
+    }
     public get [DataSym]() {
         const self = this[SelfProxy];
-        return self.resolvedDataDoc && !self.isTemplateForField ? self :
-            Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
+        return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
     }
     @computed get __LAYOUT__(): Doc | undefined {
         const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
         if (templateLayoutDoc) {
             let renderFieldKey: any;
-            const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, "layout")];
-            if (typeof layoutField === "string") {
-                renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0];//layoutField.split("'")[1];
+            const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, 'layout')];
+            if (typeof layoutField === 'string') {
+                renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0]; //layoutField.split("'")[1];
             } else {
                 return Cast(layoutField, Doc, null);
             }
-            return Cast(this[SelfProxy][renderFieldKey + "-layout[" + templateLayoutDoc[Id] + "]"], Doc, null) || templateLayoutDoc;
+            return Cast(this[SelfProxy][renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
         }
         return undefined;
-
     }
 
     private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
-    public static get noviceMode() { return Doc.UserDoc().noviceMode as boolean; }
-    public static set noviceMode(val) { Doc.UserDoc().noviceMode = val; }
-    public static get defaultAclPrivate() { return Doc.UserDoc().defaultAclPrivate; }
-    public static set defaultAclPrivate(val) { Doc.UserDoc().defaultAclPrivate = val; }
-    public static CurrentUserEmail: string = "";
-    public static get CurrentUserEmailNormalized() { return normalizeEmail(Doc.CurrentUserEmail); }
+    public static get noviceMode() {
+        return Doc.UserDoc().noviceMode as boolean;
+    }
+    public static set noviceMode(val) {
+        Doc.UserDoc().noviceMode = val;
+    }
+    public static get defaultAclPrivate() {
+        return Doc.UserDoc().defaultAclPrivate;
+    }
+    public static set defaultAclPrivate(val) {
+        Doc.UserDoc().defaultAclPrivate = val;
+    }
+    public static CurrentUserEmail: string = '';
+    public static get CurrentUserEmailNormalized() {
+        return normalizeEmail(Doc.CurrentUserEmail);
+    }
     public async [HandleUpdate](diff: any) {
         const set = diff.$set;
         const sameAuthor = this.author === Doc.CurrentUserEmail;
         if (set) {
             for (const key in set) {
-                const fprefix = "fields.";
+                const fprefix = 'fields.';
                 if (!key.startsWith(fprefix)) {
                     continue;
                 }
@@ -255,7 +278,7 @@ export class Doc extends RefField {
                     this[UpdatingFromServer] = true;
                     this[fKey] = value;
                     this[UpdatingFromServer] = false;
-                    if (fKey.startsWith("acl")) {
+                    if (fKey.startsWith('acl')) {
                         updateCachedAcls(this);
                     }
                     if (prev === AclPrivate && GetEffectiveAcl(this) !== AclPrivate) {
@@ -263,7 +286,7 @@ export class Doc extends RefField {
                     }
                 };
                 const writeMode = DocServer.getFieldWriteMode(fKey);
-                if (fKey.startsWith("acl") || writeMode !== DocServer.WriteMode.Playground) {
+                if (fKey.startsWith('acl') || writeMode !== DocServer.WriteMode.Playground) {
                     delete this[CachedUpdates][fKey];
                     await fn();
                 } else {
@@ -274,7 +297,7 @@ export class Doc extends RefField {
         const unset = diff.$unset;
         if (unset) {
             for (const key in unset) {
-                if (!key.startsWith("fields.")) {
+                if (!key.startsWith('fields.')) {
                     continue;
                 }
                 const fKey = key.substring(7);
@@ -326,7 +349,7 @@ export namespace Doc {
         return {
             end() {
                 makeEditable();
-            }
+            },
         };
     }
 
@@ -341,16 +364,16 @@ export namespace Doc {
         return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
     }
     export function IsPrototype(doc: Doc) {
-        return GetT(doc, "isPrototype", "boolean", true);
+        return GetT(doc, 'isPrototype', 'boolean', true);
     }
     export function IsBaseProto(doc: Doc) {
-        return GetT(doc, "baseProto", "boolean", true);
+        return GetT(doc, 'baseProto', 'boolean', true);
     }
     export function IsSystem(doc: Doc) {
-        return GetT(doc, "system", "boolean", true);
+        return GetT(doc, 'system', 'boolean', true);
     }
     export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) {
-        if (key.startsWith("_")) key = key.substring(1);
+        if (key.startsWith('_')) key = key.substring(1);
         const hasProto = doc.proto instanceof Doc;
         const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1;
         const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1;
@@ -359,7 +382,7 @@ export namespace Doc {
         } else doc.proto![key] = value;
     }
     export async function SetOnPrototype(doc: Doc, key: string, value: Field) {
-        const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc;
+        const proto = Object.getOwnPropertyNames(doc).indexOf('isPrototype') === -1 ? doc.proto : doc;
 
         if (proto) {
             proto[key] = value;
@@ -391,7 +414,8 @@ export namespace Doc {
         for (const key in fields) {
             if (fields.hasOwnProperty(key)) {
                 const value = fields[key];
-                if (!skipUndefineds || value !== undefined) { // Do we want to filter out undefineds?
+                if (!skipUndefineds || value !== undefined) {
+                    // Do we want to filter out undefineds?
                     doc[key] = value;
                 }
             }
@@ -403,10 +427,10 @@ export namespace Doc {
     // compare whether documents or their protos match
     export function AreProtosEqual(doc?: Doc, other?: Doc) {
         if (!doc || !other) return false;
-        const r = (doc === other);
-        const r2 = (Doc.GetProto(doc) === other);
-        const r3 = (Doc.GetProto(other) === doc);
-        const r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined);
+        const r = doc === other;
+        const r2 = Doc.GetProto(doc) === other;
+        const r3 = Doc.GetProto(other) === doc;
+        const r4 = Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined;
         return r || r2 || r3 || r4;
     }
 
@@ -417,7 +441,7 @@ export namespace Doc {
         if (doc instanceof Promise) {
             // console.log("GetProto: warning: got Promise insead of Doc");
         }
-        const proto = doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc));
+        const proto = doc && (Doc.GetT(doc, 'isPrototype', 'boolean', true) ? doc : doc.proto || doc);
         return proto === doc ? proto : Doc.GetProto(proto);
     }
     export function GetDataDoc(doc: Doc): Doc {
@@ -426,7 +450,7 @@ export namespace Doc {
     }
 
     export function allKeys(doc: Doc): string[] {
-        const results: Set<string> = new Set;
+        const results: Set<string> = new Set();
 
         let proto: Doc | undefined = doc;
         while (proto) {
@@ -441,8 +465,8 @@ export namespace Doc {
      * @returns the index of doc toFind in list of docs, -1 otherwise
      */
     export function IndexOf(toFind: Doc, list: Doc[], allowProtos: boolean = true) {
-        let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind) ? i : p, -1);
-        index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1);
+        let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind ? i : p), -1);
+        index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind) ? i : p), -1);
         return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind));
     }
 
@@ -478,7 +502,7 @@ export namespace Doc {
         const list = Cast(listDoc[key], listSpec(Doc));
         if (list) {
             if (allowDuplicates !== true) {
-                const pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1);
+                const pind = list.reduce((l, d, i) => (d instanceof Doc && d[Id] === doc[Id] ? i : l), -1);
                 if (pind !== -1) {
                     return true;
                     //list.splice(pind, 1);  // bcz: this causes schemaView docs in the Catalog to move to the bottom of the schema view when they are dragged even though they haven't left the collection
@@ -486,15 +510,13 @@ export namespace Doc {
             }
             if (first) {
                 list.splice(0, 0, doc);
-            }
-            else {
+            } else {
                 const ind = relativeTo ? list.indexOf(relativeTo) : -1;
                 if (ind === -1) {
                     if (reversed) list.splice(0, 0, doc);
                     else list.push(doc);
-                }
-                else {
-                    if (reversed) list.splice(before ? (list.length - ind) + 1 : list.length - ind, 0, doc);
+                } else {
+                    if (reversed) list.splice(before ? list.length - ind + 1 : list.length - ind, 0, doc);
                     else list.splice(before ? ind : ind + 1, 0, doc);
                 }
             }
@@ -507,19 +529,24 @@ export namespace Doc {
      * Computes the bounds of the contents of a set of documents.
      */
     export function ComputeContentBounds(docList: Doc[]) {
-        const bounds = docList.reduce((bounds, doc) => {
-            const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
-            const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
-            return {
-                x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
-                r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
-            };
-        }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
+        const bounds = docList.reduce(
+            (bounds, doc) => {
+                const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)];
+                const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()];
+                return {
+                    x: Math.min(sptX, bounds.x),
+                    y: Math.min(sptY, bounds.y),
+                    r: Math.max(bptX, bounds.r),
+                    b: Math.max(bptY, bounds.b),
+                };
+            },
+            { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }
+        );
         return bounds;
     }
 
     export function MakeAlias(doc: Doc, id?: string) {
-        const alias = !GetT(doc, "isPrototype", "boolean", true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
+        const alias = !GetT(doc, 'isPrototype', 'boolean', true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
         const layout = Doc.LayoutField(alias);
         if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) {
             Doc.SetLayout(alias, Doc.MakeAlias(layout));
@@ -529,71 +556,73 @@ export namespace Doc {
         alias.title = ComputedField.MakeFunction(`renameAlias(this)`);
         alias.author = Doc.CurrentUserEmail;
 
-        Doc.AddDocToList(Doc.GetProto(doc)[DataSym], "aliases", alias);
+        Doc.AddDocToList(Doc.GetProto(doc)[DataSym], 'aliases', alias);
 
         return alias;
     }
 
-    export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<Doc, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[], exclusions: string[], dontCreate: boolean, asBranch: boolean): Promise<Doc> {
+    export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<Doc, Doc>, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], dontCreate: boolean, asBranch: boolean): Promise<Doc> {
         if (Doc.IsBaseProto(doc)) return doc;
         if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
-        const copy = dontCreate ? asBranch ? (Cast(doc.branchMaster, Doc, null) || doc) : doc : new Doc(undefined, true);
+        const copy = dontCreate ? (asBranch ? Cast(doc.branchMaster, Doc, null) || doc : doc) : new Doc(undefined, true);
         cloneMap.set(doc[Id], copy);
-        const fieldExclusions = doc.type === DocumentType.MARKER ? exclusions.filter(ex => ex !== "annotationOn") : exclusions;
-        const filter = [...fieldExclusions, ...Cast(doc.cloneFieldFilter, listSpec("string"), [])];
-        await Promise.all(Object.keys(doc).map(async key => {
-            if (filter.includes(key)) return;
-            const assignKey = (val: any) => !dontCreate && (copy[key] = val);
-            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            const field = ProxyField.WithoutProxy(() => doc[key]);
-            const copyObjectField = async (field: ObjectField) => {
-                const list = await Cast(doc[key], listSpec(Doc));
-                const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
-                if (docs !== undefined && docs.length) {
-                    const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)));
-                    !dontCreate && 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, dontCreate, asBranch)); // reference documents except copy documents that are expanded template fields
-                } else {
-                    !dontCreate && assignKey(ObjectField.MakeCopy(field));
-                    if (field instanceof RichTextField) {
-                        if (field.Data.includes('"audioId":') || field.Data.includes('"textId":') || field.Data.includes('"anchorId":')) {
-                            rtfs.push({ copy, key, field });
+        const fieldExclusions = doc.type === DocumentType.MARKER ? exclusions.filter(ex => ex !== 'annotationOn') : exclusions;
+        const filter = [...fieldExclusions, ...Cast(doc.cloneFieldFilter, listSpec('string'), [])];
+        await Promise.all(
+            Object.keys(doc).map(async key => {
+                if (filter.includes(key)) return;
+                const assignKey = (val: any) => !dontCreate && (copy[key] = val);
+                const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+                const field = ProxyField.WithoutProxy(() => doc[key]);
+                const copyObjectField = async (field: ObjectField) => {
+                    const list = await Cast(doc[key], listSpec(Doc));
+                    const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
+                    if (docs !== undefined && docs.length) {
+                        const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch)));
+                        !dontCreate && 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, dontCreate, asBranch)); // reference documents except copy documents that are expanded template fields
+                    } else {
+                        !dontCreate && assignKey(ObjectField.MakeCopy(field));
+                        if (field instanceof RichTextField) {
+                            if (field.Data.includes('"audioId":') || field.Data.includes('"textId":') || field.Data.includes('"anchorId":')) {
+                                rtfs.push({ copy, key, field });
+                            }
                         }
                     }
-                }
-            };
-            if (key === "proto") {
-                if (doc[key] instanceof Doc) {
-                    assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch));
-                }
-            } else if (key === "anchor1" || key === "anchor2") {
-                if (doc[key] instanceof Doc) {
-                    assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, true, asBranch));
-                }
-            } else {
-                if (field instanceof RefField) {
-                    assignKey(field);
-                } else if (cfield instanceof ComputedField) {
-                    !dontCreate && assignKey(ComputedField.MakeFunction(cfield.script.originalScript));
-                } else if (field instanceof ObjectField) {
-                    await copyObjectField(field);
-                } else if (field instanceof Promise) {
-                    debugger; //This shouldn't happen...
+                };
+                if (key === 'proto') {
+                    if (doc[key] instanceof Doc) {
+                        assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch));
+                    }
+                } else if (key === 'anchor1' || key === 'anchor2') {
+                    if (doc[key] instanceof Doc) {
+                        assignKey(await Doc.makeClone(doc[key] as Doc, cloneMap, linkMap, rtfs, exclusions, true, asBranch));
+                    }
                 } else {
-                    assignKey(field);
+                    if (field instanceof RefField) {
+                        assignKey(field);
+                    } else if (cfield instanceof ComputedField) {
+                        !dontCreate && assignKey(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);
+                    }
                 }
-            }
-        }));
+            })
+        );
         for (const link of Array.from(doc[DirectLinksSym])) {
             const linkClone = await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch);
             linkMap.set(link, linkClone);
         }
         if (!dontCreate) {
-            Doc.SetInPlace(copy, "title", (asBranch ? "BRANCH: " : "CLONE: ") + doc.title, true);
+            Doc.SetInPlace(copy, 'title', (asBranch ? 'BRANCH: ' : 'CLONE: ') + doc.title, true);
             asBranch ? (copy.branchOf = doc) : (copy.cloneOf = doc);
             if (!Doc.IsPrototype(copy)) {
-                Doc.AddDocToList(doc, "branches", Doc.GetProto(copy));
+                Doc.AddDocToList(doc, 'branches', Doc.GetProto(copy));
             }
             cloneMap.set(doc[Id], copy);
         }
@@ -601,20 +630,20 @@ export namespace Doc {
     }
     export async function MakeClone(doc: Doc, dontCreate: boolean = false, asBranch = false, cloneMap: Map<string, Doc> = new Map()) {
         const linkMap = new Map<Doc, Doc>();
-        const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = [];
-        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ["cloneOf", "branches", "branchOf"], dontCreate, asBranch);
+        const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
+        const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf', 'branches', 'branchOf'], dontCreate, asBranch);
         Array.from(linkMap.entries()).map((links: Doc[]) => LinkManager.Instance.addLink(links[1], true));
         rtfMap.map(({ copy, key, field }) => {
             const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
                 const mapped = cloneMap.get(id);
-                return attr + "\"" + (mapped ? mapped[Id] : id) + "\"";
+                return attr + '"' + (mapped ? mapped[Id] : id) + '"';
             };
             const replacer2 = (match: any, href: string, id: string, offset: any, string: any) => {
                 const mapped = cloneMap.get(id);
                 return href + (mapped ? mapped[Id] : id);
             };
             const regex = `(${Doc.localServerPath()})([^"]*)`;
-            const re = new RegExp(regex, "g");
+            const re = new RegExp(regex, 'g');
             copy[key] = new RichTextField(field.Data.replace(/("textId":|"audioId":|"anchorId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
         });
         return { clone: copy, map: cloneMap };
@@ -628,37 +657,36 @@ export namespace Doc {
         // a.click();
         const { clone, map } = await Doc.MakeClone(doc, true);
         function replacer(key: any, value: any) {
-            if (["branchOf", "cloneOf", "context", "cursors"].includes(key)) return undefined;
+            if (['branchOf', 'cloneOf', 'context', 'cursors'].includes(key)) return undefined;
             else if (value instanceof Doc) {
-                if (key !== "field" && Number.isNaN(Number(key))) {
+                if (key !== 'field' && Number.isNaN(Number(key))) {
                     const __fields = value[FieldsSym]();
-                    return { id: value[Id], __type: "Doc", fields: __fields };
+                    return { id: value[Id], __type: 'Doc', fields: __fields };
                 } else {
-                    return { fieldId: value[Id], __type: "proxy" };
+                    return { fieldId: value[Id], __type: 'proxy' };
                 }
-            }
-            else if (value instanceof ScriptField) return { script: value.script, __type: "script" };
-            else if (value instanceof RichTextField) return { Data: value.Data, Text: value.Text, __type: "RichTextField" };
-            else if (value instanceof ImageField) return { url: value.url.href, __type: "image" };
-            else if (value instanceof PdfField) return { url: value.url.href, __type: "pdf" };
-            else if (value instanceof AudioField) return { url: value.url.href, __type: "audio" };
-            else if (value instanceof VideoField) return { url: value.url.href, __type: "video" };
-            else if (value instanceof WebField) return { url: value.url.href, __type: "web" };
-            else if (value instanceof MapField) return { url: value.url.href, __type: "map" };
-            else if (value instanceof DateField) return { date: value.toString(), __type: "date" };
-            else if (value instanceof ProxyField) return { fieldId: value.fieldId, __type: "proxy" };
-            else if (value instanceof Array && key !== "fields") return { fields: value, __type: "list" };
-            else if (value instanceof ComputedField) return { script: value.script, __type: "computed" };
+            } else if (value instanceof ScriptField) return { script: value.script, __type: 'script' };
+            else if (value instanceof RichTextField) return { Data: value.Data, Text: value.Text, __type: 'RichTextField' };
+            else if (value instanceof ImageField) return { url: value.url.href, __type: 'image' };
+            else if (value instanceof PdfField) return { url: value.url.href, __type: 'pdf' };
+            else if (value instanceof AudioField) return { url: value.url.href, __type: 'audio' };
+            else if (value instanceof VideoField) return { url: value.url.href, __type: 'video' };
+            else if (value instanceof WebField) return { url: value.url.href, __type: 'web' };
+            else if (value instanceof MapField) return { url: value.url.href, __type: 'map' };
+            else if (value instanceof DateField) return { date: value.toString(), __type: 'date' };
+            else if (value instanceof ProxyField) return { fieldId: value.fieldId, __type: 'proxy' };
+            else if (value instanceof Array && key !== 'fields') return { fields: value, __type: 'list' };
+            else if (value instanceof ComputedField) return { script: value.script, __type: 'computed' };
             else return value;
         }
 
         const docs: { [id: string]: any } = {};
-        Array.from(map.entries()).forEach(f => docs[f[0]] = f[1]);
+        Array.from(map.entries()).forEach(f => (docs[f[0]] = f[1]));
         const docString = JSON.stringify({ id: doc[Id], docs }, replacer);
 
         const zip = new JSZip();
 
-        zip.file("doc.json", docString);
+        zip.file('doc.json', docString);
 
         // // Generate a directory within the Zip file structure
         // var img = zip.folder("images");
@@ -667,11 +695,10 @@ export namespace Doc {
         // img.file("smile.gif", imgData, {base64: true});
 
         // Generate the zip file asynchronously
-        zip.generateAsync({ type: "blob" })
-            .then((content: any) => {
-                // Force down of the Zip file
-                saveAs(content, doc.title + ".zip"); // glr: Possibly change the name of the document to match the title?
-            });
+        zip.generateAsync({ type: 'blob' }).then((content: any) => {
+            // Force down of the Zip file
+            saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title?
+        });
     }
     //
     // Determines whether the layout needs to be expanded (as a template).
@@ -694,46 +721,47 @@ export namespace Doc {
     //   layout_mytemplate(somparam=somearg).
     // then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument
     export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) {
-        const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace("()", "") || StrCast(templateLayoutDoc.PARAMS);
-        if (!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc) || !targetDoc) return templateLayoutDoc;
+        const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace('()', '') || StrCast(templateLayoutDoc.PARAMS);
+        if ((!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc)) || !targetDoc) return templateLayoutDoc;
 
-        const templateField = StrCast(templateLayoutDoc.isTemplateForField);  // the field that the template renders
+        const templateField = StrCast(templateLayoutDoc.isTemplateForField); // the field that the template renders
         // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc
         // using the template layout doc's id as the field key.
         // If it doesn't find the expanded layout, then it makes a delegate of the template layout and
         // saves it on the data doc indexed by the template layout's id.
         //
-        const params = args.split("=").length > 1 ? args.split("=")[0] : "PARAMS";
+        const params = args.split('=').length > 1 ? args.split('=')[0] : 'PARAMS';
         const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc);
-        const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + (args ? `(${args})` : "") + "]";
+        const expandedLayoutFieldKey = (templateField || layoutFielddKey) + '-layout[' + templateLayoutDoc[Id] + (args ? `(${args})` : '') + ']';
         let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
 
         if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
             expandedTemplateLayout = undefined;
             _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true);
-        }
-        else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) {
+        } else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) {
             if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) {
                 expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params
             } else {
                 templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype
                 if (!targetDoc[expandedLayoutFieldKey]) {
                     _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey + args, true);
-                    setTimeout(action(() => {
-                        const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]");
-                        // the template's arguments are stored in params which is derefenced to find
-                        // the actual field key where the parameterized template data is stored.
-                        newLayoutDoc[params] = args !== "..." ? args : ""; // ... signifies the layout has sub template(s) -- so we have to expand the layout for them so that they can get the correct 'rootDocument' field, but we don't need to reassign their params.  it would be better if the 'rootDocument' field could be passed dynamically to avoid have to create instances
-                        newLayoutDoc.rootDocument = targetDoc;
-                        const dataDoc = Doc.GetProto(targetDoc);
-                        newLayoutDoc.resolvedDataDoc = dataDoc;
-                        if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List && (templateLayoutDoc[templateField] as any).length) {
-                            dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
-                        }
-                        targetDoc[expandedLayoutFieldKey] = newLayoutDoc;
-
-                        _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args);
-                    }));
+                    setTimeout(
+                        action(() => {
+                            const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, '[' + templateLayoutDoc.title + ']');
+                            // the template's arguments are stored in params which is derefenced to find
+                            // the actual field key where the parameterized template data is stored.
+                            newLayoutDoc[params] = args !== '...' ? args : ''; // ... signifies the layout has sub template(s) -- so we have to expand the layout for them so that they can get the correct 'rootDocument' field, but we don't need to reassign their params.  it would be better if the 'rootDocument' field could be passed dynamically to avoid have to create instances
+                            newLayoutDoc.rootDocument = targetDoc;
+                            const dataDoc = Doc.GetProto(targetDoc);
+                            newLayoutDoc.resolvedDataDoc = dataDoc;
+                            if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List && (templateLayoutDoc[templateField] as any).length) {
+                                dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc });
+                            }
+                            targetDoc[expandedLayoutFieldKey] = newLayoutDoc;
+
+                            _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args);
+                        })
+                    );
                 }
             }
         }
@@ -744,17 +772,17 @@ export namespace Doc {
     // otherwise, it just returns the childDoc
     export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt<Doc>, childDoc: Doc) {
         if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) {
-            console.log("No, no, no!");
+            console.log('No, no, no!');
             return { layout: childDoc, data: childDoc };
         }
-        const resolvedDataDoc = (Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc);
-        return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, "(" + StrCast(containerDoc.PARAMS) + ")"), data: resolvedDataDoc };
+        const resolvedDataDoc = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc;
+        return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, '(' + StrCast(containerDoc.PARAMS) + ')'), data: resolvedDataDoc };
     }
 
     export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc {
         Object.keys(doc).forEach(key => {
             const field = ProxyField.WithoutProxy(() => doc[key]);
-            if (key === "proto" && copyProto) {
+            if (key === 'proto' && copyProto) {
                 if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) {
                     overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto);
                 }
@@ -776,12 +804,12 @@ export namespace Doc {
 
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
         const copy = new Doc(copyProtoId, true);
-        const exclude = Cast(doc.cloneFieldFilter, listSpec("string"), []);
+        const exclude = Cast(doc.cloneFieldFilter, listSpec('string'), []);
         Object.keys(doc).forEach(key => {
             if (exclude.includes(key)) return;
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
             const field = ProxyField.WithoutProxy(() => doc[key]);
-            if (key === "proto" && copyProto) {
+            if (key === 'proto' && copyProto) {
                 if (doc[key] instanceof Doc) {
                     copy[key] = Doc.MakeCopy(doc[key]!, false);
                 }
@@ -789,11 +817,14 @@ export namespace Doc {
                 if (field instanceof RefField) {
                     copy[key] = field;
                 } else if (cfield instanceof ComputedField) {
-                    copy[key] = cfield[Copy]();// ComputedField.MakeFunction(cfield.script.originalScript);
+                    copy[key] = cfield[Copy](); // ComputedField.MakeFunction(cfield.script.originalScript);
                 } else if (field instanceof ObjectField) {
-                    copy[key] = doc[key] instanceof Doc ?
-                        key.includes("layout[") ? undefined : doc[key] : // reference documents except remove documents that are expanded teplate fields
-                        ObjectField.MakeCopy(field);
+                    copy[key] =
+                        doc[key] instanceof Doc
+                            ? key.includes('layout[')
+                                ? undefined
+                                : doc[key] // reference documents except remove documents that are expanded teplate fields
+                            : ObjectField.MakeCopy(field);
                 } else if (field instanceof Promise) {
                     debugger; //This shouldn't happend...
                 } else {
@@ -806,17 +837,16 @@ export namespace Doc {
             Doc.GetProto(copy).context = undefined;
             Doc.GetProto(copy).aliases = new List<Doc>([copy]);
         } else {
-            Doc.AddDocToList(Doc.GetProto(copy)[DataSym], "aliases", copy);
+            Doc.AddDocToList(Doc.GetProto(copy)[DataSym], 'aliases', copy);
         }
         copy.context = undefined;
-        Doc.defaultAclPrivate && (copy["acl-Public"] = "Not Shared");
+        Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared');
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
         }
         return copy;
     }
 
-
     export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc;
     export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc>;
     export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc> {
@@ -825,8 +855,10 @@ export namespace Doc {
             delegate[Initializing] = true;
             delegate.proto = doc;
             delegate.author = Doc.CurrentUserEmail;
-            Object.keys(doc).filter(key => key.startsWith("acl")).forEach(key => delegate[key] = doc[key]);
-            if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], "aliases", delegate);
+            Object.keys(doc)
+                .filter(key => key.startsWith('acl'))
+                .forEach(key => (delegate[key] = doc[key]));
+            if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], 'aliases', delegate);
             title && (delegate.title = title);
             delegate[Initializing] = false;
             return delegate;
@@ -849,7 +881,7 @@ export namespace Doc {
         delegate[Initializing] = true;
         delegate.proto = delegateProto;
         delegate.author = Doc.CurrentUserEmail;
-        Doc.AddDocToList(delegateProto[DataSym], "aliases", delegate);
+        Doc.AddDocToList(delegateProto[DataSym], 'aliases', delegate);
         delegate[Initializing] = false;
         delegateProto[Initializing] = false;
         return delegate;
@@ -861,11 +893,11 @@ export namespace Doc {
             const proto = new Doc();
             proto.author = Doc.CurrentUserEmail;
             const target = Doc.MakeDelegate(proto);
-            const targetKey = StrCast(templateDoc.layoutKey, "layout");
-            const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
+            const targetKey = StrCast(templateDoc.layoutKey, 'layout');
+            const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')');
             target.layoutKey = targetKey;
             applied && (Doc.GetProto(applied).type = templateDoc.type);
-            Doc.defaultAclPrivate && (applied["acl-Public"] = "Not Shared");
+            Doc.defaultAclPrivate && (applied['acl-Public'] = 'Not Shared');
             return applied;
         }
         return undefined;
@@ -888,9 +920,8 @@ export namespace Doc {
     // metadata field indicated by the title of the template field (not the default field that it was rendering)
     //
     export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Opt<Doc>): boolean {
-
         // find the metadata field key that this template field doc will display (indicated by its title)
-        const metadataFieldKey = StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, "");
+        const metadataFieldKey = StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, '');
 
         // update the original template to mark it as a template
         templateField.isTemplateForField = metadataFieldKey;
@@ -904,7 +935,7 @@ export namespace Doc {
         // note 2: this will not overwrite any field that already exists on the template doc at the field key
         if (!templateDoc?.[metadataFieldKey] && templateFieldValue instanceof ObjectField) {
             Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
-            (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue));
+            Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue);
         }
         // get the layout string that the template uses to specify its layout
         const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField)));
@@ -913,19 +944,18 @@ export namespace Doc {
         Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`);
 
         // assign the template field doc a delegate of any extension document that was previously used to render the template field (since extension doc's carry rendering informatino)
-        Doc.Layout(templateField)[metadataFieldKey + "_ext"] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + "_ext"] as Doc);
+        Doc.Layout(templateField)[metadataFieldKey + '_ext'] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + '_ext'] as Doc);
 
         return true;
     }
 
-
     // converts a document id to a url path on the server
-    export function globalServerPath(doc: Doc | string = ""): string {
-        return Utils.prepend("/doc/" + (doc instanceof Doc ? doc[Id] : doc));
+    export function globalServerPath(doc: Doc | string = ''): string {
+        return Utils.prepend('/doc/' + (doc instanceof Doc ? doc[Id] : doc));
     }
     // converts a document id to a url path on the server
     export function localServerPath(doc?: Doc): string {
-        return "/doc/" + (doc ? doc[Id] : "");
+        return '/doc/' + (doc ? doc[Id] : '');
     }
 
     export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
@@ -958,47 +988,70 @@ export namespace Doc {
     export class DocData {
         @observable _user_doc: Doc = undefined!;
         @observable _sharing_doc: Doc = undefined!;
-        @observable _searchQuery: string = "";
+        @observable _searchQuery: string = '';
     }
 
     // the document containing the view layout information - will be the Document itself unless the Document has
     // a layout field or 'layout' is given.
     export function Layout(doc: Doc, layout?: Doc): Doc {
-        const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, "data")}-layout[` + layout[Id] + "]"], Doc, null);
+        const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}-layout[` + layout[Id] + ']'], Doc, null);
         return overrideLayout || doc[LayoutSym] || doc;
     }
-    export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; }
-    export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; }
-    export function LayoutFieldKey(doc: Doc): string { return StrCast(Doc.Layout(doc).layout).split("'")[1]; }
+    export function SetLayout(doc: Doc, layout: Doc | string) {
+        doc[StrCast(doc.layoutKey, 'layout')] = layout;
+    }
+    export function LayoutField(doc: Doc) {
+        return doc[StrCast(doc.layoutKey, 'layout')];
+    }
+    export function LayoutFieldKey(doc: Doc): string {
+        return StrCast(Doc.Layout(doc).layout).split("'")[1];
+    }
     export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
         return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
     }
-    export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) { return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeWidth"], useWidth ? doc[WidthSym]() : 0)); }
+    export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
+        return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeWidth'], useWidth ? doc[WidthSym]() : 0));
+    }
     export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
-        const dheight = doc ? NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + "-nativeHeight"], useHeight ? doc[HeightSym]() : 0) : 0;
-        const nheight = doc ? Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]() / doc[WidthSym]() : 0;
+        const dheight = doc ? NumCast((dataDoc || doc)[Doc.LayoutFieldKey(doc) + '-nativeHeight'], useHeight ? doc[HeightSym]() : 0) : 0;
+        const nheight = doc ? (Doc.NativeWidth(doc, dataDoc, useHeight) * doc[HeightSym]()) / doc[WidthSym]() : 0;
         return !doc ? 0 : NumCast(doc._nativeHeight, nheight || dheight);
     }
-    export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) { doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + "-nativeWidth"] = width; }
-    export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) { doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + "-nativeHeight"] = height; }
-
+    export function SetNativeWidth(doc: Doc, width: number | undefined, fieldKey?: string) {
+        doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '-nativeWidth'] = width;
+    }
+    export function SetNativeHeight(doc: Doc, height: number | undefined, fieldKey?: string) {
+        doc[(fieldKey ?? Doc.LayoutFieldKey(doc)) + '-nativeHeight'] = height;
+    }
 
     const manager = new DocData();
-    export function SearchQuery(): string { return manager._searchQuery; }
-    export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); }
-    export function UserDoc(): Doc { return manager._user_doc; }
-    export function SharingDoc(): Doc { return CurrentUserUtils.MySharedDocs; }
-    export function LinkDBDoc(): Doc { return Cast(Doc.UserDoc().myLinkDatabase, Doc, null); }
-    export function SetUserDoc(doc: Doc) { return (manager._user_doc = doc); }
+    export function SearchQuery(): string {
+        return manager._searchQuery;
+    }
+    export function SetSearchQuery(query: string) {
+        runInAction(() => (manager._searchQuery = query));
+    }
+    export function UserDoc(): Doc {
+        return manager._user_doc;
+    }
+    export function SharingDoc(): Doc {
+        return CurrentUserUtils.MySharedDocs;
+    }
+    export function LinkDBDoc(): Doc {
+        return Cast(Doc.UserDoc().myLinkDatabase, Doc, null);
+    }
+    export function SetUserDoc(doc: Doc) {
+        return (manager._user_doc = doc);
+    }
 
     const isSearchMatchCache = computedFn(function IsSearchMatch(doc: Doc) {
-        return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) :
-            brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
+        return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
     });
-    export function IsSearchMatch(doc: Doc) { return isSearchMatchCache(doc); }
+    export function IsSearchMatch(doc: Doc) {
+        return isSearchMatchCache(doc);
+    }
     export function IsSearchMatchUnmemoized(doc: Doc) {
-        return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) :
-            brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
+        return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
     }
     export function SetSearchMatch(doc: Doc, results: { searchMatch: number }) {
         if (doc && GetEffectiveAcl(doc) !== AclPrivate && GetEffectiveAcl(Doc.GetProto(doc)) !== AclPrivate) {
@@ -1017,8 +1070,12 @@ export namespace Doc {
         brushManager.SearchMatchDoc.clear();
     }
 
-    const isBrushedCache = computedFn(function IsBrushed(doc: Doc) { return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc)); });
-    export function IsBrushed(doc: Doc) { return isBrushedCache(doc); }
+    const isBrushedCache = computedFn(function IsBrushed(doc: Doc) {
+        return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
+    });
+    export function IsBrushed(doc: Doc) {
+        return isBrushedCache(doc);
+    }
 
     export enum DocBrushStatus {
         unbrushed = 0,
@@ -1037,9 +1094,7 @@ export namespace Doc {
                 for (const link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
                     const a1 = Cast(link.anchor1, Doc, null);
                     const a2 = Cast(link.anchor2, Doc, null);
-                    if (Doc.AreProtosEqual(a1, doc) || Doc.AreProtosEqual(a2, doc) ||
-                        (Doc.AreProtosEqual(Cast(a1.annotationOn, Doc, null), doc)) ||
-                        (Doc.AreProtosEqual(Cast(a2.annotationOn, Doc, null), doc))) {
+                    if (Doc.AreProtosEqual(a1, doc) || Doc.AreProtosEqual(a2, doc) || Doc.AreProtosEqual(Cast(a1.annotationOn, Doc, null), doc) || Doc.AreProtosEqual(Cast(a2.annotationOn, Doc, null), doc)) {
                         return DocBrushStatus.linkHighlighted;
                     }
                 }
@@ -1070,22 +1125,21 @@ export namespace Doc {
     }
 
     export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) {
-        return Doc.AreProtosEqual(anchorDoc, (linkDoc.anchor1 as Doc).annotationOn as Doc) ||
-            Doc.AreProtosEqual(anchorDoc, linkDoc.anchor1 as Doc) ? "1" : "2";
+        return Doc.AreProtosEqual(anchorDoc, (linkDoc.anchor1 as Doc).annotationOn as Doc) || Doc.AreProtosEqual(anchorDoc, linkDoc.anchor1 as Doc) ? '1' : '2';
     }
 
     export function linkFollowUnhighlight() {
         Doc.UnhighlightAll();
-        document.removeEventListener("pointerdown", linkFollowUnhighlight);
+        document.removeEventListener('pointerdown', linkFollowUnhighlight);
     }
 
     let _lastDate = 0;
     export function linkFollowHighlight(destDoc: Doc | Doc[], dataAndDisplayDocs = true) {
         linkFollowUnhighlight();
         (destDoc instanceof Doc ? [destDoc] : destDoc).forEach(doc => Doc.HighlightDoc(doc, dataAndDisplayDocs));
-        document.removeEventListener("pointerdown", linkFollowUnhighlight);
-        document.addEventListener("pointerdown", linkFollowUnhighlight);
-        const lastDate = _lastDate = Date.now();
+        document.removeEventListener('pointerdown', linkFollowUnhighlight);
+        document.addEventListener('pointerdown', linkFollowUnhighlight);
+        const lastDate = (_lastDate = Date.now());
         window.setTimeout(() => _lastDate === lastDate && linkFollowUnhighlight(), 5000);
     }
 
@@ -1116,53 +1170,57 @@ export namespace Doc {
             const targetDoc = docEntry.value;
             targetDoc && Doc.UnHighlightDoc(targetDoc);
         }
-
     }
     export function UnBrushAllDocs() {
         brushManager.BrushedDoc.clear();
     }
 
     export function getDocTemplate(doc?: Doc) {
-        return !doc ? undefined :
-            doc.isTemplateDoc ? doc :
-                Cast(doc.dragFactory, Doc, null)?.isTemplateDoc ? doc.dragFactory :
-                    Cast(Doc.Layout(doc), Doc, null)?.isTemplateDoc ?
-                        (Cast(Doc.Layout(doc), Doc, null).resolvedDataDoc ? Doc.Layout(doc).proto : Doc.Layout(doc)) :
-                        undefined;
+        return !doc
+            ? undefined
+            : doc.isTemplateDoc
+            ? doc
+            : Cast(doc.dragFactory, Doc, null)?.isTemplateDoc
+            ? doc.dragFactory
+            : Cast(Doc.Layout(doc), Doc, null)?.isTemplateDoc
+            ? Cast(Doc.Layout(doc), Doc, null).resolvedDataDoc
+                ? Doc.Layout(doc).proto
+                : Doc.Layout(doc)
+            : undefined;
     }
 
     export function matchFieldValue(doc: Doc, key: string, value: any): boolean {
         if (Utils.HasTransparencyFilter(value)) {
-            const isTransparent = (color: string) => color !== "" && (DashColor(color).alpha() !== 1);
+            const isTransparent = (color: string) => color !== '' && DashColor(color).alpha() !== 1;
             return isTransparent(StrCast(doc[key]));
         }
-        if (typeof value === "string") {
-            value = value.replace(`,${Utils.noRecursionHack}`, "");
+        if (typeof value === 'string') {
+            value = value.replace(`,${Utils.noRecursionHack}`, '');
         }
-        const fieldVal = key === "#" ? (StrCast(doc.tags).includes(":#" + value + ":") ? StrCast(doc.tags) : undefined) : doc[key];
-        if (Cast(fieldVal, listSpec("string"), []).length) {
-            const vals = Cast(fieldVal, listSpec("string"), []);
+        const fieldVal = key === '#' ? (StrCast(doc.tags).includes(':#' + value + ':') ? StrCast(doc.tags) : undefined) : doc[key];
+        if (Cast(fieldVal, listSpec('string'), []).length) {
+            const vals = Cast(fieldVal, listSpec('string'), []);
             const docs = vals.some(v => (v as any) instanceof Doc);
             if (docs) return value === Field.toString(fieldVal as Field);
-            return vals.some(v => v.includes(value));  // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+            return vals.some(v => v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
         }
         const fieldStr = Field.toString(fieldVal as Field);
         return fieldStr.includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
     }
 
     export function deiconifyView(doc: Doc) {
-        StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc);
+        StrCast(doc.layoutKey).split('_')[1] === 'icon' && setNativeView(doc);
     }
 
     export function setNativeView(doc: any) {
-        const prevLayout = StrCast(doc.layoutKey).split("_")[1];
-        const deiconify = prevLayout === "icon" && StrCast(doc.deiconifyLayout) ? "layout_" + StrCast(doc.deiconifyLayout) : "";
-        prevLayout === "icon" && (doc.deiconifyLayout = undefined);
-        doc.layoutKey = deiconify || "layout";
+        const prevLayout = StrCast(doc.layoutKey).split('_')[1];
+        const deiconify = prevLayout === 'icon' && StrCast(doc.deiconifyLayout) ? 'layout_' + StrCast(doc.deiconifyLayout) : '';
+        prevLayout === 'icon' && (doc.deiconifyLayout = undefined);
+        doc.layoutKey = deiconify || 'layout';
     }
     export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: number[]) {
         if (!container) return;
-        const docRangeFilters = Cast(container._docRangeFilters, listSpec("string"), []);
+        const docRangeFilters = Cast(container._docRangeFilters, listSpec('string'), []);
         for (let i = 0; i < docRangeFilters.length; i += 3) {
             if (docRangeFilters[i] === key) {
                 docRangeFilters.splice(i, 3);
@@ -1180,16 +1238,16 @@ export namespace Doc {
     // filters document in a container collection:
     // all documents with the specified value for the specified key are included/excluded
     // based on the modifiers :"check", "x", undefined
-    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x" | "exists" | "unset", toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
+    export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: 'remove' | 'match' | 'check' | 'x' | 'exists' | 'unset', toggle?: boolean, fieldSuffix?: string, append: boolean = true) {
         if (!container) return;
-        const filterField = "_" + (fieldSuffix ? fieldSuffix + "-" : "") + "docFilters";
-        const docFilters = Cast(container[filterField], listSpec("string"), []);
+        const filterField = '_' + (fieldSuffix ? fieldSuffix + '-' : '') + 'docFilters';
+        const docFilters = Cast(container[filterField], listSpec('string'), []);
         runInAction(() => {
             for (let i = 0; i < docFilters.length; i++) {
-                const fields = docFilters[i].split(":"); // split key:value:modifier
-                if (fields[0] === key && (fields[1] === value || modifiers === "match")) {
+                const fields = docFilters[i].split(':'); // split key:value:modifier
+                if (fields[0] === key && (fields[1] === value || modifiers === 'match')) {
                     if (fields[2] === modifiers && modifiers && fields[1] === value) {
-                        if (toggle) modifiers = "remove";
+                        if (toggle) modifiers = 'remove';
                         else return;
                     }
                     docFilters.splice(i, 1);
@@ -1197,17 +1255,17 @@ export namespace Doc {
                     break;
                 }
             }
-            if (!docFilters.length && modifiers === "match" && value === undefined) {
+            if (!docFilters.length && modifiers === 'match' && value === undefined) {
                 container[filterField] = undefined;
-            } else if (modifiers !== "remove") {
+            } else if (modifiers !== 'remove') {
                 !append && (docFilters.length = 0);
-                docFilters.push(key + ":" + value + ":" + modifiers);
+                docFilters.push(key + ':' + value + ':' + modifiers);
                 container[filterField] = new List<string>(docFilters);
             }
         });
     }
     export function readDocRangeFilter(doc: Doc, key: string) {
-        const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []);
+        const docRangeFilters = Cast(doc._docRangeFilters, listSpec('string'), []);
         for (let i = 0; i < docRangeFilters.length; i += 3) {
             if (docRangeFilters[i] === key) {
                 return [Number(docRangeFilters[i + 1]), Number(docRangeFilters[i + 2])];
@@ -1225,8 +1283,7 @@ export namespace Doc {
                 layoutDoc._viewScale = NumCast(layoutDoc._viewScale, 1) * contentScale;
                 layoutDoc._nativeWidth = undefined;
                 layoutDoc._nativeHeight = undefined;
-            }
-            else {
+            } else {
                 layoutDoc._autoHeight = false;
                 if (!Doc.NativeWidth(layoutDoc)) {
                     layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
@@ -1239,44 +1296,61 @@ export namespace Doc {
     export function isDocPinned(doc: Doc) {
         //add this new doc to props.Document
         const curPres = CurrentUserUtils.ActivePresentation;
-        return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
+        return !curPres ? false : DocListCast(curPres.data).findIndex(val => Doc.AreProtosEqual(val, doc)) !== -1;
     }
 
     export function toIcon(doc?: Doc, isOpen?: boolean) {
         switch (StrCast(doc?.type)) {
-            case DocumentType.IMG: return "image";
-            case DocumentType.COMPARISON: return "columns";
-            case DocumentType.RTF: return "sticky-note";
+            case DocumentType.IMG:
+                return 'image';
+            case DocumentType.COMPARISON:
+                return 'columns';
+            case DocumentType.RTF:
+                return 'sticky-note';
             case DocumentType.COL:
-                const folder: IconProp = isOpen ? "folder-open" : "folder";
-                const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right";
+                const folder: IconProp = isOpen ? 'folder-open' : 'folder';
+                const chevron: IconProp = isOpen ? 'chevron-down' : 'chevron-right';
                 return !doc?.isFolder ? folder : chevron;
-            case DocumentType.WEB: return "globe-asia";
-            case DocumentType.SCREENSHOT: return "photo-video";
-            case DocumentType.WEBCAM: return "video";
-            case DocumentType.AUDIO: return "microphone";
-            case DocumentType.BUTTON: return "bolt";
-            case DocumentType.PRES: return "tv";
-            case DocumentType.SCRIPTING: return "terminal";
-            case DocumentType.IMPORT: return "cloud-upload-alt";
-            case DocumentType.VID: return "video";
-            case DocumentType.INK: return "pen-nib";
-            case DocumentType.PDF: return "file-pdf";
-            case DocumentType.LINK: return "link";
-            case DocumentType.MAP: return "map-marker-alt";
-            default: return "question";
+            case DocumentType.WEB:
+                return 'globe-asia';
+            case DocumentType.SCREENSHOT:
+                return 'photo-video';
+            case DocumentType.WEBCAM:
+                return 'video';
+            case DocumentType.AUDIO:
+                return 'microphone';
+            case DocumentType.BUTTON:
+                return 'bolt';
+            case DocumentType.PRES:
+                return 'tv';
+            case DocumentType.SCRIPTING:
+                return 'terminal';
+            case DocumentType.IMPORT:
+                return 'cloud-upload-alt';
+            case DocumentType.VID:
+                return 'video';
+            case DocumentType.INK:
+                return 'pen-nib';
+            case DocumentType.PDF:
+                return 'file-pdf';
+            case DocumentType.LINK:
+                return 'link';
+            case DocumentType.MAP:
+                return 'map-marker-alt';
+            default:
+                return 'question';
         }
     }
 
-    export async function importDocument(file:File) {
-        const upload = Utils.prepend("/uploadDoc");
+    export async function importDocument(file: File) {
+        const upload = Utils.prepend('/uploadDoc');
         const formData = new FormData();
         if (file) {
             formData.append('file', file);
-            formData.append('remap', "true");
-            const response = await fetch(upload, { method: "POST", body: formData });
+            formData.append('remap', 'true');
+            const response = await fetch(upload, { method: 'POST', body: formData });
             const json = await response.json();
-            if (json !== "error") {
+            if (json !== 'error') {
                 const doc = await DocServer.GetRefField(json);
                 return doc;
             }
@@ -1285,17 +1359,16 @@ export namespace Doc {
     }
 
     export namespace Get {
-
-        const primitives = ["string", "number", "boolean"];
+        const primitives = ['string', 'number', 'boolean'];
 
         export interface JsonConversionOpts {
             data: any;
             title?: string;
-            appendToExisting?: { targetDoc: Doc, fieldKey?: string };
+            appendToExisting?: { targetDoc: Doc; fieldKey?: string };
             excludeEmptyObjects?: boolean;
         }
 
-        const defaultKey = "json";
+        const defaultKey = 'json';
 
         /**
          * This function takes any valid JSON(-like) data, i.e. parsed or unparsed, and at arbitrarily
@@ -1340,17 +1413,17 @@ export namespace Doc {
             if (excludeEmptyObjects === undefined) {
                 excludeEmptyObjects = true;
             }
-            if (data === undefined || data === null || ![...primitives, "object"].includes(typeof data)) {
+            if (data === undefined || data === null || ![...primitives, 'object'].includes(typeof data)) {
                 return undefined;
             }
             let resolved: any;
             try {
-                resolved = JSON.parse(typeof data === "string" ? data : JSON.stringify(data));
+                resolved = JSON.parse(typeof data === 'string' ? data : JSON.stringify(data));
             } catch (e) {
                 return undefined;
             }
             let output: Opt<Doc>;
-            if (typeof resolved === "object" && !(resolved instanceof Array)) {
+            if (typeof resolved === 'object' && !(resolved instanceof Array)) {
                 output = convertObject(resolved, excludeEmptyObjects, title, appendToExisting?.targetDoc);
             } else {
                 // give the proper types to the data extracted from the JSON
@@ -1358,7 +1431,7 @@ export namespace Doc {
                 if (appendToExisting) {
                     (output = appendToExisting.targetDoc)[appendToExisting.fieldKey || defaultKey] = result;
                 } else {
-                    (output = new Doc).json = result;
+                    (output = new Doc()).json = result;
                 }
             }
             title && output && (output.title = title);
@@ -1375,14 +1448,14 @@ export namespace Doc {
         const convertObject = (object: any, excludeEmptyObjects: boolean, title?: string, target?: Doc): Opt<Doc> => {
             const hasEntries = Object.keys(object).length;
             if (hasEntries || !excludeEmptyObjects) {
-                const resolved = target ?? new Doc;
+                const resolved = target ?? new Doc();
                 if (hasEntries) {
                     let result: Opt<Field>;
                     Object.keys(object).map(key => {
                         // if excludeEmptyObjects is true, any qualifying conversions from toField will
                         // be undefined, and thus the results that would have
                         // otherwise been empty (List or Doc)s will just not be written
-                        if (result = toField(object[key], excludeEmptyObjects, key)) {
+                        if ((result = toField(object[key], excludeEmptyObjects, key))) {
                             resolved[key] = result;
                         }
                     });
@@ -1418,40 +1491,78 @@ export namespace Doc {
             if (primitives.includes(typeof data)) {
                 return data;
             }
-            if (typeof data === "object") {
+            if (typeof data === 'object') {
                 return data instanceof Array ? convertList(data, excludeEmptyObjects) : convertObject(data, excludeEmptyObjects, title, undefined);
             }
             throw new Error(`How did ${data} of type ${typeof data} end up in JSON?`);
         };
     }
-
 }
 
-ScriptingGlobals.add(function idToDoc(id: string): any { return DocServer.GetCachedRefField(id); });
-ScriptingGlobals.add(function renameAlias(doc: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${doc.aliasNumber})`; });
-ScriptingGlobals.add(function getProto(doc: any) { return Doc.GetProto(doc); });
-ScriptingGlobals.add(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); });
-ScriptingGlobals.add(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
-ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
-ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); });
-ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); });
-ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); });
-ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
-ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
-ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
-ScriptingGlobals.add(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); });
-ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; });
-ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
-ScriptingGlobals.add(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
+ScriptingGlobals.add(function idToDoc(id: string): any {
+    return DocServer.GetCachedRefField(id);
+});
+ScriptingGlobals.add(function renameAlias(doc: any) {
+    return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, '') + `(${doc.aliasNumber})`;
+});
+ScriptingGlobals.add(function getProto(doc: any) {
+    return Doc.GetProto(doc);
+});
+ScriptingGlobals.add(function getDocTemplate(doc?: any) {
+    return Doc.getDocTemplate(doc);
+});
+ScriptingGlobals.add(function getAlias(doc: any) {
+    return Doc.MakeAlias(doc);
+});
+ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) {
+    return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto);
+});
+ScriptingGlobals.add(function copyField(field: any) {
+    return Field.Copy(field);
+});
+ScriptingGlobals.add(function docList(field: any) {
+    return DocListCast(field);
+});
+ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added: Doc) {
+    return Doc.AddDocToList(doc, field, added);
+});
+ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) {
+    return Doc.SetInPlace(doc, field, value, false);
+});
+ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) {
+    return Doc.AreProtosEqual(doc1, doc2);
+});
+ScriptingGlobals.add(function undo() {
+    SelectionManager.DeselectAll();
+    return UndoManager.Undo();
+});
+ScriptingGlobals.add(function redo() {
+    SelectionManager.DeselectAll();
+    return UndoManager.Redo();
+});
+ScriptingGlobals.add(function DOC(id: string) {
+    console.log("Can't parse a document id in a script");
+    return 'invalid';
+});
+ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) {
+    return Doc.assignDocToField(doc, field, id);
+});
+ScriptingGlobals.add(function docCast(doc: FieldResult): any {
+    return DocCastAsync(doc);
+});
 ScriptingGlobals.add(function activePresentationItem() {
     const curPres = CurrentUserUtils.ActivePresentation;
     return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
 });
 ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
-    const docs = SelectionManager.Views().map(dv => dv.props.Document).
-        filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP &&
-            (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
+    const docs = SelectionManager.Views()
+        .map(dv => dv.props.Document)
+        .filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
     return docs.length ? new List(docs) : prevValue;
 });
-ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: any, modifiers: "match" | "check" | "x" | "remove") { Doc.setDocFilter(container, key, value, modifiers); });
-ScriptingGlobals.add(function setDocRangeFilter(container: Doc, key: string, range: number[]) { Doc.setDocRangeFilter(container, key, range); });
+ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: any, modifiers: 'match' | 'check' | 'x' | 'remove') {
+    Doc.setDocFilter(container, key, value, modifiers);
+});
+ScriptingGlobals.add(function setDocRangeFilter(container: Doc, key: string, range: number[]) {
+    Doc.setDocRangeFilter(container, key, range);
+});
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index a19be5df9..bf055cd8b 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -1,48 +1,46 @@
-import { AssertionError } from "assert";
-import { docs_v1 } from "googleapis";
-import { Fragment, Mark, Node } from "prosemirror-model";
-import { sinkListItem } from "prosemirror-schema-list";
-import { Utils, DashColor } from "../Utils";
-import { Docs, DocUtils } from "../client/documents/Documents";
-import { schema } from "../client/views/nodes/formattedText/schema_rts";
-import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils";
-import { DocServer } from "../client/DocServer";
-import { Networking } from "../client/Network";
-import { FormattedTextBox } from "../client/views/nodes/formattedText/FormattedTextBox";
-import { Doc, Opt } from "./Doc";
-import { Id } from "./FieldSymbols";
-import { RichTextField } from "./RichTextField";
-import { Cast, StrCast } from "./Types";
+import { AssertionError } from 'assert';
+import { docs_v1 } from 'googleapis';
+import { Fragment, Mark, Node } from 'prosemirror-model';
+import { sinkListItem } from 'prosemirror-schema-list';
+import { Utils, DashColor } from '../Utils';
+import { Docs, DocUtils } from '../client/documents/Documents';
+import { schema } from '../client/views/nodes/formattedText/schema_rts';
+import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils';
+import { DocServer } from '../client/DocServer';
+import { Networking } from '../client/Network';
+import { FormattedTextBox } from '../client/views/nodes/formattedText/FormattedTextBox';
+import { Doc, Opt } from './Doc';
+import { Id } from './FieldSymbols';
+import { RichTextField } from './RichTextField';
+import { Cast, StrCast } from './Types';
 import Color = require('color');
-import { EditorState, TextSelection, Transaction } from "prosemirror-state";
-import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClientUtils";
+import { EditorState, TextSelection, Transaction } from 'prosemirror-state';
+import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils';
 
 export namespace RichTextUtils {
-
-    const delimiter = "\n";
-    const joiner = "";
-
+    const delimiter = '\n';
+    const joiner = '';
 
     export const Initialize = (initial?: string) => {
         const content: any[] = [];
         const state = {
             doc: {
-                type: "doc",
+                type: 'doc',
                 content,
             },
             selection: {
-                type: "text",
+                type: 'text',
                 anchor: 0,
-                head: 0
-            }
+                head: 0,
+            },
         };
         if (initial && initial.length) {
             content.push({
-                type: "paragraph",
+                type: 'paragraph',
                 content: {
-                    type: "text",
-                    text: initial
-                }
+                    type: 'text',
+                    text: initial,
+                },
             });
             state.selection.anchor = state.selection.head = initial.length + 1;
         }
@@ -56,8 +54,8 @@ export namespace RichTextUtils {
     export const ToPlainText = (state: EditorState) => {
         // Because we're working with plain text, just concatenate all paragraphs
         const content = state.doc.content;
-        const paragraphs: Node<any>[] = [];
-        content.forEach(node => node.type.name === "paragraph" && paragraphs.push(node));
+        const paragraphs: Node[] = [];
+        content.forEach(node => node.type.name === 'paragraph' && paragraphs.push(node));
 
         // Functions to flatten ProseMirror paragraph objects (and their components) to plain text
         // Concatentate paragraphs and string the result together
@@ -80,22 +78,21 @@ export namespace RichTextUtils {
         // Preserve the current state, but re-write the content to be the blocks
         const parsed = JSON.parse(oldState ? oldState.Data : Initialize());
         parsed.doc.content = elements.map(text => {
-            const paragraph: any = { type: "paragraph" };
-            text.length && (paragraph.content = [{ type: "text", marks: [], text }]); // An empty paragraph gets treated as a line break
+            const paragraph: any = { type: 'paragraph' };
+            text.length && (paragraph.content = [{ type: 'text', marks: [], text }]); // An empty paragraph gets treated as a line break
             return paragraph;
         });
 
         // If the new content is shorter than the previous content and selection is unchanged, may throw an out of bounds exception, so we reset it
-        parsed.selection = { type: "text", anchor: 1, head: 1 };
+        parsed.selection = { type: 'text', anchor: 1, head: 1 };
 
         // Export the ProseMirror-compatible state object we've just built
         return JSON.stringify(parsed);
     };
 
     export namespace GoogleDocs {
-
         export const Export = async (state: EditorState): Promise<GoogleApiClientUtils.Docs.Content> => {
-            const nodes: (Node<any> | null)[] = [];
+            const nodes: (Node | null)[] = [];
             const text = ToPlainText(state);
             state.doc.content.forEach(node => {
                 if (!node.childCount) {
@@ -126,10 +123,10 @@ export namespace RichTextUtils {
                     return { baseUrl: embeddedObject.imageProperties!.contentUri! };
                 });
 
-                const uploads = await Networking.PostToServer("/googlePhotosMediaGet", { mediaItems });
+                const uploads = await Networking.PostToServer('/googlePhotosMediaGet', { mediaItems });
 
                 if (uploads.length !== mediaItems.length) {
-                    throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: "Error with internally uploading inlineObjects!" });
+                    throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: 'Error with internally uploading inlineObjects!' });
                 }
 
                 for (let i = 0; i < objects.length; i++) {
@@ -144,14 +141,14 @@ export namespace RichTextUtils {
                         title: embeddedObject.title || `Imported Image from ${document.title}`,
                         width,
                         url: Utils.prepend(_m.client),
-                        agnostic: Utils.prepend(agnostic.client)
+                        agnostic: Utils.prepend(agnostic.client),
                     });
                 }
             }
             return inlineObjectMap;
         };
 
-        type BulletPosition = { value: number, sinks: number };
+        type BulletPosition = { value: number; sinks: number };
 
         interface MediaItem {
             baseUrl: string;
@@ -172,7 +169,7 @@ export namespace RichTextUtils {
             const lists: ListGroup[] = [];
             const indentMap = new Map<ListGroup, BulletPosition[]>();
             let globalOffset = 0;
-            const nodes: Node<any>[] = [];
+            const nodes: Node[] = [];
             for (const element of structured) {
                 if (Array.isArray(element)) {
                     lists.push(element);
@@ -182,7 +179,7 @@ export namespace RichTextUtils {
                         const sinks = paragraph.bullet!;
                         positions.push({
                             value: position + globalOffset,
-                            sinks
+                            sinks,
                         });
                         position += item.nodeSize;
                         globalOffset += 2 * sinks;
@@ -191,13 +188,13 @@ export namespace RichTextUtils {
                     indentMap.set(element, positions);
                     nodes.push(list(state.schema, items));
                 } else {
-                    if (element.contents.some(child => "inlineObjectId" in child)) {
+                    if (element.contents.some(child => 'inlineObjectId' in child)) {
                         const group = element.contents;
                         group.forEach((child, i) => {
-                            let node: Opt<Node<any>>;
-                            if ("inlineObjectId" in child) {
+                            let node: Opt<Node>;
+                            if ('inlineObjectId' in child) {
                                 node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote);
-                            } else if ("content" in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) {
+                            } else if ('content' in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) {
                                 node = paragraphNode(state.schema, [child]);
                             }
                             if (node) {
@@ -215,7 +212,7 @@ export namespace RichTextUtils {
             state = state.apply(state.tr.replaceWith(0, 2, nodes));
 
             const sink = sinkListItem(state.schema.nodes.list_item);
-            const dispatcher = (tr: Transaction) => state = state.apply(tr);
+            const dispatcher = (tr: Transaction) => (state = state.apply(tr));
             for (const list of lists) {
                 for (const pos of indentMap.get(list)!) {
                     const resolved = state.doc.resolve(pos.value);
@@ -252,17 +249,17 @@ export namespace RichTextUtils {
         };
 
         const listItem = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
-            return schema.node("list_item", null, paragraphNode(schema, runs));
+            return schema.node('list_item', null, paragraphNode(schema, runs));
         };
 
         const list = (schema: any, items: Node[]): Node => {
-            return schema.node("ordered_list", { mapStyle: "bullet" }, items);
+            return schema.node('ordered_list', { mapStyle: 'bullet' }, items);
         };
 
         const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
             const children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined);
             const fragment = children.length ? Fragment.from(children) : undefined;
-            return schema.node("paragraph", null, fragment);
+            return schema.node('paragraph', null, fragment);
         };
 
         const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => {
@@ -278,7 +275,7 @@ export namespace RichTextUtils {
             } else {
                 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) => {
@@ -287,10 +284,10 @@ export namespace RichTextUtils {
         };
 
         const StyleToMark = new Map<keyof docs_v1.Schema$TextStyle, keyof typeof schema.marks>([
-            ["bold", "strong"],
-            ["italic", "em"],
-            ["foregroundColor", "pFontColor"],
-            ["fontSize", "pFontSize"]
+            ['bold', 'strong'],
+            ['italic', 'em'],
+            ['foregroundColor', 'pFontColor'],
+            ['fontSize', 'pFontSize'],
         ]);
 
         const styleToMarks = (schema: any, textStyle?: docs_v1.Schema$TextStyle) => {
@@ -301,21 +298,21 @@ export namespace RichTextUtils {
             Object.keys(textStyle).forEach(key => {
                 let value: any;
                 const targeted = key as keyof docs_v1.Schema$TextStyle;
-                if (value = textStyle[targeted]) {
+                if ((value = textStyle[targeted])) {
                     const attributes: any = {};
                     let converted = StyleToMark.get(targeted) || targeted;
 
                     value.url && (attributes.href = value.url);
                     if (value.color) {
                         const object = value.color.rgbColor;
-                        attributes.color = Color.rgb(["red", "green", "blue"].map(color => object[color] * 255 || 0)).hex();
+                        attributes.color = Color.rgb(['red', 'green', 'blue'].map(color => object[color] * 255 || 0)).hex();
                     }
                     if (value.magnitude) {
                         attributes.fontSize = value.magnitude;
                     }
 
-                    if (converted === "weightedFontFamily") {
-                        converted = ImportFontFamilyMapping.get(value.fontFamily) || "timesNewRoman";
+                    if (converted === 'weightedFontFamily') {
+                        converted = ImportFontFamilyMapping.get(value.fontFamily) || 'timesNewRoman';
                     }
 
                     const mapped = schema.marks[converted];
@@ -332,38 +329,38 @@ export namespace RichTextUtils {
         };
 
         const MarkToStyle = new Map<keyof typeof schema.marks, keyof docs_v1.Schema$TextStyle>([
-            ["strong", "bold"],
-            ["em", "italic"],
-            ["pFontColor", "foregroundColor"],
-            ["pFontSize", "fontSize"],
-            ["timesNewRoman", "weightedFontFamily"],
-            ["georgia", "weightedFontFamily"],
-            ["comicSans", "weightedFontFamily"],
-            ["tahoma", "weightedFontFamily"],
-            ["impact", "weightedFontFamily"]
+            ['strong', 'bold'],
+            ['em', 'italic'],
+            ['pFontColor', 'foregroundColor'],
+            ['pFontSize', 'fontSize'],
+            ['timesNewRoman', 'weightedFontFamily'],
+            ['georgia', 'weightedFontFamily'],
+            ['comicSans', 'weightedFontFamily'],
+            ['tahoma', 'weightedFontFamily'],
+            ['impact', 'weightedFontFamily'],
         ]);
 
         const ExportFontFamilyMapping = new Map<string, string>([
-            ["timesNewRoman", "Times New Roman"],
-            ["arial", "Arial"],
-            ["georgia", "Georgia"],
-            ["comicSans", "Comic Sans MS"],
-            ["tahoma", "Tahoma"],
-            ["impact", "Impact"]
+            ['timesNewRoman', 'Times New Roman'],
+            ['arial', 'Arial'],
+            ['georgia', 'Georgia'],
+            ['comicSans', 'Comic Sans MS'],
+            ['tahoma', 'Tahoma'],
+            ['impact', 'Impact'],
         ]);
 
         const ImportFontFamilyMapping = new Map<string, string>([
-            ["Times New Roman", "timesNewRoman"],
-            ["Arial", "arial"],
-            ["Georgia", "georgia"],
-            ["Comic Sans MS", "comicSans"],
-            ["Tahoma", "tahoma"],
-            ["Impact", "impact"]
+            ['Times New Roman', 'timesNewRoman'],
+            ['Arial', 'arial'],
+            ['Georgia', 'georgia'],
+            ['Comic Sans MS', 'comicSans'],
+            ['Tahoma', 'tahoma'],
+            ['Impact', 'impact'],
         ]);
 
-        const ignored = ["user_mark"];
+        const ignored = ['user_mark'];
 
-        const marksToStyle = async (nodes: (Node<any> | null)[]): Promise<docs_v1.Schema$Request[]> => {
+        const marksToStyle = async (nodes: (Node | null)[]): Promise<docs_v1.Schema$Request[]> => {
             const requests: docs_v1.Schema$Request[] = [];
             let position = 1;
             for (const node of nodes) {
@@ -376,25 +373,25 @@ export namespace RichTextUtils {
                 const information: LinkInformation = {
                     startIndex: position,
                     endIndex: position + nodeSize,
-                    textStyle
+                    textStyle,
                 };
-                let mark: Mark<any>;
+                let mark: Mark;
                 const markMap = BuildMarkMap(marks);
                 for (const markName of Object.keys(schema.marks)) {
                     if (ignored.includes(markName) || !(mark = markMap[markName])) {
                         continue;
                     }
-                    let converted = MarkToStyle.get(markName) || markName as keyof docs_v1.Schema$TextStyle;
+                    let converted = MarkToStyle.get(markName) || (markName as keyof docs_v1.Schema$TextStyle);
                     let value: any = true;
                     if (!converted) {
                         continue;
                     }
                     const { attrs } = mark;
                     switch (converted) {
-                        case "link":
-                            let url = attrs.allLinks.length ? attrs.allLinks[0].href : "";
-                            const delimiter = "/doc/";
-                            const alreadyShared = "?sharing=true";
+                        case 'link':
+                            let url = attrs.allLinks.length ? attrs.allLinks[0].href : '';
+                            const delimiter = '/doc/';
+                            const alreadyShared = '?sharing=true';
                             if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) {
                                 const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]);
                                 if (linkDoc instanceof Doc) {
@@ -411,41 +408,43 @@ export namespace RichTextUtils {
                             textStyle.foregroundColor = fromRgb.blue;
                             textStyle.bold = true;
                             break;
-                        case "fontSize":
-                            value = { magnitude: attrs.fontSize, unit: "PT" };
+                        case 'fontSize':
+                            value = { magnitude: attrs.fontSize, unit: 'PT' };
                             break;
-                        case "foregroundColor":
+                        case 'foregroundColor':
                             value = fromHex(attrs.color);
                             break;
-                        case "weightedFontFamily":
+                        case 'weightedFontFamily':
                             value = { fontFamily: ExportFontFamilyMapping.get(markName) };
                     }
                     let matches: RegExpExecArray | null;
                     if ((matches = /p(\d+)/g.exec(markName)) !== null) {
-                        converted = "fontSize";
-                        value = { magnitude: parseInt(matches[1].replace("px", "")), unit: "PT" };
+                        converted = 'fontSize';
+                        value = { magnitude: parseInt(matches[1].replace('px', '')), unit: 'PT' };
                     }
                     textStyle[converted] = value;
                 }
                 if (Object.keys(textStyle).length) {
                     requests.push(EncodeStyleUpdate(information));
                 }
-                if (node.type.name === "image") {
+                if (node.type.name === 'image') {
                     const width = attrs.width;
-                    requests.push(await EncodeImage({
-                        startIndex: position + nodeSize - 1,
-                        uri: attrs.agnostic,
-                        width: Number(typeof width === "string" ? width.replace("px", "") : width)
-                    }));
+                    requests.push(
+                        await EncodeImage({
+                            startIndex: position + nodeSize - 1,
+                            uri: attrs.agnostic,
+                            width: Number(typeof width === 'string' ? width.replace('px', '') : width),
+                        })
+                    );
                 }
                 position += nodeSize;
             }
             return requests;
         };
 
-        const BuildMarkMap = (marks: Mark<any>[]) => {
-            const markMap: { [type: string]: Mark<any> } = {};
-            marks.forEach(mark => markMap[mark.type.name] = mark);
+        const BuildMarkMap = (marks: readonly Mark[]) => {
+            const markMap: { [type: string]: Mark } = {};
+            marks.forEach(mark => (markMap[mark.type.name] = mark));
             return markMap;
         };
 
@@ -462,23 +461,21 @@ export namespace RichTextUtils {
         }
 
         namespace fromRgb {
-
             export const convert = (red: number, green: number, blue: number): docs_v1.Schema$OptionalColor => {
                 return {
                     color: {
                         rgbColor: {
                             red: red / 255,
                             green: green / 255,
-                            blue: blue / 255
-                        }
-                    }
+                            blue: blue / 255,
+                        },
+                    },
                 };
             };
 
             export const red = convert(255, 0, 0);
             export const green = convert(0, 255, 0);
             export const blue = convert(0, 0, 255);
-
         }
 
         const fromHex = (color: string): docs_v1.Schema$OptionalColor => {
@@ -490,10 +487,10 @@ export namespace RichTextUtils {
             const { startIndex, endIndex, textStyle } = information;
             return {
                 updateTextStyle: {
-                    fields: "*",
+                    fields: '*',
                     range: { startIndex, endIndex },
-                    textStyle
-                } as docs_v1.Schema$UpdateTextStyleRequest
+                    textStyle,
+                } as docs_v1.Schema$UpdateTextStyleRequest,
             };
         };
 
@@ -507,13 +504,12 @@ export namespace RichTextUtils {
                 return {
                     insertInlineImage: {
                         uri: baseUrls[0],
-                        objectSize: { width: { magnitude: width, unit: "PT" } },
-                        location: { index: startIndex }
-                    }
+                        objectSize: { width: { magnitude: width, unit: 'PT' } },
+                        location: { index: startIndex },
+                    },
                 };
             }
             return {};
         };
     }
-
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3-70-g09d2

