From 873dac83d1c5b031878c469e0eb68e0d1dd43177 Mon Sep 17 00:00:00 2001
From: Geireann <60007097+geireann@users.noreply.github.com>
Date: Tue, 15 Feb 2022 16:49:04 -0500
Subject: added a single comment

---
 src/client/views/search/SearchBox.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 09cfb2077..d23aa1d3b 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -118,6 +118,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
         this.selectElement(doc, () => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, false));
     });
 
+    // TODO: nda -- Change this method to change what happens when you click on the item.
     makeLink = action((linkTo: Doc) => {
         if (this.props.linkFrom) {
             const linkFrom = this.props.linkFrom();
-- 
cgit v1.2.3-70-g09d2


From 59a22f3a007b201e68ac2f3e1d62e0ca5e66488a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 28 Apr 2022 12:13:44 -0400
Subject: fixed feedback dragging link button. fixed warnings.  added some
 assymetric linkRelationships.

---
 src/client/documents/Documents.ts                              |  3 ++-
 src/client/util/DocumentManager.ts                             |  2 +-
 src/client/util/LinkManager.ts                                 |  2 +-
 src/client/views/SidebarAnnos.tsx                              |  2 +-
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../views/collections/collectionFreeForm/MarqueeView.tsx       |  2 +-
 src/client/views/linking/LinkMenu.tsx                          |  4 +---
 src/client/views/nodes/DocumentLinksButton.scss                |  2 ++
 src/client/views/nodes/DocumentLinksButton.tsx                 | 10 +++++-----
 src/client/views/nodes/DocumentView.tsx                        |  4 ++--
 src/client/views/nodes/ImageBox.tsx                            |  2 +-
 src/client/views/nodes/PDFBox.tsx                              |  4 ++--
 src/client/views/nodes/formattedText/RichTextRules.ts          |  2 +-
 src/client/views/search/SearchBox.tsx                          |  2 +-
 src/server/ApiManagers/UploadManager.ts                        |  4 ++--
 15 files changed, 24 insertions(+), 23 deletions(-)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0bb873667..087d32a60 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1094,10 +1094,11 @@ export namespace DocUtils {
     export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) {
         broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1);
         return DocUtils.ActiveRecordings.map(audio =>
-            DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording link", "recording timeline"));
+            DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline"));
     }
 
     export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
+        if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? "Commentary:Comments On" : "link";
         const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return;
         if (target.doc === Doc.UserDoc()) return undefined;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 412d5a169..b6c28d2fe 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -167,7 +167,7 @@ export class DocumentManager {
     ): Promise<void> => {
         originalTarget = originalTarget ?? targetDoc;
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
-        var docView = getFirstDocView(targetDoc, originatingDoc);
+        const docView = getFirstDocView(targetDoc, originatingDoc);
         const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
         const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field
         var wasHidden = resolvedTarget.hidden;
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 7ba7a1d4c..9445533dc 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -152,7 +152,7 @@ export class LinkManager {
         this.relatedLinker(anchor).forEach(link => {
             if (link.linkRelationship && link.linkRelationship !== "-ungrouped-") {
                 const relation = StrCast(link.linkRelationship);
-                const anchorRelation = relation.indexOf(":") ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
+                const anchorRelation = relation.indexOf(":") !== -1 ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
                 const group = anchorGroups.get(anchorRelation);
                 anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);
             } else {
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 43a02d029..04c0565ea 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -61,7 +61,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
         this.allMetadata.map(tag => target[tag] = tag);
-        DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup");
+        DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline comment:comment on");
         this.addDocument(target);
         this._stackRef.current?.focusDocument(target);
     }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f0b3d70a0..3f72052ae 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -278,7 +278,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) { // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
                 const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
                 this.props.addDocument?.(source);
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed
+                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkDragData.linkSourceGetAnchor() }, { doc: source }, "annotated by:annotation of", ""); // TODODO this is where in text links get passed
             }
             e.stopPropagation();  //  do nothing if link is dropped into any freeform view parent of dragged document
             return true;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 2174aaf10..5f303b428 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -521,7 +521,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         });
         const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
-        DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summarizing", "");
+        DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
 
         portal.hidden = true;
         this.props.addDocument?.(portal);
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index a564c59d3..17d28e886 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -1,9 +1,7 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc } from "../../../fields/Doc";
 import { LinkManager } from "../../util/LinkManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import { DocumentLinksButton } from "../nodes/DocumentLinksButton";
 import { DocumentView } from "../nodes/DocumentView";
 import { LinkDocPreview } from "../nodes/LinkDocPreview";
 import { LinkEditor } from "./LinkEditor";
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 9ab3171d3..0f3eb14bc 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -2,7 +2,9 @@
 
 .documentLinksButton-wrapper {
     transform-origin: top left;
+    width: 100%;
 }
+
 .documentLinksButton-menu {
     width: 100%;
     height: 100%;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index d2e6e8579..7f69adf6c 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -160,8 +160,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
             endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink;
             startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink;
             const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink },
-                DocumentLinksButton.AnnotationId ? "hypothes.is annotation" :
-                    endLink.type === DocumentType.RTF ? "Commentary:Comments On" : "link", undefined, undefined, true);
+                DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : undefined, undefined, undefined, true);
 
             LinkManager.currentLink = linkDoc;
 
@@ -245,9 +244,9 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 </div>
             </div>
             :
-            <div className="documentLinksButton-menu" ref={this._linkButton}>
+            <div className="documentLinksButton-menu" >
                 {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? //if the origin node is not this node
-                    <div className={"documentLinksButton-endLink"}
+                    <div className={"documentLinksButton-endLink"} ref={this._linkButton}
                         onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
                         onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}>
                         <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
@@ -256,7 +255,8 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                 }
                 {
                     this.props.InMenu && this.props.StartLink ? //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
-                        <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
+                        <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton}
+                            onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
                             <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                         </div>
                         :
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9d738180b..8570f6fff 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -663,7 +663,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
                 const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, "link", undefined, undefined, undefined, [de.x, de.y]);
+                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y]);
             }
         }
     }
@@ -674,7 +674,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document);
         if (!portalLink) {
             const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + " [Portal]" });
-            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to");
+            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to:portal from");
         }
         this.Document.followLinkLocation = "inPlace";
         this.Document.followLinkZoom = true;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index aadad5ffa..5982d4d66 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -152,7 +152,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         croppingProto.isPrototype = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
-        croppingProto.layout = ImageBox.LayoutString("data")
+        croppingProto.layout = ImageBox.LayoutString("data");
         croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
         croppingProto["data-nativeWidth"] = anchw;
         croppingProto["data-nativeHeight"] = anchh;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b556b0ef1..668c6c8fc 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -160,11 +160,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public backPage = () => {
         this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1);
         return true;
-    };
+    }
     public forwardPage = () => {
         this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + "-numPages"]), (NumCast(this.Document._curPage) || 1) + 1);
         return true;
-    };
+    }
     public gotoPage = (p: number) => this.Document._curPage = p;
 
     @undoBatch
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 00c03875b..427e05edb 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -275,7 +275,7 @@ export class RichTextRules {
                                     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", undefined);
+                                DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, "portal to:portal from", undefined);
 
                                 const fstate = this.TextBox.EditorView?.state;
                                 if (fstate && selection) {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 5fe2a5ab1..9257cb75e 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -112,7 +112,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         if (this.props.linkFrom) {
             const linkFrom = this.props.linkFrom();
             if (linkFrom) {
-                DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo }, "Link");
+                DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
             }
         }
     });
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 5e0cd67cc..83a2bc79b 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -14,7 +14,7 @@ import { normalize } from "path";
 import RouteSubscriber from "../RouteSubscriber";
 const imageDataUri = require('image-data-uri');
 import { SolrManager } from "./SearchManager";
-let fs = require('fs');
+const fs = require('fs');
 
 export enum Directory {
     parsed_files = "parsed_files",
@@ -265,7 +265,7 @@ export default class UploadManager extends ApiManager {
                 }
                 if (deleteFiles) {
                     const path = serverPathToFile(Directory.images, "");
-                    let regex = new RegExp(`${deleteFiles}.*`);
+                    const regex = new RegExp(`${deleteFiles}.*`);
                     fs.readdirSync(path).filter((f: any) => regex.test(f)).map((f: any) => fs.unlinkSync(path + f));
                 }
                 return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => {
-- 
cgit v1.2.3-70-g09d2


From f072a9f2c1ad69eb8ae4242fa5d2e18bbd94f6ef Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 6 May 2022 11:45:44 -0400
Subject: changed following links to open up icons all the way down to the
 target document. allow StartLink to work with webBox w/ text selection. 
 fixed web clippings to have a cursor and grab keyDown events.

---
 src/client/util/DocumentManager.ts                 | 42 +++++++++++++++-------
 .../util/Import & Export/DirectoryImportBox.tsx    |  2 +-
 src/client/util/LinkManager.ts                     | 11 +++---
 src/client/util/SharingManager.tsx                 | 30 ++++++++--------
 src/client/views/DocumentDecorations.tsx           | 13 ++++---
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/collections/TabDocView.tsx        |  2 +-
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collectionLinear/CollectionLinearView.tsx      |  2 +-
 .../collectionSchema/CollectionSchemaCells.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 src/client/views/nodes/LinkDescriptionPopup.tsx    |  1 +
 src/client/views/nodes/PDFBox.tsx                  |  1 +
 src/client/views/nodes/WebBox.scss                 |  3 ++
 src/client/views/nodes/WebBox.tsx                  | 11 +++---
 src/client/views/nodes/trails/PresBox.tsx          |  4 +--
 src/client/views/search/SearchBox.tsx              |  2 +-
 18 files changed, 84 insertions(+), 51 deletions(-)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index b6c28d2fe..aeddc3249 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -156,7 +156,7 @@ export class DocumentManager {
         targetDoc: Doc,        // document to display
         willZoom: boolean,     // whether to zoom doc to take up most of screen
         createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist
-        docContext?: Doc,  // context to load that should contain the target
+        docContext: Doc[],  // context to load that should contain the target
         linkDoc?: Doc,   // link that's being followed
         closeContextIfNotFound: boolean = false, // after opening a context where the document should be, this determines whether the context should be closed if the Doc isn't actually there
         originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc
@@ -190,31 +190,40 @@ export class DocumentManager {
             finished?.();
         };
         const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc);
-        const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
-        const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
+        const contextDocs = docContext.length ? await DocListCastAsync(docContext[0].data) : undefined;
+        const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext.lastElement() : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
         const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||
             (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
-        if ((!docView && targetDoc.type !== DocumentType.MARKER) && annoContainerView) {
+        if (annoContainerView) {
             if (annoContainerView.props.Document.layoutKey === "layout_icon") {
-                annoContainerView.iconify(() => this.jumpToDocument(
-                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
-                    finished, originalTarget, noSelect, presZoom));
+                annoContainerView.iconify(() => annoContainerView.focus(targetDoc, {
+                    originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
+                        new Promise<ViewAdjustment>(res => {
+                            focusAndFinish(true);
+                            res(ViewAdjustment.doNothing);
+                        })
+                }));
                 return;
-            } else {
+            } else if (!docView && targetDoc.type !== DocumentType.MARKER) {
                 annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
             }
         }
         if (focusView) {
             !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
-            focusView.focus(targetDoc, {
+            const doFocus = (forceDidFocus: boolean) => focusView.focus(originalTarget ?? targetDoc, {
                 originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
                     new Promise<ViewAdjustment>(res => {
-                        focusAndFinish(didFocus);
+                        focusAndFinish(forceDidFocus || didFocus);
                         res(ViewAdjustment.doNothing);
                     })
             });
+            if (focusView.props.Document.layoutKey === "layout_icon") {
+                focusView.iconify(() => doFocus(true));
+            } else {
+                doFocus(false);
+            }
         } else {
             if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
                 createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
@@ -228,7 +237,7 @@ export class DocumentManager {
                             targetDocContext._viewTransition = undefined;
                             if (targetDocContext.layoutKey === "layout_icon") {
                                 targetDocContextView.iconify(() => this.jumpToDocument(
-                                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
+                                    resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
                                     finished, originalTarget, noSelect, presZoom));
                             }
                             return ViewAdjustment.doNothing;
@@ -263,7 +272,16 @@ export class DocumentManager {
                         };
                         setTimeout(() => findView(0), 0);
                     }
-                } else {  // there's no context view so we need to create one first and try again when that finishes
+                } else {
+                    if (docContext.length && docContext[0]?.layoutKey === "layout_icon") {
+                        const docContextView = this.getFirstDocumentView(docContext[0]);
+                        if (docContextView) {
+                            return docContextView.iconify(() => this.jumpToDocument(
+                                targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc,
+                                finished, originalTarget, noSelect, presZoom));
+                        }
+                    }
+                    // there's no context view so we need to create one first and try again when that finishes
                     const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget);
                     createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
                         finishFunc);
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 39e9251a5..37571ae01 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -161,7 +161,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
             await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
             Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
             !this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
-            DocumentManager.Instance.jumpToDocument(importContainer, true);
+            DocumentManager.Instance.jumpToDocument(importContainer, true, undefined, []);
         }
 
         runInAction(() => {
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 9445533dc..b28662a57 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,3 +1,4 @@
+import { validationResult } from "express-validator/check";
 import { action, observable, observe } from "mobx";
 import { computedFn } from "mobx-utils";
 import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc";
@@ -241,10 +242,12 @@ export class LinkManager {
                     } else {
                         const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
                         const containerDoc = containerAnnoDoc || target;
-                        const containerDocContext = Cast(containerDoc?.context, Doc, null);
-                        const targetContext = LightboxView.LightboxDoc ? containerAnnoDoc || containerDocContext : containerDocContext;
-                        const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
-                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, allFinished);
+                        var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : [] as Doc[];
+                        while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
+                            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+                        }
+                        const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]] : containerDocContext;
+                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
                     }
                 } else {
                     allFinished();
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6d7f7e8df..11450309a 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -370,11 +370,11 @@ export class SharingManager extends React.Component<{}> {
         if (!uniform) dropdownValues.unshift("-multiple-");
         if (override) dropdownValues.unshift("None");
         return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
-            (
-                <option key={permission} value={permission}>
-                    {permission}
-                </option>
-            )
+        (
+            <option key={permission} value={permission}>
+                {permission}
+            </option>
+        )
         );
     }
 
@@ -388,7 +388,7 @@ export class SharingManager extends React.Component<{}> {
                 onClick={() => {
                     let context: Opt<CollectionView>;
                     if (this.targetDoc && this.targetDocView && docs.length === 1 && (context = this.targetDocView.props.ContainingCollectionView)) {
-                        DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document);
+                        DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, [context.props.Document]);
                     }
                 }}
                 onPointerEnter={action(() => {
@@ -548,10 +548,10 @@ export class SharingManager extends React.Component<{}> {
                                 {this.sharingOptions(uniform)}
                             </select>
                         ) : (
-                                <div className={"permissions-dropdown"}>
-                                    {permissions}
-                                </div>
-                            )}
+                            <div className={"permissions-dropdown"}>
+                                {permissions}
+                            </div>
+                        )}
                     </div>
                 </div>
             );
@@ -572,7 +572,7 @@ export class SharingManager extends React.Component<{}> {
                         <div className="edit-actions">
                             <div className={"permissions-dropdown"}>
                                 Owner
-                        </div>
+                            </div>
                         </div>
                     </div>
                 ) : null,
@@ -622,10 +622,10 @@ export class SharingManager extends React.Component<{}> {
                                 {this.sharingOptions(uniform, group.title === "Override")}
                             </select>
                         ) : (
-                                <div className={"permissions-dropdown"}>
-                                    {permissions}
-                                </div>
-                            )}
+                            <div className={"permissions-dropdown"}>
+                                {permissions}
+                            </div>
+                        )}
                     </div>
                 </div>
             );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index eb2bcc18c..936946df0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -113,7 +113,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         }
     }
 
-    titleEntered = (e: React.KeyboardEvent) => e.key === "Enter" && (e.target as any).blur();
+    titleEntered = (e: React.KeyboardEvent) => {
+        if (e.key === "Enter") {
+            e.stopPropagation();
+            (e.target as any).blur();
+        }
+    }
 
     @action onTitleDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
@@ -288,7 +293,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
             const project = (p: number[], a: number[], b: number[]) => {
                 const atob = [b[0] - a[0], b[1] - a[1]];
-                const atop = [p[0] - a[0], p[1] - a[1]]; first
+                const atop = [p[0] - a[0], p[1] - a[1]];
                 const len = atob[0] * atob[0] + atob[1] * atob[1];
                 let dot = atop[0] * atob[0] + atop[1] * atob[1];
                 const t = dot / len;
@@ -501,7 +506,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     value={this._accumulatedTitle}
                     onBlur={e => this.titleBlur()}
                     onChange={action(e => this._accumulatedTitle = e.target.value)}
-                    onKeyPress={this.titleEntered} /> :
+                    onKeyDown={this.titleEntered} /> :
                 <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
@@ -521,7 +526,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
         const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : "";
 
-        const rotation = NumCast(seldoc.rootDoc._jitterRotation)
+        const rotation = NumCast(seldoc.rootDoc._jitterRotation);
         const rdeg = rotation / 180 * Math.PI;
 
         return (<div className={`documentDecorations${colorScheme}`}>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c794c73db..db51c54f8 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -133,7 +133,7 @@ export class MainView extends React.Component {
         window.addEventListener("paste", KeyManager.Instance.paste as any);
         document.addEventListener("dash", (e: any) => {  // event used by chrome plugin to tell Dash which document to focus on
             const id = FormattedTextBox.GetDocFromUrl(e.detail);
-            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null));
+            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined, []) : (null));
         });
         document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
         this.initEventListeners();
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 78b125e07..8dfc7edc7 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -256,7 +256,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 }
                 PresBox.Instance?._selectedArray.clear();
                 pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-                DocumentManager.Instance.jumpToDocument(doc, false, undefined);
+                DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
                 batch.end();
             });
         }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 72480b094..164021358 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -72,7 +72,7 @@ export enum TreeSort {
     Down = "down",
     Zindex = "z",
     None = "none"
-};
+}
 /**
  * Renders a treeView of a collection of documents
  * 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5d1e1892c..5b3a7db41 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1423,7 +1423,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     replaceCanvases = (oldDiv: HTMLElement, newDiv: HTMLElement) => {
-        if (oldDiv.childNodes) {
+        if (oldDiv.childNodes && newDiv) {
             for (let i = 0; i < oldDiv.childNodes.length; i++) {
                 this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
             }
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index e3d4f86c1..c615bfb8e 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -231,7 +231,7 @@ export class CollectionLinearView extends CollectionSubView() {
                         <span className="bottomPopup-text">
                             Currently playing:
                             {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
-                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true)}>
+                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
                                     {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")}
                                 </span>)}
                         </span>
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index c2bb3b3ac..adcd9e1e3 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -199,7 +199,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
             const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
             // Jump to the this document
-            DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext,
+            DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext ? [targetContext] : [],
                 undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc));
         }
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8570f6fff..02ea87103 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -663,7 +663,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
                 const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y]);
+                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
             }
         }
     }
@@ -857,6 +857,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     @observable _retryThumb = 1;
     thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+        !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
         !this._componentView?.isAnyChildContentActive?.() ? true : false
     @computed get contents() {
         TraceMobx();
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index a9d33f161..ccac66996 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -54,6 +54,7 @@ export class LinkDescriptionPopup extends React.Component<{}> {
                 top: LinkDescriptionPopup.popupY ? LinkDescriptionPopup.popupY : 350,
             }}>
             <input className="linkDescriptionPopup-input"
+                onKeyDown={e => e.stopPropagation()}
                 onKeyPress={e => e.key === "Enter" && this.onDismiss(true)}
                 placeholder={"(Optional) Enter link description..."}
                 onChange={(e) => this.descriptionChanged(e)}>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 69662d53a..48465976a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -75,6 +75,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     updateIcon = () => {
+        return; // currently we render pdf icons as text labels
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
         newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index b037e7220..d8dd074a5 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -156,6 +156,9 @@
             position: absolute;
             top: 0;
             left: 0;
+            cursor: text;
+            padding: 15px;
+            height: 100%
         }
 
         .webBox-cont {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 80961c92f..f441e5d36 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -207,8 +207,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     @action
-    createTextAnnotation = (sel: Selection, selRange: Range) => {
-        if (this._mainCont.current) {
+    createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
+        if (this._mainCont.current && selRange) {
             const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
@@ -235,7 +235,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
-        this.props.select(true);
+        this.props.select(false); // hack to preven the webBox from being replaced with a thumbnail that currently doesn't show the the anchors
         if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth);
         if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
@@ -252,7 +252,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
         this._initialScroll = NumCast(this.layoutDoc._scrollTop);
-        return 0;
+        return undefined;
     }
 
     getAnchor = () => {
@@ -279,7 +279,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
-                this._textAnnotationCreator = () => this.createTextAnnotation(sel, sel.getRangeAt(0));
+                this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX,
                     e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
             }
@@ -650,6 +650,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get content() {
         const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
         return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
+            onKeyDown={e => e.stopPropagation()}
             style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
             {this.urlContent}
         </div>;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 30ad43562..820d7ef96 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -389,11 +389,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             LightboxView.SetLightboxDoc(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
+            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
         } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
             //awaiting jump so that new scale can be found, since jumping is async     
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
+            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
         }
         // After navigating to the document, if it is added as a presPinView then it will
         // adjust the pan and scale to that of the pinView when it was added.
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 9257cb75e..3d36efbc4 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -365,7 +365,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * or opening it in a new tab.
      */
     selectElement = async (doc: Doc, finishFunc: () => void) => {
-        await DocumentManager.Instance.jumpToDocument(doc, true, undefined, undefined, undefined, undefined, undefined, finishFunc);
+        await DocumentManager.Instance.jumpToDocument(doc, true, undefined, [], undefined, undefined, undefined, finishFunc);
     }
 
     /**
-- 
cgit v1.2.3-70-g09d2


From e40a1e248da90af698d5ff64bd5d63d11211e6dc Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 13 May 2022 11:58:10 -0400
Subject: fixed crashing bug when quickly re-updating webbox (or other) icon. 
 fixed infinite loop in FilterBox.  changed TimeView to scale WebBox's.  fixed
 issues with timeView filters.  fixed bugs with webBox scrolling on startup,
 following links, etc.

---
 .../views/collections/CollectionTimeView.tsx       |  18 +--
 .../CollectionFreeFormLayoutEngines.tsx            |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   4 +-
 src/client/views/nodes/ComparisonBox.tsx           |   2 +-
 src/client/views/nodes/DocumentView.tsx            |  23 ++--
 src/client/views/nodes/FilterBox.tsx               |  10 +-
 src/client/views/nodes/WebBox.tsx                  | 136 +++++++++++----------
 src/client/views/pdf/Annotation.tsx                |  37 +++---
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 src/client/views/search/SearchBox.tsx              |   4 +-
 src/fields/Doc.ts                                  |  32 ++++-
 src/server/ApiManagers/UploadManager.ts            |  14 ++-
 12 files changed, 166 insertions(+), 118 deletions(-)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index e63ea7151..4f6f45d2f 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -10,6 +10,7 @@ import { ComputedField, ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
 import { DocumentManager } from "../../util/DocumentManager";
 import { ScriptingGlobals } from "../../util/ScriptingGlobals";
 import { ContextMenu } from "../ContextMenu";
@@ -128,6 +129,7 @@ export class CollectionTimeView extends CollectionSubView() {
         }
     }
 
+    dontScaleFilter = (doc: Doc) => doc.type === DocumentType.RTF;
     @computed get contents() {
         return <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.isContentActive() ? undefined : "none" }}
             onClick={this.contentsDown}>
@@ -137,6 +139,7 @@ export class CollectionTimeView extends CollectionSubView() {
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
+                dontScaleFilter={this.dontScaleFilter}
                 layoutEngine={this.layoutEngine} />
         </div>;
     }
@@ -174,7 +177,7 @@ export class CollectionTimeView extends CollectionSubView() {
             typeof (pair.layout[fieldKey]) === "string").filter(fieldKey => fieldKey[0] !== "_" && (fieldKey[0] !== "#" || fieldKey === "#") && (fieldKey === "tags" || fieldKey[0] === toUpper(fieldKey)[0])).map(fieldKey => keySet.add(fieldKey)));
         Array.from(keySet).map(fieldKey =>
             docItems.push({ description: ":" + fieldKey, event: () => this.layoutDoc._pivotField = fieldKey, icon: "compress-arrows-alt" }));
-        docItems.push({ description: ":(null)", event: () => this.layoutDoc._pivotField = undefined, icon: "compress-arrows-alt" });
+        docItems.push({ description: ":default", event: () => this.layoutDoc._pivotField = undefined, icon: "compress-arrows-alt" });
         ContextMenu.Instance.addItem({ description: "Pivot Fields ...", subitems: docItems, icon: "eye" });
         const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
         ContextMenu.Instance.displayMenu(x, y, ":");
@@ -234,16 +237,17 @@ export class CollectionTimeView extends CollectionSubView() {
 }
 
 ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
+    const pivotField = StrCast(pivotDoc._pivotField) || "author";
     let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
     const originalFilter = StrListCast(ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField));
     pivotDoc["_prevDocFilter" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField);
     pivotDoc["_prevDocRangeFilters" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docRangeFilters as ObjectField);
-    pivotDoc["_prevPivotFields" + prevFilterIndex] = pivotDoc._pivotField;
+    pivotDoc["_prevPivotFields" + prevFilterIndex] = pivotField;
     pivotDoc._prevFilterIndex = ++prevFilterIndex;
-    runInAction(() => {
-        pivotDoc._docFilters = new List();
+    pivotDoc._docFilters = new List();
+    setTimeout(action(() => {
         const filterVals = (bounds.payload as string[]);
-        filterVals.map(filterVal => Doc.setDocFilter(pivotDoc, StrCast(pivotDoc._pivotField), filterVal, "check"));
+        filterVals.map(filterVal => Doc.setDocFilter(pivotDoc, pivotField, filterVal, "check"));
         const pivotView = DocumentManager.Instance.getDocumentView(pivotDoc);
         if (pivotDoc && pivotView?.ComponentView instanceof CollectionTimeView && filterVals.length === 1) {
             if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) {
@@ -252,11 +256,11 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou
         }
         const newFilters = StrListCast(pivotDoc._docFilters);
         if (newFilters.length && originalFilter.length &&
-            newFilters[newFilters.length - 1] === originalFilter[originalFilter.length - 1]) {
+            newFilters.lastElement() === originalFilter.lastElement()) {
             pivotDoc._prevFilterIndex = --prevFilterIndex;
             pivotDoc["_prevDocFilter" + prevFilterIndex] = undefined;
             pivotDoc["_prevDocRangeFilters" + prevFilterIndex] = undefined;
             pivotDoc["_prevPivotFields" + prevFilterIndex] = undefined;
         }
-    });
+    }));
 });
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 16c7df311..9de2cfcf9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -147,7 +147,7 @@ export function computePivotLayout(
     const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
 
     let nonNumbers = 0;
-    const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField);
+    const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || "author";
     childPairs.map(pair => {
         const lval = pivotFieldKey === "#" || pivotFieldKey === "tags" ? Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(k => k.startsWith("#")).map(k => k.substring(1)) :
             Cast(pair.layout[pivotFieldKey], listSpec("string"), null);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 19b6612b0..4f79d19ad 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -75,6 +75,7 @@ export type collectionFreeformViewProps = {
     scaleField?: string;
     noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
     engineProps?: any;
+    dontScaleFilter?: (doc: Doc) => boolean; // whether this collection should scale documents to fit their panel vs just scrolling them
     dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not.  
     // However, this screws up interactions since only the top layer gets events.  so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents.
 };
@@ -1197,10 +1198,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             layerProvider={this.props.layerProvider}
             dataProvider={this.childDataProvider}
             sizeProvider={this.childSizeProvider}
-            freezeDimensions={this.props.childFreezeDimensions}
+            freezeDimensions={BoolCast(this.props.Document.childFreezeDimensions, this.props.childFreezeDimensions)}
             dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
             bringToFront={this.bringToFront}
             showTitle={this.props.childShowTitle}
+            dontScaleFilter={this.props.dontScaleFilter}
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.pointerEvents}
             jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index abb01c466..49cd39f43 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -87,7 +87,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             return whichDoc ? <>
                 <DocumentView
                     ref={(r) => {
-                        whichDoc !== targetDoc && r?.focus(targetDoc);
+                        whichDoc !== targetDoc && r?.focus(whichDoc);
                     }}
                     {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                     isContentActive={returnFalse}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e117be0c2..f30d093a1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -168,6 +168,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     radialMenu?: String[];
     LayoutTemplateString?: string;
     dontCenter?: "x" | "y" | "xy";
+    dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling
     ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
     NativeWidth?: () => number;
     NativeHeight?: () => number;
@@ -249,6 +250,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this));
         }
     }
+    @action
     cleanupHandlers(unbrush: boolean) {
         this._dropDisposer?.();
         this._multiTouchDisposer?.();
@@ -856,9 +858,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this.props.isContentActive()) ? true : undefined;
     }
     @observable _retryThumb = 1;
-    thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
-        !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
-        !this._componentView?.isAnyChildContentActive?.() ? true : false
+    thumbShown = () => {
+        return !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+            !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
+            !Doc.isBrushedHighlightedDegree(this.props.Document) &&
+            !this._componentView?.isAnyChildContentActive?.() ? true : false;
+    }
     @computed get contents() {
         TraceMobx();
         const audioView = !this.layoutDoc._showAudio ? (null) :
@@ -874,7 +879,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             {!this._retryThumb || !this.thumbShown() ? (null) :
-                <img style={{ background: "white", top: 0, position: "absolute" }} src={this.thumb} // + '?d=' + (new Date()).getTime()}
+                <img style={{ background: "white", top: 0, position: "relative" }} src={this.thumb} // + '?d=' + (new Date()).getTime()}
                     width={this.props.PanelWidth()} height={this.props.PanelHeight()}
                     onError={(e: any) => {
                         setTimeout(action(() => this._retryThumb = 0), 0);
@@ -1109,8 +1114,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     render() {
         TraceMobx();
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange"][highlightIndex];
-        const highlightStyle = ["solid", "dashed", "solid", "solid"][highlightIndex];
+        const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange", "lightBlue"][highlightIndex];
+        const highlightStyle = ["solid", "dashed", "solid", "solid", "solid"][highlightIndex];
         const excludeTypes = !this.props.treeViewDoc && highlightIndex < 3 ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
         let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
         highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]";  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
@@ -1126,8 +1131,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             onKeyDown={this.onKeyDown}
             onPointerDown={this.onPointerDown}
             onClick={this.onClick}
-            onPointerEnter={e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)}
-            onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)}
+            onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
+            onPointerLeave={action(e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document))}
             style={{
                 display: this.hidden ? "inline" : undefined,
                 borderRadius: this.borderRounding,
@@ -1207,7 +1212,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
     @computed get shouldNotScale() {
         return (this.fitWidth && !this.nativeWidth) ||
-            this.props.ContainingCollectionView?.collectionViewType === CollectionViewType.Time ||
+            this.props.dontScaleFilter?.(this.Document) ||
             this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
     }
     @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); }
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index dc0ab5ec2..78adf63cb 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -96,17 +96,15 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 runInAction(() => this._loaded = true);
             }, { fireImmediately: true });
     }
-    @observable _allDocs = [] as Doc[];
+
     @computed get allDocs() {
         // trace();
+        const allDocs = new Set<Doc>();
         const targetDoc = FilterBox.targetDoc;
         if (this._loaded && targetDoc) {
-            const allDocs = new Set<Doc>();
-            const activeTabs = FilterBox.targetDocChildren;
-            SearchBox.foreachRecursiveDoc(activeTabs, (depth, doc) => allDocs.add(doc));
-            setTimeout(action(() => this._allDocs = Array.from(allDocs)));
+            SearchBox.foreachRecursiveDoc(FilterBox.targetDocChildren, (depth, doc) => allDocs.add(doc));
         }
-        return this._allDocs;
+        return Array.from(allDocs);
     }
 
     @computed get _allFacets() {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 64655ba0b..af7a2b941 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -40,7 +40,6 @@ import React = require("react");
 const { CreateImage } = require("./WebBoxRenderer");
 const _global = (window /* browser */ || global /* node */) as any;
 const htmlToText = require("html-to-text");
-
 @observer
 export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
@@ -52,7 +51,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     private _keyInput = React.createRef<HTMLInputElement>();
-    private _initialScroll: Opt<number>;
+    private _initialScroll: Opt<number> = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop));
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _searchRef = React.createRef<HTMLInputElement>();
     private _searchString = "";
@@ -68,14 +67,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _iframeClick: HTMLIFrameElement | undefined = undefined;
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight, 1500);
+    @observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight);
     @computed get _url() { return this.webField?.toString() || ""; }
     @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; }
-    @computed get scrollHeight() { return this._scrollHeight; }
+    @computed get scrollHeight() { return Math.max(this.layoutDoc[HeightSym](), this._scrollHeight); }
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
     @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
-    @computed get webThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; }
+    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url }
 
     constructor(props: any) {
         super(props);
@@ -100,14 +99,49 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         return true;
     }
+    @action
     setScrollPos = (pos: number) => {
         if (!this._outerRef.current || this._outerRef.current.scrollHeight < pos) {
-            setTimeout(() => this.setScrollPos(pos), 250);
+            if (this._webPageHasBeenRendered) setTimeout(() => this.setScrollPos(pos), 250);
         } else {
             this._outerRef.current.scrollTop = pos;
             this._initialScroll = undefined;
         }
     }
+
+    lockout = false;
+    updateThumb = async () => {
+        const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
+        const scrollTop = NumCast(this.layoutDoc._scrollTop);
+        if (!this.lockout && this._iframe && !imageBitmap && scrollTop !== this.layoutDoc.thumbScrollTop) {
+            var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
+            if (!htmlString) {
+                htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
+            }
+            this.layoutDoc.thumb = undefined;
+            this.lockout = true; // lock to prevent multiple thumb updates.
+            const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
+            CreateImage(
+                this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl,
+                this._iframe.contentDocument?.styleSheets ?? [],
+                htmlString,
+                nativeWidth,
+                nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
+                scrollTop
+            ).then
+                ((data_url: any) => {
+                    VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
+                        returnedfilename => setTimeout(action(() => {
+                            this.lockout = false;
+                            this.layoutDoc.thumb = new ImageField(returnedfilename);
+                            this.layoutDoc.thumbScrollTop = scrollTop
+                        }), 500));
+                })
+                .catch(function (error: any) {
+                    console.error('oops, something went wrong!', error);
+                });
+        }
+    }
     async componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
@@ -117,45 +151,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`);
             this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`);
         });
-        reaction(() => this.props.isSelected(),
+        reaction(() => this.props.isSelected() || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
             async (selected) => {
                 if (selected) {
                     this._webPageHasBeenRendered = true;
-                    setTimeout(action(() => {
-                        this._scrollHeight = Math.max(this.scrollHeight, this._iframe?.contentDocument?.body.scrollHeight || 0);
-                        if (this._initialScroll !== undefined) {
-                            this.setScrollPos(this._initialScroll);
-                        }
-                    }));
                 } else if ((!this.props.isContentActive() || SnappingManager.GetIsDragging()) &&   // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
                     !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
                     LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
-                    const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
-                    if (this._iframe && !imageBitmap) {
-                        var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
-                        if (!htmlString) {
-                            htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
-                        }
-                        this.layoutDoc.thumb = undefined;
-                        const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
-                        CreateImage(
-                            this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl,
-                            this._iframe.contentDocument?.styleSheets ?? [],
-                            htmlString,
-                            nativeWidth,
-                            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
-                            NumCast(this.layoutDoc._scrollTop)
-                        ).then
-                            ((data_url: any) => {
-                                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
-                                    returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500));
-                            })
-                            .catch(function (error: any) {
-                                console.error('oops, something went wrong!', error);
-                            });
-                    }
+                    this.updateThumb();
                 }
-            }, { fireImmediately: this.props.isSelected() });
+            }, { fireImmediately: this.props.isSelected() || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegree(this.props.Document) ? true : false) });
 
         this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight,
             autoHeight => {
@@ -183,21 +188,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
 
-        var quickScroll = true;
         this._disposers.scrollReaction = reaction(() => NumCast(this.layoutDoc._scrollTop),
             (scrollTop) => {
-                if (quickScroll) this._initialScroll = scrollTop;
-                else {
-                    const viewTrans = StrCast(this.Document._viewTransition);
-                    const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
-                    const durationSecStr = viewTrans.match(/([0-9.]*)s/);
-                    const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
-                    this.goTo(scrollTop, duration);
-                }
+                const viewTrans = StrCast(this.Document._viewTransition);
+                const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
+                const durationSecStr = viewTrans.match(/([0-9.]*)s/);
+                const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
+                this.goTo(scrollTop, duration);
             },
             { fireImmediately: true }
         );
-        quickScroll = false;
     }
     @action componentWillUnmount() {
         Object.values(this._disposers).forEach(disposer => disposer?.());
@@ -234,7 +234,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
-        this.props.select(false); // hack to preven the webBox from being replaced with a thumbnail that currently doesn't show the the anchors
         if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth);
         if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
@@ -242,15 +241,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         if (doc !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1, this.getScrollHeight());
-            if (scrollTo !== undefined) {
+            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1,
+                Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
+            if (scrollTo !== undefined && this._initialScroll === undefined) {
                 const focusSpeed = smooth ? 500 : 0;
-                this._initialScroll !== undefined && (this._initialScroll = scrollTo);
                 this.goTo(scrollTo, focusSpeed);
                 return focusSpeed;
+            } else if (!this._webPageHasBeenRendered || !this.getScrollHeight() || this._initialScroll !== undefined) {
+                this._initialScroll = scrollTo;
             }
         }
-        this._initialScroll = NumCast(this.layoutDoc._scrollTop);
         return undefined;
     }
 
@@ -324,6 +324,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     iframeLoaded = (e: any) => {
         const iframe = this._iframe;
+        if (this._initialScroll !== undefined) {
+            this.setScrollPos(this._initialScroll);
+        }
         let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend("") + "/corsProxy/", "") ?? this._url.toString());
         if (requrlraw !== this._url.toString()) {
             if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) {
@@ -342,8 +345,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (iframe?.contentDocument) {
             iframe.contentDocument.addEventListener("pointerup", this.iframeUp);
             iframe.contentDocument.addEventListener("pointerdown", this.iframeDown);
-            this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight);
-            setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000);
+            this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument.body.scrollHeight);
+            setTimeout(action(() => this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000);
             iframe.setAttribute("enable-annotation", "true");
             iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => {
                 let href = "";
@@ -374,29 +377,29 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     setDashScrollTop = (scrollTop: number, timeout: number = 250) => {
-        const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight());
-        timeout = scrollTop > iframeHeight ? 0 : timeout;
+        const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight());
         this._scrollTimer && clearTimeout(this._scrollTimer);
         this._scrollTimer = setTimeout(action(() => {
             this._scrollTimer = undefined;
             if (!LinkDocPreview.LinkInfo && this._outerRef.current &&
                 (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
-                this.layoutDoc._scrollTop = this._outerRef.current.scrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
+                this.layoutDoc.thumb = undefined;
+                this.layoutDoc.thumbScrollTop = undefined;
+                this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
             }
         }), timeout);
     }
 
     goTo = (scrollTop: number, duration: number) => {
         if (this._outerRef.current) {
-            const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight());
-            scrollTop = scrollTop > iframeHeight + 50 ? iframeHeight : scrollTop;
+            const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight());
             if (duration) {
                 smoothScroll(duration, [this._outerRef.current], scrollTop);
                 this.setDashScrollTop(scrollTop, duration);
             } else {
                 this.setDashScrollTop(scrollTop);
             }
-        }
+        } else this._initialScroll = scrollTop;
     }
 
     forward = (checkAvailable?: boolean) => {
@@ -456,6 +459,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             if (url && !preview) {
                 this.dataDoc[this.fieldKey + "-history"] = new List<string>([...(history || []), url]);
                 this.layoutDoc._scrollTop = 0;
+                if (this._webPageHasBeenRendered) {
+                    this.layoutDoc.thumb = undefined;
+                    this.layoutDoc.thumbScrollTop = undefined;
+                }
                 future && (future.length = 0);
             }
             if (!preview) {
@@ -566,6 +573,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @computed get urlContent() {
         if (this._hackHide || (this.webThumb && (!this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc))) return (null);
+        this.props.thumbShown?.();
         const field = this.dataDoc[this.props.fieldKey];
         let view;
         if (field instanceof HtmlField) {
@@ -583,7 +591,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
                 ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
         }
-        setTimeout(action(() => this._webPageHasBeenRendered = true));
+        setTimeout(action(() => {
+            this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0);
+            if (this._initialScroll === undefined && !this._webPageHasBeenRendered) this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+            this._webPageHasBeenRendered = true;
+        }));
         return view;
     }
 
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 218f37f3d..fb2e33e2a 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -22,7 +22,11 @@ interface IAnnotationProps extends FieldViewProps {
 export
     class Annotation extends React.Component<IAnnotationProps> {
     render() {
-        return DocListCast(this.props.anno.textInlineAnnotations).map(a => <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />);
+        return <div>
+            {DocListCast(this.props.anno.textInlineAnnotations).map(a =>
+                <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
+            )}
+        </div>
     }
 }
 
@@ -32,23 +36,10 @@ interface IRegionAnnotationProps extends IAnnotationProps {
 }
 @observer
 class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
-    private _disposers: { [name: string]: IReactionDisposer } = {};
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
 
-    @observable _brushed: boolean = false;
     @computed get annoTextRegion() { return Cast(this.props.document.annoTextRegion, Doc, null) || this.props.document; }
 
-    componentDidMount() {
-        this._disposers.brush = reaction(
-            () => this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion),
-            brushed => brushed !== undefined && runInAction(() => this._brushed = brushed !== 0)
-        );
-    }
-
-    componentWillUnmount() {
-        Object.values(this._disposers).forEach(disposer => disposer?.());
-    }
-
     @undoBatch
     deleteAnnotation = () => {
         const docAnnotations = DocListCast(this.props.dataDoc[this.props.fieldKey]);
@@ -91,15 +82,27 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     }
 
     render() {
-        return (<div className="htmlAnnotation" onPointerEnter={() => this.props.showInfo(this.props.anno)} onPointerLeave={() => this.props.showInfo(undefined)} onPointerDown={this.onPointerDown} ref={this._mainCont}
+        const brushed = this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion);
+        return (<div className="htmlAnnotation" ref={this._mainCont}
+            onPointerEnter={action(() => {
+                Doc.BrushDoc(this.props.anno);
+                this.props.showInfo(this.props.anno);
+            })}
+            onPointerLeave={action(() => {
+                Doc.UnBrushDoc(this.props.anno);
+                this.props.showInfo(undefined);
+            })}
+            onPointerDown={this.onPointerDown}
             style={{
                 left: NumCast(this.props.document.x),
                 top: NumCast(this.props.document.y),
                 width: NumCast(this.props.document._width),
                 height: NumCast(this.props.document._height),
-                opacity: this._brushed ? 0.5 : undefined,
+                opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
                 pointerEvents: this.props.pointerEvents?.() as any,
-                backgroundColor: this._brushed ? "orange" : StrCast(this.props.document.backgroundColor),
+                outline: brushed === Doc.DocBrushStatus.linkHighlighted ? "solid 1px lightBlue" : undefined,
+                backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? "orange" :
+                    StrCast(this.props.document.backgroundColor),
             }} >
         </div>);
     }
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 608ba07ff..b0a5fc93b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -484,7 +484,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             CollectionView={undefined}
             ScreenToLocalTransform={this.overlayTransform}
             renderDepth={this.props.renderDepth + 1}
-        />;
+        />
     @computed get overlayTransparentAnnotations() { return this.renderAnnotations(this.transparentFilter, false); }
     @computed get overlayOpaqueAnnotations() { return this.renderAnnotations(this.opaqueFilter, false); }
     @computed get overlayClickableAnnotations() {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3d36efbc4..2905e96d9 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -127,9 +127,11 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     static foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) {
         let newarray: Doc[] = [];
         var depth = 0;
+        let visited: Doc[] = [];
         while (docs.length > 0) {
             newarray = [];
-            docs.filter(d => d).forEach(d => {
+            docs.filter(d => d && !visited.includes(d)).forEach(d => {
+                visited.push(d);
                 const fieldKey = Doc.LayoutFieldKey(d);
                 const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
                 const data = d[annos ? fieldKey + "-annotations" : fieldKey];
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 50e5fcbc4..9de8d0831 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1016,12 +1016,28 @@ export namespace Doc {
         unbrushed = 0,
         protoBrushed = 1,
         selfBrushed = 2,
-        highlighted = 3
+        highlighted = 3,
+        linkHighlighted = 4,
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return DocBrushStatus.unbrushed;
-        return brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
+        const status = brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
+        if (status === DocBrushStatus.unbrushed) {
+            const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
+            if (lastBrushed) {
+                for (var 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))) {
+                        return DocBrushStatus.linkHighlighted;
+                    }
+                }
+            }
+        }
+        return status;
     }
     export function IsBrushedDegree(doc: Doc) {
         return computedFn(function IsBrushDegree(doc: Doc) {
@@ -1030,14 +1046,18 @@ export namespace Doc {
     }
     export function BrushDoc(doc: Doc) {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
-        brushManager.BrushedDoc.set(doc, true);
-        brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
+        runInAction(() => {
+            brushManager.BrushedDoc.set(doc, true);
+            brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
+        });
         return doc;
     }
     export function UnBrushDoc(doc: Doc) {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
-        brushManager.BrushedDoc.delete(doc);
-        brushManager.BrushedDoc.delete(Doc.GetProto(doc));
+        runInAction(() => {
+            brushManager.BrushedDoc.delete(doc);
+            brushManager.BrushedDoc.delete(Doc.GetProto(doc));
+        });
         return doc;
     }
 
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 83a2bc79b..cd85a7a65 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -271,10 +271,10 @@ export default class UploadManager extends ApiManager {
                 return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => {
                     const ext = extname(savedName).toLowerCase();
                     const { pngs, jpgs } = AcceptableMedia;
-                    const resizers = !origSuffix ? [{ resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" }] : [
-                        { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" },
-                        { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" },
-                        { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: "_l" },
+                    const resizers = !origSuffix ? [{ resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium }] : [
+                        { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Small },
+                        { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium },
+                        { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Large },
                     ];
                     let isImage = false;
                     if (pngs.includes(ext)) {
@@ -290,8 +290,10 @@ export default class UploadManager extends ApiManager {
                     }
                     if (isImage) {
                         resizers.forEach(resizer => {
-                            const path = serverPathToFile(Directory.images, filename + resizer.suffix + ext);
-                            createReadStream(savedName).pipe(resizer.resizer).pipe(createWriteStream(path));
+                            const path = serverPathToFile(Directory.images, InjectSize(filename, resizer.suffix) + ext);
+                            createReadStream(savedName).on("error", e => console.log("Resizing Read error:" + e))
+                                .pipe(resizer.resizer)
+                                .pipe(createWriteStream(path).on("error", e => console.log("Resizing write error: " + e)));
                         });
 
                     }
-- 
cgit v1.2.3-70-g09d2


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/documents/Documents.ts                  |  3 +-
 src/client/views/DocComponent.tsx                  |  4 --
 src/client/views/GestureOverlay.tsx                |  1 -
 src/client/views/InkingStroke.tsx                  |  2 +-
 src/client/views/LightboxView.tsx                  |  1 -
 src/client/views/MainView.tsx                      |  6 ---
 src/client/views/OverlayView.tsx                   |  1 -
 src/client/views/Palette.tsx                       |  1 -
 src/client/views/PropertiesView.tsx                |  2 -
 src/client/views/StyleProvider.tsx                 | 52 +++-------------------
 src/client/views/TemplateMenu.tsx                  |  1 -
 src/client/views/collections/CollectionMenu.tsx    |  1 -
 .../collections/CollectionStackedTimeline.tsx      |  2 +-
 .../views/collections/CollectionStackingView.tsx   |  2 -
 .../views/collections/CollectionTreeView.tsx       |  1 -
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        | 19 +-------
 src/client/views/collections/TreeView.tsx          |  2 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 24 +++++-----
 .../collections/collectionFreeForm/MarqueeView.tsx | 10 ++---
 .../collectionLinear/CollectionLinearView.tsx      |  1 -
 .../CollectionMulticolumnView.tsx                  |  1 -
 .../CollectionMultirowView.tsx                     |  1 -
 .../collectionSchema/CollectionSchemaView.tsx      |  1 -
 .../collections/collectionSchema/SchemaTable.tsx   |  1 -
 src/client/views/linking/LinkPopup.tsx             |  3 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 -
 src/client/views/nodes/DocumentView.tsx            |  5 +--
 src/client/views/nodes/FilterBox.tsx               |  1 -
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/KeyValuePair.tsx            |  1 -
 src/client/views/nodes/LinkAnchorBox.tsx           |  1 -
 src/client/views/nodes/LinkDocPreview.tsx          |  1 -
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 src/client/views/nodes/WebBox.tsx                  |  6 +--
 .../views/nodes/formattedText/DashDocView.tsx      |  1 -
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  2 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  1 -
 src/client/views/pdf/Annotation.tsx                |  2 +-
 src/client/views/search/SearchBox.tsx              |  2 +-
 src/fields/Doc.ts                                  |  2 +-
 src/mobile/AudioUpload.tsx                         |  1 -
 src/mobile/MobileInterface.tsx                     |  1 -
 src/server/server_Initialization.ts                |  2 +-
 46 files changed, 41 insertions(+), 143 deletions(-)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d9c2e0d8b..797a129c4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -122,7 +122,6 @@ export class DocumentOptions {
     _scrollTop?: number; // scroll location for pdfs
     _noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged
     _chromeHidden?: boolean; // whether the editing chrome for a document is hidden
-    _layerTags?: List<string>; // layer tags a document has (used for tab filtering "layers" in document tab)
     _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
     _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active)
     _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
@@ -426,7 +425,7 @@ export namespace Docs {
                     childDontRegisterViews: true, _isLinkButton: true, _height: 150, description: "", showCaption: "description",
                     backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area
                     links: "@links(self)",
-                    _removeDropProperties: new List(["_layerTags", "isLinkButton"]),
+                    _removeDropProperties: new List(["isLinkButton"]),
                 }
             }],
             [DocumentType.LINKDB, {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 79aaf2158..9c176a4fd 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -44,7 +44,6 @@ interface ViewBoxBaseProps {
     ContainingCollectionDoc: Opt<Doc>;
     DocumentView?: () => DocumentView;
     fieldKey: string;
-    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
     isContentActive: () => boolean | undefined;
     renderDepth: number;
@@ -85,7 +84,6 @@ export interface ViewBoxAnnotatableProps {
     DataDoc?: Doc;
     fieldKey: string;
     filterAddDocument?: (doc: Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
-    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
     isContentActive: () => boolean | undefined;
     select: (isCtrlPressed: boolean) => void;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
@@ -209,13 +207,11 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
-                            this.props.layerProvider?.(doc, true);
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         });
                     }
                     else {
                         added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => {  // only make a pushpin if we have acl's to edit the document
-                            this.props.layerProvider?.(doc, true);
                             //DocUtils.LeavePushpin(doc);
                             doc._stayInCollection = undefined;
                             doc.context = this.props.Document;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 50dca0a99..482b62479 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -881,7 +881,6 @@ export class GestureOverlay extends Touchable {
                 isContentActive={returnFalse}
                 renderDepth={0}
                 styleProvider={returnEmptyString}
-                layerProvider={undefined}
                 docViewPath={returnEmptyDoclist}
                 focus={DocUtils.DefaultFocus}
                 whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 3d9c048e5..d0f01c363 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -332,7 +332,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             inkStrokeWidth, inkStrokeWidth + (highlightIndex && closed && fillColor && (new Color(fillColor)).alpha() < 1 ? 6 : 15),
             StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap),
             StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" || suppressFill ? "none" : fillColor, startMarker, endMarker,
-            markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents?.() ?? (this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted"), 0.0,
+            markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? "none" : "visiblepainted"), 0.0,
             false, downHdlr);
         const fsize = +(StrCast(this.props.Document.fontSize, "12px").replace("px", ""));
         // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size. 
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 59ed0dc92..9882693ee 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -246,7 +246,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         docFilters={this.docFilters}
                         removeDocument={undefined}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={returnTrue}
                         ScreenToLocalTransform={this.lightboxScreenToLocal}
                         PanelWidth={this.lightboxWidth}
                         PanelHeight={this.lightboxHeight}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index db51c54f8..2e1d10955 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -281,7 +281,6 @@ export class MainView extends React.Component {
             addDocTab={this.addDocTabFunc}
             pinToPres={emptyFunction}
             docViewPath={returnEmptyDoclist}
-            layerProvider={undefined}
             styleProvider={undefined}
             rootSelected={returnTrue}
             isContentActive={returnTrue}
@@ -361,7 +360,6 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        layerProvider={undefined}
                         styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
@@ -402,7 +400,6 @@ export class MainView extends React.Component {
                 docViewPath={returnEmptyDoclist}
                 focus={DocUtils.DefaultFocus}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 isContentActive={returnTrue}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
@@ -516,7 +513,6 @@ export class MainView extends React.Component {
                     dropAction={"alias"}
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     rootSelected={returnTrue}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
@@ -591,7 +587,6 @@ export class MainView extends React.Component {
                     ContainingCollectionDoc={undefined}
                     Document={DocumentLinksButton.invisibleWebDoc}
                     dropAction={"move"}
-                    layerProvider={undefined}
                     styleProvider={undefined}
                     isSelected={returnFalse}
                     select={returnFalse}
@@ -673,7 +668,6 @@ export class MainView extends React.Component {
                             rootSelected={returnFalse}
                             renderDepth={0}
                             setHeight={returnFalse}
-                            layerProvider={undefined}
                             styleProvider={undefined}
                             addDocTab={returnFalse}
                             pinToPres={returnFalse}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 0f51cf9b2..ebad2981d 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -195,7 +195,6 @@ export class OverlayView extends React.Component {
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 529697f71..954529bc9 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -54,7 +54,6 @@ export default class Palette extends React.Component<PaletteProps> {
                             focus={emptyFunction}
                             docViewPath={returnEmptyDoclist}
                             styleProvider={returnEmptyString}
-                            layerProvider={undefined}
                             whenChildContentsActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
                             docFilters={returnEmptyFilter}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 21c688421..bba2ac211 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -303,7 +303,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     fitContentsToDoc={returnTrue}
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     freezeDimensions={true}
                     dontCenter={"y"}
@@ -1010,7 +1009,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             createNewFilterDoc={this.createNewFilterDoc}
                             updateFilterDoc={this.updateFilterDoc}
                             docViewPath={returnEmptyDoclist}
-                            layerProvider={undefined}
                             dontCenter="y"
                         />
                     </div>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 5056dedaf..334415b38 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -26,10 +26,6 @@ import React = require("react");
 import { InkingStroke } from './InkingStroke';
 import { TreeSort } from './collections/TreeView';
 
-export enum StyleLayers {
-    Background = "background"
-}
-
 export enum StyleProp {
     TreeViewIcon = "treeViewIcon",
     TreeViewSortings = "treeViewSortings",// options for how to sort tree view items
@@ -56,14 +52,9 @@ export enum StyleProp {
 
 function darkScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark; }
 
-function toggleBackground(doc: Doc) {
+function toggleLockedPosition(doc: Doc) {
     UndoManager.RunInBatch(() => runInAction(() => {
-        const layers = StrListCast(doc._layerTags);
-        if (!layers.includes(StyleLayers.Background)) {
-            if (!layers.length) doc._layerTags = new List<string>([StyleLayers.Background]);
-            else layers.push(StyleLayers.Background);
-        }
-        else layers.splice(layers.indexOf(StyleLayers.Background), 1);
+        doc._lockedPosition = !doc._lockedPosition;
     }), "toggleBackground");
 }
 
@@ -86,7 +77,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const isOpen = property.includes(":open");
     const fieldKey = props?.fieldKey ? props.fieldKey + "-" : isCaption ? "caption-" : "";
     const comicStyle = () => doc && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === "comic";
-    const isBackground = () => StrListCast(doc?._layerTags).includes(StyleLayers.Background);
+    const isBackground = () => doc && BoolCast(doc._lockedPosition);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
     const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
     const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
@@ -177,7 +168,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
             }
-            if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = DashColor(docColor).fade(0.5).toString();
+            if (docColor && !doc) docColor = DashColor(docColor).fade(0.5).toString();
             return docColor;
         }
         case StyleProp.BoxShadow: {
@@ -205,17 +196,15 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.PointerEvents:
             if (doc?.type === DocumentType.MARKER) return "none";
             if (props?.pointerEvents?.() === "none") return "none";
-            const layer = doc && props?.layerProvider?.(doc);
             const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name);
-            if (opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
-            if (layer === false && !selected && !SnappingManager.GetIsDragging()) return "none";
-            if (!isInk && layer === true) return "all";
+            if (doc?._lockedPosition || opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
+            if (!isInk) return "all";
             return undefined;
         case StyleProp.Decorations:
             if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) {
                 return doc && (isBackground() || selected) && (props?.renderDepth || 0) > 0 &&
                     ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
-                    <div className="styleProvider-lock" onClick={() => toggleBackground(doc)}>
+                    <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                         <FontAwesomeIcon icon={isBackground() ? "lock" : "unlock"} style={{ color: isBackground() ? "red" : undefined }} size="lg" />
                     </div>
                     : (null);
@@ -251,30 +240,3 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps
     }
     return DefaultStyleProvider(doc, props, property);
 }
-
-//
-// a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents
-//  currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab.
-//     if it matches, then the document gets pointer events, otherwise it does not.
-//
-export function DefaultLayerProvider(thisDoc: Doc) {
-    return (doc: Doc, assign?: boolean) => {
-        if (doc.z) return true;
-        if (assign) {
-            const activeLayer = StrCast(thisDoc?.activeLayer);
-            if (activeLayer) {
-                const layers = Cast(doc._layerTags, listSpec("string"), []);
-                if (layers.length && !layers.includes(activeLayer)) layers.push(activeLayer);
-                else if (!layers.length) doc._layerTags = new List<string>([activeLayer]);
-                if (activeLayer === "red" || activeLayer === "green" || activeLayer === "blue") doc._backgroundColor = activeLayer;
-            }
-            return true;
-        } else {
-            if (Doc.AreProtosEqual(doc, thisDoc)) return true;
-            const layers = StrListCast(doc._layerTags);
-            if (!layers.length && !thisDoc?.activeLayer) return true;
-            if (layers.includes(StrCast(thisDoc?.activeLayer))) return true;
-            return false;
-        }
-    };
-}
\ No newline at end of file
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index b3a24e031..636f7042f 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -131,7 +131,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 setHeight={returnFalse}
                 docViewPath={returnEmptyDoclist}
                 docFilters={returnEmptyFilter}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 364a2440e..b3fbd4ca4 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -108,7 +108,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
                 dropAction={"alias"}
                 setHeight={returnFalse}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 rootSelected={returnTrue}
                 bringToFront={emptyFunction}
                 select={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index de307416f..683b6d51d 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -392,7 +392,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     // handles dragging and dropping markers in timeline
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) {
-        if (!de.embedKey && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false;
+        if (!de.embedKey && this.props.Document._isGroup) return false;
         if (!super.onInternalDrop(e, de)) return false;
 
         // determine x coordinate of drop and assign it to the documents being dragged  --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 509005b45..4338dedcc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -222,7 +222,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             PanelWidth={width}
             PanelHeight={height}
             styleProvider={this.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             fitWidth={this.props.childFitWidth}
             isContentActive={emptyFunction}
@@ -543,7 +542,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     renderDepth={this.props.renderDepth}
                     focus={emptyFunction}
                     styleProvider={this.props.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 41970eb96..5e89016db 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -296,7 +296,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     renderDepth={this.props.renderDepth + 1}
                     focus={emptyFunction}
                     styleProvider={this.props.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ee2c28b5f..7c5f49b42 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -261,7 +261,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             CollectionView: this,
         };
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: this.props.layerProvider?.(this.rootDoc) === false ? "none" : undefined }}>
+            style={{ pointerEvents: this.rootDoc._lockedPosition ? "none" : undefined }}>
             {this.showIsTagged()}
             {this.renderSubView(this.collectionViewType, props)}
         </div>);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 8dfc7edc7..73e5a9178 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -26,7 +26,7 @@ import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
-import { DefaultLayerProvider, DefaultStyleProvider, StyleLayers, StyleProp } from '../StyleProvider';
+import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@@ -94,20 +94,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
 
             if (tab.element[0].children[1].children.length === 1) {
-                const toggle = document.createElement("div");
-                toggle.style.width = "10px";
-                toggle.style.height = "calc(100% - 2px)";
-                toggle.style.left = "-2px";
-                toggle.style.bottom = "1px";
-                toggle.style.borderTopRightRadius = "7px";
-                toggle.style.position = "relative";
-                toggle.style.display = "inline-block";
-                toggle.style.background = "transparent";
-                toggle.onclick = (e: MouseEvent) => {
-                    if (tab.contentItem === tab.header.parent.getActiveContentItem()) {
-                        tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : StyleLayers.Background;
-                    }
-                };
                 iconWrap.className = "lm_iconWrap";
                 iconWrap.id = "lm_iconWrap";
                 closeWrap.className = "lm_iconWrap";
@@ -357,7 +343,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform);
     hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
 
-    @computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); }
     @computed get docView() {
         return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
             <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)}
@@ -369,7 +354,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 isContentActive={returnTrue}
                 PanelWidth={this.PanelWidth}
                 PanelHeight={this.PanelHeight}
-                layerProvider={this.layerProvider}
                 styleProvider={DefaultStyleProvider}
                 docFilters={CollectionDockingView.Instance.childDocFilters}
                 docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
@@ -520,7 +504,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={TabMinimapView.miniStyleProvider}
-                    layerProvider={undefined}
                     addDocTab={this.props.addDocTab}
                     pinToPres={TabDocView.PinDoc}
                     docFilters={CollectionDockingView.Instance.childDocFilters}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 164021358..d608f800c 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -663,7 +663,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideDecorationTitle={this.props.treeView.outlineMode}
                 hideResizeHandles={this.props.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
-                layerProvider={returnTrue}
                 docViewPath={returnEmptyDoclist}
                 treeViewDoc={this.props.treeView.props.Document}
                 addDocument={undefined}
@@ -760,7 +759,6 @@ export class TreeView extends React.Component<TreeViewProps> {
             renderDepth={this.props.renderDepth + 1}
             treeViewDoc={this.props.treeView?.props.Document}
             rootSelected={returnTrue}
-            layerProvider={returnTrue}
             docViewPath={this.props.treeView.props.docViewPath}
             docFilters={returnEmptyFilter}
             docRangeFilters={returnEmptyFilter}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4f79d19ad..322944d92 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -44,7 +44,7 @@ import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { VideoBox } from "../../nodes/VideoBox";
 import { CreateImage } from "../../nodes/WebBoxRenderer";
-import { StyleLayers, StyleProp } from "../../StyleProvider";
+import { StyleProp } from "../../StyleProvider";
 import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
 import { CollectionViewType } from "../CollectionView";
@@ -122,8 +122,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable ChildDrag: DocumentView | undefined; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
 
     @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
-    @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); }
-    @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); }
     @computed get fitToContentVals() {
         return {
             bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
@@ -222,8 +220,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
-        if (!de.embedKey && !this.ChildDrag && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false;
-        if (!super.onInternalDrop(e, de)) return false;
+        if (!de.embedKey && !this.ChildDrag && this.rootDoc._isGroup) return false;
+        if (this.rootDoc._lockedPosition || !super.onInternalDrop(e, de)) return false;
         const refDoc = docDragData.droppedDocuments[0];
         const [xpo, ypo] = this.getContainerTransform().transformPoint(de.x, de.y);
         const z = NumCast(refDoc.z);
@@ -251,7 +249,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            !StrListCast(d._layerTags).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -418,8 +416,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 styleProp = colors[cluster % colors.length];
                 const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
                 // override the cluster color with an explicitly set color on a non-background document.  then override that with an explicitly set color on a background document
-                set?.filter(s => !StrListCast(s._layerTags).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
-                set?.filter(s => StrListCast(s._layerTags).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
+                set?.map(s => styleProp = StrCast(s.backgroundColor));
             }
         } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
         return styleProp;
@@ -1017,7 +1014,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     bringToFront = (doc: Doc, sendToBack?: boolean) => {
-        if (sendToBack || StrListCast(doc._layerTags).includes(StyleLayers.Background)) {
+        if (sendToBack) {
             doc.zIndex = 0;
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
@@ -1155,7 +1152,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     pointerEvents = () => {
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
-        const pointerEvents = this.props.isContentActive() === false ? "none" : this.backgroundActive || this.props.childPointerEvents ? "all" :
+        const pointerEvents = this.props.isContentActive() === false ? "none" : this.props.childPointerEvents ? "all" :
             (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents?.();
         return pointerEvents;
     }
@@ -1195,7 +1192,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
             docViewPath={this.props.docViewPath}
             styleProvider={this.getClusterColor}
-            layerProvider={this.props.layerProvider}
             dataProvider={this.childDataProvider}
             sizeProvider={this.childSizeProvider}
             freezeDimensions={BoolCast(this.props.Document.childFreezeDimensions, this.props.childFreezeDimensions)}
@@ -1614,7 +1610,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => intersectRect(docDims(doc), rect);
 
         const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
-        let snappableDocs = activeDocs.filter(doc => !StrListCast(doc._layerTags).includes(StyleLayers.Background) && doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
+        let snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs
 
@@ -1739,7 +1735,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             onContextMenu={this.onContextMenu}
             style={{
                 pointerEvents: this.props.Document.type === DocumentType.MARKER ? "none" :  // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
-                    this.backgroundEvents ? "all" : this.props.pointerEvents?.() as any,
+                    this.props.pointerEvents?.() as any,
                 transform: `scale(${this.contentScaling || 1})`,
                 width: `${100 / (this.contentScaling || 1)}%`,
                 height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
@@ -1767,7 +1763,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     </svg>
                 </div>}
 
-            {this.props.Document._isGroup && SnappingManager.GetIsDragging() && (this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) ?
+            {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ?
                 <div className="collectionFreeForm-groupDropper" ref={this.createGroupEventsTarget} style={{
                     width: this.ChildDrag ? "10000" : "100%",
                     height: this.ChildDrag ? "10000" : "100%",
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5f303b428..1c4d47d57 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -28,7 +28,6 @@ import { CollectionView } from "../CollectionView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
-import { StyleLayers } from "../../StyleProvider";
 import { TreeView } from "../TreeView";
 import { VideoBox } from "../../nodes/VideoBox";
 import { ImageField, WebField } from "../../../../fields/URLField";
@@ -348,7 +347,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
-    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[], makeGroup: Opt<boolean>) => {
+    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
         const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
             Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
@@ -357,7 +356,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             return doc;
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
         newCollection.system = undefined;
-        newCollection._layerTags = new List<string>(layers);
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
         newCollection._isGroup = makeGroup;
@@ -437,7 +435,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             }));
             this.props.removeDocument?.(selected);
         }
-        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, [], group);
+        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -531,7 +529,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const newCollection = this.getCollection([], undefined, [StyleLayers.Background], undefined);
+        const newCollection = this.getCollection([], undefined, undefined);
         this.props.addDocument?.(newCollection);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
@@ -617,7 +615,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 (this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
             }
         };
-        this.props.activeDocuments().filter(doc => this.props.layerProvider?.(doc) !== false && !doc.z).map(selectFunc);
+        this.props.activeDocuments().filter(doc => !doc.z && !doc._lockedPosition).map(selectFunc);
         if (!selection.length && selectBackgrounds) this.props.activeDocuments().filter(doc => doc.z === undefined).map(selectFunc);
         if (!selection.length) this.props.activeDocuments().filter(doc => doc.z !== undefined).map(selectFunc);
         return selection;
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c615bfb8e..bec582dcd 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -155,7 +155,6 @@ export class CollectionLinearView extends CollectionSubView() {
                 renderDepth={this.props.renderDepth + 1}
                 focus={emptyFunction}
                 styleProvider={this.props.styleProvider}
-                layerProvider={this.props.layerProvider}
                 docViewPath={returnEmptyDoclist}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 6929a1cd8..92f9b2f49 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -219,7 +219,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             styleProvider={this.props.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 7e2b83230..4c4054b09 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -218,7 +218,6 @@ export class CollectionMultirowView extends CollectionSubView() {
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             styleProvider={this.props.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index a93762ea4..b731479a5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -417,7 +417,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     docRangeFilters={this.childDocRangeFilters}
                     searchFilterDocs={this.searchFilterDocs}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                     ContainingCollectionView={this.props.CollectionView}
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}
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index c8be9069c..4b33ef8ae 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -67,7 +67,7 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                     <input defaultValue={""} autoComplete="off" type="text" placeholder="Search for Document..." id="search-input"
                         className="linkPopup-searchBox searchBox-input" /> */}
 
-                    <SearchBox 
+                    <SearchBox
                         Document={CurrentUserUtils.MySearchPanelDoc}
                         DataDoc={CurrentUserUtils.MySearchPanelDoc}
                         linkFrom={linkDoc}
@@ -83,7 +83,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         pinToPres={emptyFunction}
                         rootSelected={returnTrue}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={undefined}
                         removeDocument={undefined}
                         ScreenToLocalTransform={Transform.Identity}
                         PanelWidth={this.getPWidth}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index e28e5b453..d97cb6f84 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -654,7 +654,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             className="audiobox-container"
             onContextMenu={this.specificContextMenu}
             onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined}
-            style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined }}
+            style={{ pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined }}
         >
             {!this.path ? this.recordingControls : this.playbackControls}
         </div>;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index edadd59c0..5a0ab9110 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -21,7 +21,6 @@ import React = require("react");
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
     sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined;
-    layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined;
     renderCutoffProvider: (doc: Doc) => boolean;
     zIndex?: number;
     highlight?: boolean;
@@ -159,7 +158,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             ...this.props,
             CollectionFreeFormDocumentView: this.returnThis,
             styleProvider: this.styleProvider,
-            layerProvider: this.props.layerProvider,
             ScreenToLocalTransform: this.screenToLocalTransform,
             PanelWidth: this.panelWidth,
             PanelHeight: this.panelHeight,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f30d093a1..c999b3224 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -37,7 +37,7 @@ import { DocComponent } from "../DocComponent";
 import { EditableView } from '../EditableView';
 import { InkingStroke } from "../InkingStroke";
 import { LightboxView } from "../LightboxView";
-import { StyleLayers, StyleProp } from "../StyleProvider";
+import { StyleProp } from "../StyleProvider";
 import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { DocumentLinksButton } from './DocumentLinksButton';
@@ -123,7 +123,6 @@ export interface DocumentViewSharedProps {
     PanelHeight: () => number;
     docViewPath: () => DocumentView[];
     dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
-    layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean);
     styleProvider: Opt<StyleProviderFunc>;
     focus: DocFocusFunc;
     fitWidth?: (doc: Doc) => boolean;
@@ -474,7 +473,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             let stopPropagate = true;
             let preventDefault = true;
-            !StrListCast(this.props.Document._layerTags).includes(StyleLayers.Background) && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+            (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
             if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
                 if (this._timeout) {
                     clearTimeout(this._timeout);
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 78adf63cb..a47e17dfc 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -423,7 +423,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     treeViewHideHeaderFields={false}
                     dontRegisterView={true}
                     styleProvider={this.FilterStyleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={this.props.docViewPath}
                     scriptContext={this.props.scriptContext}
                     moveDocument={returnFalse}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index e7c43e5cd..3b04aa807 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -379,7 +379,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             style={{
                 width: this.props.PanelWidth() ? undefined : `100%`,
                 height: this.props.PanelWidth() ? undefined : `100%`,
-                pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
+                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
                 borderRadius
             }} >
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 881cbf2bb..80def3025 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -60,7 +60,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             docRangeFilters: returnEmptyFilter,
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
-            layerProvider: undefined,
             docViewPath: returnEmptyDoclist,
             ContainingCollectionView: undefined,
             ContainingCollectionDoc: undefined,
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 437d29f39..7fd289a97 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -89,7 +89,6 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     openLinkTargetOnRight = (e: React.MouseEvent) => {
         const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
         alias._isLinkButton = undefined;
-        alias._layerTags = undefined;
         alias.layoutKey = "layout";
         this.props.addDocTab(alias, "add:right");
     }
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 375434933..ba515fb89 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -179,7 +179,6 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             moveDocument={returnFalse}
                             rootSelected={returnFalse}
                             styleProvider={this.props.docProps?.styleProvider}
-                            layerProvider={this.props.docProps?.layerProvider}
                             docViewPath={returnEmptyDoclist}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 3b4c94562..c350e3139 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -809,7 +809,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
         return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
             style={{
-                pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
+                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
                 borderRadius,
                 overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
             }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 20e01be05..445df8ddd 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -74,7 +74,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
     @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
-    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url }
+    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url; }
 
     constructor(props: any) {
         super(props);
@@ -134,7 +134,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         returnedfilename => setTimeout(action(() => {
                             this.lockout = false;
                             this.layoutDoc.thumb = new ImageField(returnedfilename);
-                            this.layoutDoc.thumbScrollTop = scrollTop
+                            this.layoutDoc.thumbScrollTop = scrollTop;
                         }), 500));
                 })
                 .catch(function (error: any) {
@@ -717,7 +717,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
     annotationPointerEvents = () => this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none";
     render() {
-        const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents?.() as any;
+        const pointerEvents = this.layoutDoc._lockedPosition ? "none" : this.props.pointerEvents?.() as any;
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
         const renderAnnotations = (docFilters?: () => string[]) =>
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 364be461f..1d8e3a2cf 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -182,7 +182,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     removeDocument={this.removeDoc}
                     isDocumentActive={returnFalse}
                     isContentActive={this._textBox.props.isContentActive}
-                    layerProvider={this._textBox.props.layerProvider}
                     styleProvider={this._textBox.props.styleProvider}
                     docViewPath={this._textBox.props.docViewPath}
                     ScreenToLocalTransform={this.getDocTransform}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 29117794e..f2a222cee 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1630,7 +1630,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         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 interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
+        const interactive = (CurrentUserUtils.SelectedTool === 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);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 820d7ef96..64f5a296f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -700,7 +700,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && this.props.layerProvider?.(this.layoutDoc) !== false) &&
+    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
         (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     /**
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f4dc9b615..ef918d991 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -80,7 +80,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     Document={this.targetDoc}
                     DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     styleProvider={this.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     rootSelected={returnTrue}
                     addDocument={returnFalse}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index fb2e33e2a..5bdce273d 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -26,7 +26,7 @@ export
             {DocListCast(this.props.anno.textInlineAnnotations).map(a =>
                 <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
             )}
-        </div>
+        </div>;
     }
 }
 
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 2905e96d9..152b7bbcb 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -127,7 +127,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     static foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) {
         let newarray: Doc[] = [];
         var depth = 0;
-        let visited: Doc[] = [];
+        const visited: Doc[] = [];
         while (docs.length > 0) {
             newarray = [];
             docs.filter(d => d && !visited.includes(d)).forEach(d => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 9de8d0831..194b3ba27 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1026,7 +1026,7 @@ export namespace Doc {
         if (status === DocBrushStatus.unbrushed) {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
             if (lastBrushed) {
-                for (var link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
+                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) ||
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 88221732e..418464f0e 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -96,7 +96,6 @@ export class AudioUpload extends React.Component {
                         isDocumentActive={returnTrue}
                         isContentActive={emptyFunction}
                         focus={emptyFunction}
-                        layerProvider={undefined}
                         styleProvider={() => "rgba(0,0,0,0)"}
                         docViewPath={returnEmptyDoclist}
                         whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index cfcc48608..78ec706d7 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -214,7 +214,6 @@ export class MobileInterface extends React.Component {
                     isContentActive={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={this.whitebackground}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index bdea57cb2..fd000a83c 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -195,7 +195,7 @@ function proxyServe(req: any, requrl: string, response: any) {
                 console.log("EROR?: ", e);
             }
         } else req.pipe(request(requrl)).pipe(response);
-    }
+    };
     retrieveHTTPBody = () => {
         req.headers.cookie = "";
         req.pipe(request(requrl))
-- 
cgit v1.2.3-70-g09d2


From a2ea001c425fb425e4d0cce7c59e77736da86d3d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 4 Jun 2022 20:45:29 -0400
Subject: fixed searching to highlight in text notes and to unhighlight when
 search string changes. fixed links to update when text boxes scroll (hackily)

---
 .../CollectionFreeFormLinkView.tsx                 | 15 ++++++--
 .../views/nodes/formattedText/FormattedTextBox.tsx | 43 ++++++++++++++--------
 src/client/views/search/SearchBox.tsx              |  3 ++
 3 files changed, 43 insertions(+), 18 deletions(-)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index f5a5492e3..fe849ee48 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import { Doc, Field } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { List } from "../../../../fields/List";
-import { NumCast } from "../../../../fields/Types";
+import { Cast, NumCast } from "../../../../fields/Types";
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { LinkManager } from "../../../util/LinkManager";
 import { SelectionManager } from "../../../util/SelectionManager";
@@ -30,7 +30,14 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     componentWillUnmount() { this._anchorDisposer?.(); }
     @action timeout = action(() => (Date.now() < this._start++ + 1000) && (this._timeout = setTimeout(this.timeout, 25)));
     componentDidMount() {
-        this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform()],
+        this._anchorDisposer = reaction(() => [
+            this.props.A.props.ScreenToLocalTransform(),
+            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
+            this.props.B.props.ScreenToLocalTransform(),
+            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
+        ],
             action(() => {
                 this._start = Date.now();
                 this._timeout && clearTimeout(this._timeout);
@@ -75,6 +82,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             const mpy = mp[1] / A.props.PanelHeight();
             if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
             if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
+            if (getComputedStyle(targetAhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+            else linkDoc.opacity = 1;
         }
         if (!targetBhyperlink) {
             if (linkDoc.linkAutoMove) {
@@ -253,7 +262,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             this.props.LinkDocs[0].displayArrow = false;
         }
 
-        return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
+        return !this.props.LinkDocs[0].opacity || !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
             <defs>
                 <marker id="arrowhead" markerWidth="4" markerHeight="3"
                     refX="0" refY="1.5" orient="auto">
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 434fccf8d..6004ac271 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -83,7 +83,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
     public static Instance: FormattedTextBox;
     public static LiveTextUndo: UndoManager.Batch | undefined;
-    static _highlights: 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();
@@ -412,6 +412,12 @@ 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);
@@ -548,35 +554,40 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     updateHighlights = () => {
+        const highlights = FormattedTextBox._globalHighlights;
         clearStyleSheetRules(FormattedTextBox._userStyleSheet);
-        if (FormattedTextBox._highlights.indexOf("Audio Tags") === -1) {
+        if (highlights.indexOf("Audio Tags") === -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "audiotag", { display: "none" }, "");
         }
-        if (FormattedTextBox._highlights.indexOf("Text from Others") !== -1) {
+        if (highlights.indexOf("Text from Others") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-remote", { background: "yellow" });
         }
-        if (FormattedTextBox._highlights.indexOf("My Text") !== -1) {
+        if (highlights.indexOf("My Text") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { background: "moccasin" });
         }
-        if (FormattedTextBox._highlights.indexOf("Todo Items") !== -1) {
+        if (highlights.indexOf("Todo Items") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "todo", { outline: "black solid 1px" });
         }
-        if (FormattedTextBox._highlights.indexOf("Important Items") !== -1) {
+        if (highlights.indexOf("Important Items") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "important", { "font-size": "larger" });
         }
-        if (FormattedTextBox._highlights.indexOf("Disagree Items") !== -1) {
+        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 (FormattedTextBox._highlights.indexOf("Ignore Items") !== -1) {
+        if (highlights.indexOf("Ignore Items") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "ignore", { "font-size": "1" });
         }
-        if (FormattedTextBox._highlights.indexOf("By Recent Minute") !== -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() }));
             setTimeout(this.updateHighlights);
         }
-        if (FormattedTextBox._highlights.indexOf("By Recent Hour") !== -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() }));
@@ -634,17 +645,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             });
         });
         const highlighting: ContextMenuProps[] = [];
-        const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others"];
+        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.UserDoc().noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
             highlighting.push({
-                description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
+                description: (FormattedTextBox._globalHighlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
                     e.stopPropagation();
-                    if (FormattedTextBox._highlights.indexOf(option) === -1) {
-                        FormattedTextBox._highlights.push(option);
+                    if (FormattedTextBox._globalHighlights.indexOf(option) === -1) {
+                        FormattedTextBox._globalHighlights.push(option);
                     } else {
-                        FormattedTextBox._highlights.splice(FormattedTextBox._highlights.indexOf(option), 1);
+                        FormattedTextBox._globalHighlights.splice(FormattedTextBox._globalHighlights.indexOf(option), 1);
                     }
+                    runInAction(() => this.layoutDoc._highlights = FormattedTextBox._globalHighlights.join(""));
                     this.updateHighlights();
                 }, icon: "expand-arrows-alt"
             }));
@@ -923,6 +935,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
         this._disposers.selected = reaction(() => this.props.isSelected(),
             action(selected => {
+                this.layoutDoc._highlights = selected ? FormattedTextBox._globalHighlights.join("") : "";
                 if (RichTextMenu.Instance?.view === this._editorView && !selected) {
                     RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
                 }
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 152b7bbcb..48c045b57 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -341,6 +341,9 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         const query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
+        Array.from(this._results.keys()).forEach(doc =>
+            DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true)
+        );
         this._results.clear();
 
         if (query) {
-- 
cgit v1.2.3-70-g09d2


From dd16695b0c5fe8c54bc276a230381ae36e19e5ac Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 7 Jul 2022 13:02:33 -0400
Subject: trying to fix errors in compiles

---
 package-lock.json                                  | 2282 +++++++++++++++++--
 package.json                                       |   40 +-
 src/client/util/CaptureManager.tsx                 |  157 +-
 src/client/util/CurrentUserUtils.ts                |    2 +-
 src/client/util/GroupManager.tsx                   |  253 ++-
 src/client/util/ScrollBox.tsx                      |   23 +-
 src/client/util/SharingManager.tsx                 |  566 +++--
 src/client/views/Main.tsx                          |   44 +-
 src/client/views/MainView.tsx                      | 1086 ++++++----
 src/client/views/Touchable.tsx                     |   68 +-
 src/client/views/animationtimeline/Timeline.tsx    |  286 ++-
 .../views/collections/CollectionStackingView.tsx   |  657 +++---
 .../views/collections/CollectionTreeView.tsx       |  321 +--
 src/client/views/collections/CollectionView.tsx    |  266 ++-
 .../CollectionFreeFormLinksView.tsx                |   34 +-
 .../views/collections/collectionGrid/Grid.tsx      |   17 +-
 .../collectionLinear/CollectionLinearView.tsx      |  291 +--
 src/client/views/linking/LinkEditor.tsx            |  346 +--
 src/client/views/linking/LinkMenuItem.tsx          |  226 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  259 ++-
 src/client/views/nodes/LinkDocPreview.tsx          |  192 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  505 ++---
 src/client/views/nodes/PDFBox.tsx                  |  451 ++--
 src/client/views/nodes/formattedText/schema_rts.ts |   13 +-
 src/client/views/nodes/trails/PresBox.tsx          | 2285 +++++++++++++-------
 src/client/views/search/SearchBox.tsx              |  213 +-
 src/mobile/MobileInterface.tsx                     |  602 ++++--
 src/server/authentication/AuthenticationManager.ts |  306 +--
 28 files changed, 7658 insertions(+), 4133 deletions(-)

(limited to 'src/client/views/search/SearchBox.tsx')

diff --git a/package-lock.json b/package-lock.json
index e895161b5..7e413d5c5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -462,6 +462,76 @@
             "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
             "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
         },
+        "@eslint/eslintrc": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
+            "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
+            "dev": true,
+            "requires": {
+                "ajv": "^6.12.4",
+                "debug": "^4.3.2",
+                "espree": "^9.3.2",
+                "globals": "^13.15.0",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.2.1",
+                "js-yaml": "^4.1.0",
+                "minimatch": "^3.1.2",
+                "strip-json-comments": "^3.1.1"
+            },
+            "dependencies": {
+                "argparse": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+                    "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+                    "dev": true
+                },
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "globals": {
+                    "version": "13.16.0",
+                    "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
+                    "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
+                    "dev": true,
+                    "requires": {
+                        "type-fest": "^0.20.2"
+                    }
+                },
+                "ignore": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+                    "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+                    "dev": true
+                },
+                "js-yaml": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+                    "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+                    "dev": true,
+                    "requires": {
+                        "argparse": "^2.0.1"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "strip-json-comments": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+                    "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+                    "dev": true
+                }
+            }
+        },
         "@ffmpeg/core": {
             "version": "0.10.0",
             "resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.10.0.tgz",
@@ -547,9 +617,9 @@
             }
         },
         "@fortawesome/react-fontawesome": {
-            "version": "0.1.18",
-            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz",
-            "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==",
+            "version": "0.1.19",
+            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz",
+            "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==",
             "requires": {
                 "prop-types": "^15.8.1"
             }
@@ -580,6 +650,19 @@
                 "emotion": "^10.0.0",
                 "prop-types": "^15.7.1",
                 "react-transition-group": "^2.3.1"
+            },
+            "dependencies": {
+                "react-transition-group": {
+                    "version": "2.9.0",
+                    "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
+                    "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+                    "requires": {
+                        "dom-helpers": "^3.4.0",
+                        "loose-envify": "^1.4.0",
+                        "prop-types": "^15.6.2",
+                        "react-lifecycles-compat": "^3.0.4"
+                    }
+                }
             }
         },
         "@hig/theme-context": {
@@ -608,6 +691,40 @@
                 "lodash.memoize": "^4.1.2"
             }
         },
+        "@humanwhocodes/config-array": {
+            "version": "0.9.5",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
+            "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
+            "dev": true,
+            "requires": {
+                "@humanwhocodes/object-schema": "^1.2.1",
+                "debug": "^4.1.1",
+                "minimatch": "^3.0.4"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                }
+            }
+        },
+        "@humanwhocodes/object-schema": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+            "dev": true
+        },
         "@hypnosphi/create-react-context": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
@@ -1184,9 +1301,9 @@
             }
         },
         "@types/express-session": {
-            "version": "1.17.4",
-            "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz",
-            "integrity": "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==",
+            "version": "1.17.5",
+            "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz",
+            "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
@@ -1332,6 +1449,12 @@
             "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
             "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
         },
+        "@types/json5": {
+            "version": "0.0.29",
+            "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+            "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+            "dev": true
+        },
         "@types/keygrip": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
@@ -1461,9 +1584,9 @@
             "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
         },
         "@types/passport": {
-            "version": "1.0.8",
-            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.8.tgz",
-            "integrity": "sha512-Gdcvis7+7G/Mobm+25AeFi+oe5teBhHzpbCOFWeN10Bj8tnoEE1L5lkraQjzmDEKkJQuM7xSJUGIFGl/giyRfQ==",
+            "version": "1.0.9",
+            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.9.tgz",
+            "integrity": "sha512-9+ilzUhmZQR4JP49GdC2O4UdDE3POPLwpmaTC/iLkW7l0TZCXOo1zsTnnlXPq6rP1UsUZPfbAV4IUdiwiXyC7g==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
@@ -1663,10 +1786,9 @@
             }
         },
         "@types/react": {
-            "version": "16.14.26",
-            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.26.tgz",
-            "integrity": "sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==",
-            "dev": true,
+            "version": "18.0.15",
+            "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz",
+            "integrity": "sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==",
             "requires": {
                 "@types/prop-types": "*",
                 "@types/scheduler": "*",
@@ -1676,8 +1798,7 @@
                 "csstype": {
                     "version": "3.1.0",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
-                    "dev": true
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 }
             }
         },
@@ -1720,18 +1841,18 @@
             }
         },
         "@types/react-dom": {
-            "version": "16.9.16",
-            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.16.tgz",
-            "integrity": "sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg==",
+            "version": "18.0.6",
+            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz",
+            "integrity": "sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==",
             "dev": true,
             "requires": {
-                "@types/react": "^16"
+                "@types/react": "*"
             }
         },
         "@types/react-grid-layout": {
-            "version": "0.17.2",
-            "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-0.17.2.tgz",
-            "integrity": "sha512-iLKQ5rI9KyPGs/BlWmeBfoikRnoOmTuddK8V5mOCgJ46e2JaecZuPhuIX1kV0mkQSk6X3XoEZALVW0XQbz9g5Q==",
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-1.3.2.tgz",
+            "integrity": "sha512-ZzpBEOC1JTQ7MGe1h1cPKSLP4jSWuxc+yvT4TsAlEW9+EFPzAf8nxQfFd7ea9gL17Em7PbwJZAsiwfQQBUklZQ==",
             "dev": true,
             "requires": {
                 "@types/react": "*"
@@ -1741,6 +1862,7 @@
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/@types/react-icons/-/react-icons-3.0.0.tgz",
             "integrity": "sha512-Vefs6LkLqF61vfV7AiAqls+vpR94q67gunhMueDznG+msAkrYgRxl7gYjNem/kZ+as2l2mNChmF1jRZzzQQtMg==",
+            "dev": true,
             "requires": {
                 "react-icons": "*"
             }
@@ -1758,6 +1880,7 @@
             "version": "0.26.7",
             "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
             "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
+            "dev": true,
             "requires": {
                 "@types/react": "*"
             },
@@ -1766,6 +1889,7 @@
                     "version": "18.0.12",
                     "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
                     "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
+                    "dev": true,
                     "requires": {
                         "@types/prop-types": "*",
                         "@types/scheduler": "*",
@@ -1775,7 +1899,8 @@
                 "csstype": {
                     "version": "3.1.0",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
+                    "dev": true
                 }
             }
         },
@@ -1844,28 +1969,11 @@
             }
         },
         "@types/react-transition-group": {
-            "version": "4.4.4",
-            "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
-            "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
+            "version": "4.4.5",
+            "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
+            "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==",
             "requires": {
                 "@types/react": "*"
-            },
-            "dependencies": {
-                "@types/react": {
-                    "version": "18.0.12",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
-                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-                }
             }
         },
         "@types/reactcss": {
@@ -2359,22 +2467,22 @@
             }
         },
         "@webpack-cli/configtest": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz",
-            "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg=="
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
+            "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg=="
         },
         "@webpack-cli/info": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz",
-            "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==",
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
+            "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
             "requires": {
                 "envinfo": "^7.7.3"
             }
         },
         "@webpack-cli/serve": {
-            "version": "1.6.1",
-            "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz",
-            "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw=="
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
+            "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q=="
         },
         "@webscopeio/react-textarea-autocomplete": {
             "version": "4.9.1",
@@ -2487,7 +2595,7 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
         },
         "agent-base": {
             "version": "6.0.2",
@@ -2617,6 +2725,23 @@
                 }
             }
         },
+        "ansi-escapes": {
+            "version": "4.3.2",
+            "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+            "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+            "dev": true,
+            "requires": {
+                "type-fest": "^0.21.3"
+            },
+            "dependencies": {
+                "type-fest": {
+                    "version": "0.21.3",
+                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+                    "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+                    "dev": true
+                }
+            }
+        },
         "ansi-html-community": {
             "version": "0.0.8",
             "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
@@ -2735,6 +2860,16 @@
                 "sprintf-js": "~1.0.2"
             }
         },
+        "aria-query": {
+            "version": "4.2.2",
+            "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
+            "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
+            "dev": true,
+            "requires": {
+                "@babel/runtime": "^7.10.2",
+                "@babel/runtime-corejs3": "^7.10.2"
+            }
+        },
         "arr-diff": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -2852,6 +2987,19 @@
             "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
             "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
         },
+        "array-includes": {
+            "version": "3.1.5",
+            "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz",
+            "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.19.5",
+                "get-intrinsic": "^1.1.1",
+                "is-string": "^1.0.7"
+            }
+        },
         "array-union": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@@ -2872,6 +3020,30 @@
             "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
             "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
         },
+        "array.prototype.flat": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz",
+            "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.2",
+                "es-shim-unscopables": "^1.0.0"
+            }
+        },
+        "array.prototype.flatmap": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz",
+            "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.2",
+                "es-shim-unscopables": "^1.0.0"
+            }
+        },
         "array.prototype.reduce": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz",
@@ -2922,6 +3094,18 @@
             "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
             "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
         },
+        "ast-types-flow": {
+            "version": "0.0.7",
+            "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+            "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
+            "dev": true
+        },
+        "astral-regex": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+            "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+            "dev": true
+        },
         "async": {
             "version": "2.6.4",
             "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
@@ -3006,6 +3190,12 @@
             "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
             "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
         },
+        "axe-core": {
+            "version": "4.4.2",
+            "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.2.tgz",
+            "integrity": "sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==",
+            "dev": true
+        },
         "axios": {
             "version": "0.19.2",
             "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
@@ -3014,6 +3204,12 @@
                 "follow-redirects": "1.5.10"
             }
         },
+        "axobject-query": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
+            "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==",
+            "dev": true
+        },
         "babel": {
             "version": "6.23.0",
             "resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
@@ -3078,6 +3274,28 @@
                 }
             }
         },
+        "babel-eslint": {
+            "version": "10.1.0",
+            "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
+            "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==",
+            "dev": true,
+            "requires": {
+                "@babel/code-frame": "^7.0.0",
+                "@babel/parser": "^7.7.0",
+                "@babel/traverse": "^7.7.0",
+                "@babel/types": "^7.7.0",
+                "eslint-visitor-keys": "^1.0.0",
+                "resolve": "^1.12.0"
+            },
+            "dependencies": {
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                }
+            }
+        },
         "babel-plugin-emotion": {
             "version": "10.2.2",
             "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz",
@@ -3169,7 +3387,7 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
         },
         "balanced-match": {
             "version": "1.0.2",
@@ -3234,7 +3452,7 @@
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
+            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
         },
         "base64-js": {
             "version": "1.5.1",
@@ -3752,9 +3970,9 @@
             "integrity": "sha512-u+Ll+RDaiQEproTQjZLjZwyfNgNezA1fERMT7/54npcz+PkbVJUAHXMUz4bkXQYRPWrcFNO0Fbi1mwjfXg6N5g=="
         },
         "canvas": {
-            "version": "2.9.1",
-            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.1.tgz",
-            "integrity": "sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==",
+            "version": "2.9.3",
+            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.3.tgz",
+            "integrity": "sha512-WOUM7ghii5TV2rbhaZkh1youv/vW1/Canev6Yx6BG2W+1S07w8jKZqKkPnbiPpQEDsnJdN8ouDd7OvQEGXDcUw==",
             "requires": {
                 "@mapbox/node-pre-gyp": "^1.0.0",
                 "nan": "^2.15.0",
@@ -3822,6 +4040,12 @@
                 "is-regex": "^1.0.3"
             }
         },
+        "chardet": {
+            "version": "0.7.0",
+            "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+            "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+            "dev": true
+        },
         "check-error": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -3932,6 +4156,21 @@
             "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
             "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
         },
+        "cli-cursor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+            "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+            "dev": true,
+            "requires": {
+                "restore-cursor": "^3.1.0"
+            }
+        },
+        "cli-width": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+            "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+            "dev": true
+        },
         "clipboard": {
             "version": "1.7.1",
             "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
@@ -4140,7 +4379,7 @@
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -4150,7 +4389,7 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
         },
         "compress-brotli": {
             "version": "1.3.8",
@@ -4295,6 +4534,12 @@
                 }
             }
         },
+        "confusing-browser-globals": {
+            "version": "1.0.11",
+            "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+            "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+            "dev": true
+        },
         "connect-flash": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz",
@@ -4860,16 +5105,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -4995,6 +5230,12 @@
                 "d3-transition": "2"
             }
         },
+        "damerau-levenshtein": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+            "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+            "dev": true
+        },
         "dashdash": {
             "version": "1.14.1",
             "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -5584,6 +5825,15 @@
                 "buffer-indexof": "^1.0.0"
             }
         },
+        "doctrine": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+            "dev": true,
+            "requires": {
+                "esutils": "^2.0.2"
+            }
+        },
         "doctypes": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
@@ -5809,9 +6059,9 @@
             }
         },
         "engine.io": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz",
-            "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==",
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.6.0.tgz",
+            "integrity": "sha512-Kc8fo5bbg8F4a2f3HPHTEpGyq/IRIQpyeHu3H1ThR14XDD7VrLcsGBo16HUpahgp8YkHJDaU5gNxJZbuGcuueg==",
             "requires": {
                 "accepts": "~1.3.4",
                 "base64id": "2.0.0",
@@ -5865,7 +6115,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -5985,6 +6235,15 @@
             "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
             "dev": true
         },
+        "es-shim-unscopables": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+            "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+            "dev": true,
+            "requires": {
+                "has": "^1.0.3"
+            }
+        },
         "es-to-primitive": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
@@ -5995,28 +6254,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.61",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
-            "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6028,7 +6265,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -6069,36 +6305,1192 @@
                 }
             }
         },
-        "eslint-scope": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
-            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
-            "dev": true,
-            "requires": {
-                "esrecurse": "^4.3.0",
-                "estraverse": "^4.1.1"
-            }
-        },
-        "esprima": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
-        },
-        "esrecurse": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
-            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+        "eslint": {
+            "version": "8.19.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz",
+            "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==",
             "dev": true,
             "requires": {
-                "estraverse": "^5.2.0"
+                "@eslint/eslintrc": "^1.3.0",
+                "@humanwhocodes/config-array": "^0.9.2",
+                "ajv": "^6.10.0",
+                "chalk": "^4.0.0",
+                "cross-spawn": "^7.0.2",
+                "debug": "^4.3.2",
+                "doctrine": "^3.0.0",
+                "escape-string-regexp": "^4.0.0",
+                "eslint-scope": "^7.1.1",
+                "eslint-utils": "^3.0.0",
+                "eslint-visitor-keys": "^3.3.0",
+                "espree": "^9.3.2",
+                "esquery": "^1.4.0",
+                "esutils": "^2.0.2",
+                "fast-deep-equal": "^3.1.3",
+                "file-entry-cache": "^6.0.1",
+                "functional-red-black-tree": "^1.0.1",
+                "glob-parent": "^6.0.1",
+                "globals": "^13.15.0",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.0.0",
+                "imurmurhash": "^0.1.4",
+                "is-glob": "^4.0.0",
+                "js-yaml": "^4.1.0",
+                "json-stable-stringify-without-jsonify": "^1.0.1",
+                "levn": "^0.4.1",
+                "lodash.merge": "^4.6.2",
+                "minimatch": "^3.1.2",
+                "natural-compare": "^1.4.0",
+                "optionator": "^0.9.1",
+                "regexpp": "^3.2.0",
+                "strip-ansi": "^6.0.1",
+                "strip-json-comments": "^3.1.0",
+                "text-table": "^0.2.0",
+                "v8-compile-cache": "^2.0.3"
             },
             "dependencies": {
-                "estraverse": {
-                    "version": "5.3.0",
-                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
-                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                "ansi-regex": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
                     "dev": true
-                }
+                },
+                "ansi-styles": {
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+                    "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+                    "dev": true,
+                    "requires": {
+                        "color-convert": "^2.0.1"
+                    }
+                },
+                "argparse": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+                    "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+                    "dev": true
+                },
+                "chalk": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+                    "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^4.1.0",
+                        "supports-color": "^7.1.0"
+                    }
+                },
+                "color-convert": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+                    "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+                    "dev": true,
+                    "requires": {
+                        "color-name": "~1.1.4"
+                    }
+                },
+                "color-name": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+                    "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+                    "dev": true
+                },
+                "cross-spawn": {
+                    "version": "7.0.3",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+                    "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+                    "dev": true,
+                    "requires": {
+                        "path-key": "^3.1.0",
+                        "shebang-command": "^2.0.0",
+                        "which": "^2.0.1"
+                    }
+                },
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "escape-string-regexp": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+                    "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+                    "dev": true
+                },
+                "eslint-scope": {
+                    "version": "7.1.1",
+                    "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+                    "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+                    "dev": true,
+                    "requires": {
+                        "esrecurse": "^4.3.0",
+                        "estraverse": "^5.2.0"
+                    }
+                },
+                "estraverse": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                    "dev": true
+                },
+                "glob-parent": {
+                    "version": "6.0.2",
+                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+                    "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+                    "dev": true,
+                    "requires": {
+                        "is-glob": "^4.0.3"
+                    }
+                },
+                "globals": {
+                    "version": "13.16.0",
+                    "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
+                    "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
+                    "dev": true,
+                    "requires": {
+                        "type-fest": "^0.20.2"
+                    }
+                },
+                "has-flag": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+                    "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+                    "dev": true
+                },
+                "ignore": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+                    "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+                    "dev": true
+                },
+                "js-yaml": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+                    "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+                    "dev": true,
+                    "requires": {
+                        "argparse": "^2.0.1"
+                    }
+                },
+                "levn": {
+                    "version": "0.4.1",
+                    "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+                    "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+                    "dev": true,
+                    "requires": {
+                        "prelude-ls": "^1.2.1",
+                        "type-check": "~0.4.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "optionator": {
+                    "version": "0.9.1",
+                    "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+                    "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+                    "dev": true,
+                    "requires": {
+                        "deep-is": "^0.1.3",
+                        "fast-levenshtein": "^2.0.6",
+                        "levn": "^0.4.1",
+                        "prelude-ls": "^1.2.1",
+                        "type-check": "^0.4.0",
+                        "word-wrap": "^1.2.3"
+                    }
+                },
+                "path-key": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+                    "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+                    "dev": true
+                },
+                "prelude-ls": {
+                    "version": "1.2.1",
+                    "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+                    "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+                    "dev": true
+                },
+                "shebang-command": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+                    "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+                    "dev": true,
+                    "requires": {
+                        "shebang-regex": "^3.0.0"
+                    }
+                },
+                "shebang-regex": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+                    "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^5.0.1"
+                    }
+                },
+                "strip-json-comments": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+                    "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+                    "dev": true
+                },
+                "supports-color": {
+                    "version": "7.2.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+                    "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+                    "dev": true,
+                    "requires": {
+                        "has-flag": "^4.0.0"
+                    }
+                },
+                "type-check": {
+                    "version": "0.4.0",
+                    "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+                    "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+                    "dev": true,
+                    "requires": {
+                        "prelude-ls": "^1.2.1"
+                    }
+                },
+                "which": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+                    "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+                    "dev": true,
+                    "requires": {
+                        "isexe": "^2.0.0"
+                    }
+                }
+            }
+        },
+        "eslint-config-airbnb": {
+            "version": "19.0.4",
+            "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
+            "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
+            "dev": true,
+            "requires": {
+                "eslint-config-airbnb-base": "^15.0.0",
+                "object.assign": "^4.1.2",
+                "object.entries": "^1.1.5"
+            },
+            "dependencies": {
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "dev": true,
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                }
+            }
+        },
+        "eslint-config-airbnb-base": {
+            "version": "15.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+            "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+            "dev": true,
+            "requires": {
+                "confusing-browser-globals": "^1.0.10",
+                "object.assign": "^4.1.2",
+                "object.entries": "^1.1.5",
+                "semver": "^6.3.0"
+            },
+            "dependencies": {
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "dev": true,
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-config-esnext": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-esnext/-/eslint-config-esnext-4.1.0.tgz",
+            "integrity": "sha512-GhfVEXdqYKEIIj7j+Fw2SQdL9qyZMekgXfq6PyXM66cQw0B435ddjz3P3kxOBVihMRJ0xGYjosaveQz5Y6z0uA==",
+            "dev": true,
+            "requires": {
+                "babel-eslint": "^10.0.1",
+                "eslint": "^6.8.0",
+                "eslint-plugin-babel": "^5.2.1",
+                "eslint-plugin-import": "^2.14.0"
+            },
+            "dependencies": {
+                "acorn": {
+                    "version": "7.4.1",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+                    "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+                    "dev": true
+                },
+                "ansi-regex": {
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+                    "dev": true
+                },
+                "cross-spawn": {
+                    "version": "6.0.5",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+                    "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+                    "dev": true,
+                    "requires": {
+                        "nice-try": "^1.0.4",
+                        "path-key": "^2.0.1",
+                        "semver": "^5.5.0",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
+                    },
+                    "dependencies": {
+                        "semver": {
+                            "version": "5.7.1",
+                            "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                            "dev": true
+                        }
+                    }
+                },
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "eslint": {
+                    "version": "6.8.0",
+                    "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+                    "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+                    "dev": true,
+                    "requires": {
+                        "@babel/code-frame": "^7.0.0",
+                        "ajv": "^6.10.0",
+                        "chalk": "^2.1.0",
+                        "cross-spawn": "^6.0.5",
+                        "debug": "^4.0.1",
+                        "doctrine": "^3.0.0",
+                        "eslint-scope": "^5.0.0",
+                        "eslint-utils": "^1.4.3",
+                        "eslint-visitor-keys": "^1.1.0",
+                        "espree": "^6.1.2",
+                        "esquery": "^1.0.1",
+                        "esutils": "^2.0.2",
+                        "file-entry-cache": "^5.0.1",
+                        "functional-red-black-tree": "^1.0.1",
+                        "glob-parent": "^5.0.0",
+                        "globals": "^12.1.0",
+                        "ignore": "^4.0.6",
+                        "import-fresh": "^3.0.0",
+                        "imurmurhash": "^0.1.4",
+                        "inquirer": "^7.0.0",
+                        "is-glob": "^4.0.0",
+                        "js-yaml": "^3.13.1",
+                        "json-stable-stringify-without-jsonify": "^1.0.1",
+                        "levn": "^0.3.0",
+                        "lodash": "^4.17.14",
+                        "minimatch": "^3.0.4",
+                        "mkdirp": "^0.5.1",
+                        "natural-compare": "^1.4.0",
+                        "optionator": "^0.8.3",
+                        "progress": "^2.0.0",
+                        "regexpp": "^2.0.1",
+                        "semver": "^6.1.2",
+                        "strip-ansi": "^5.2.0",
+                        "strip-json-comments": "^3.0.1",
+                        "table": "^5.2.3",
+                        "text-table": "^0.2.0",
+                        "v8-compile-cache": "^2.0.3"
+                    }
+                },
+                "eslint-utils": {
+                    "version": "1.4.3",
+                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+                    "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+                    "dev": true,
+                    "requires": {
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                },
+                "espree": {
+                    "version": "6.2.1",
+                    "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
+                    "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+                    "dev": true,
+                    "requires": {
+                        "acorn": "^7.1.1",
+                        "acorn-jsx": "^5.2.0",
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "file-entry-cache": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+                    "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+                    "dev": true,
+                    "requires": {
+                        "flat-cache": "^2.0.1"
+                    }
+                },
+                "flat-cache": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+                    "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+                    "dev": true,
+                    "requires": {
+                        "flatted": "^2.0.0",
+                        "rimraf": "2.6.3",
+                        "write": "1.0.3"
+                    }
+                },
+                "flatted": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+                    "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+                    "dev": true
+                },
+                "glob-parent": {
+                    "version": "5.1.2",
+                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+                    "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+                    "dev": true,
+                    "requires": {
+                        "is-glob": "^4.0.1"
+                    }
+                },
+                "globals": {
+                    "version": "12.4.0",
+                    "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+                    "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+                    "dev": true,
+                    "requires": {
+                        "type-fest": "^0.8.1"
+                    }
+                },
+                "ignore": {
+                    "version": "4.0.6",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+                    "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+                    "dev": true
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "regexpp": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+                    "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+                    "dev": true
+                },
+                "rimraf": {
+                    "version": "2.6.3",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+                    "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+                    "dev": true,
+                    "requires": {
+                        "glob": "^7.1.3"
+                    }
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                },
+                "strip-json-comments": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+                    "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+                    "dev": true
+                },
+                "type-fest": {
+                    "version": "0.8.1",
+                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+                    "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-config-node": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-node/-/eslint-config-node-4.1.0.tgz",
+            "integrity": "sha512-Wz17xV5O2WFG8fGdMYEBdbiL6TL7YNJSJvSX9V4sXQownewfYmoqlly7wxqLkOUv/57pq6LnnotMiQQrrPjCqQ==",
+            "dev": true,
+            "requires": {
+                "eslint": "^6.8.0",
+                "eslint-config-esnext": "^4.1.0"
+            },
+            "dependencies": {
+                "acorn": {
+                    "version": "7.4.1",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+                    "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+                    "dev": true
+                },
+                "ansi-regex": {
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+                    "dev": true
+                },
+                "cross-spawn": {
+                    "version": "6.0.5",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+                    "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+                    "dev": true,
+                    "requires": {
+                        "nice-try": "^1.0.4",
+                        "path-key": "^2.0.1",
+                        "semver": "^5.5.0",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
+                    },
+                    "dependencies": {
+                        "semver": {
+                            "version": "5.7.1",
+                            "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                            "dev": true
+                        }
+                    }
+                },
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "eslint": {
+                    "version": "6.8.0",
+                    "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+                    "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+                    "dev": true,
+                    "requires": {
+                        "@babel/code-frame": "^7.0.0",
+                        "ajv": "^6.10.0",
+                        "chalk": "^2.1.0",
+                        "cross-spawn": "^6.0.5",
+                        "debug": "^4.0.1",
+                        "doctrine": "^3.0.0",
+                        "eslint-scope": "^5.0.0",
+                        "eslint-utils": "^1.4.3",
+                        "eslint-visitor-keys": "^1.1.0",
+                        "espree": "^6.1.2",
+                        "esquery": "^1.0.1",
+                        "esutils": "^2.0.2",
+                        "file-entry-cache": "^5.0.1",
+                        "functional-red-black-tree": "^1.0.1",
+                        "glob-parent": "^5.0.0",
+                        "globals": "^12.1.0",
+                        "ignore": "^4.0.6",
+                        "import-fresh": "^3.0.0",
+                        "imurmurhash": "^0.1.4",
+                        "inquirer": "^7.0.0",
+                        "is-glob": "^4.0.0",
+                        "js-yaml": "^3.13.1",
+                        "json-stable-stringify-without-jsonify": "^1.0.1",
+                        "levn": "^0.3.0",
+                        "lodash": "^4.17.14",
+                        "minimatch": "^3.0.4",
+                        "mkdirp": "^0.5.1",
+                        "natural-compare": "^1.4.0",
+                        "optionator": "^0.8.3",
+                        "progress": "^2.0.0",
+                        "regexpp": "^2.0.1",
+                        "semver": "^6.1.2",
+                        "strip-ansi": "^5.2.0",
+                        "strip-json-comments": "^3.0.1",
+                        "table": "^5.2.3",
+                        "text-table": "^0.2.0",
+                        "v8-compile-cache": "^2.0.3"
+                    }
+                },
+                "eslint-utils": {
+                    "version": "1.4.3",
+                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+                    "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+                    "dev": true,
+                    "requires": {
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                },
+                "espree": {
+                    "version": "6.2.1",
+                    "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
+                    "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+                    "dev": true,
+                    "requires": {
+                        "acorn": "^7.1.1",
+                        "acorn-jsx": "^5.2.0",
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "file-entry-cache": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+                    "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+                    "dev": true,
+                    "requires": {
+                        "flat-cache": "^2.0.1"
+                    }
+                },
+                "flat-cache": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+                    "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+                    "dev": true,
+                    "requires": {
+                        "flatted": "^2.0.0",
+                        "rimraf": "2.6.3",
+                        "write": "1.0.3"
+                    }
+                },
+                "flatted": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+                    "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+                    "dev": true
+                },
+                "glob-parent": {
+                    "version": "5.1.2",
+                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+                    "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+                    "dev": true,
+                    "requires": {
+                        "is-glob": "^4.0.1"
+                    }
+                },
+                "globals": {
+                    "version": "12.4.0",
+                    "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+                    "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+                    "dev": true,
+                    "requires": {
+                        "type-fest": "^0.8.1"
+                    }
+                },
+                "ignore": {
+                    "version": "4.0.6",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+                    "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+                    "dev": true
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "regexpp": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+                    "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+                    "dev": true
+                },
+                "rimraf": {
+                    "version": "2.6.3",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+                    "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+                    "dev": true,
+                    "requires": {
+                        "glob": "^7.1.3"
+                    }
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                },
+                "strip-json-comments": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+                    "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+                    "dev": true
+                },
+                "type-fest": {
+                    "version": "0.8.1",
+                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+                    "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-config-prettier": {
+            "version": "8.5.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
+            "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
+            "dev": true
+        },
+        "eslint-import-resolver-node": {
+            "version": "0.3.6",
+            "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz",
+            "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==",
+            "dev": true,
+            "requires": {
+                "debug": "^3.2.7",
+                "resolve": "^1.20.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.2.7",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+                    "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
+                }
+            }
+        },
+        "eslint-module-utils": {
+            "version": "2.7.3",
+            "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz",
+            "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==",
+            "dev": true,
+            "requires": {
+                "debug": "^3.2.7",
+                "find-up": "^2.1.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.2.7",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+                    "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
+                },
+                "find-up": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+                    "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+                    "dev": true,
+                    "requires": {
+                        "locate-path": "^2.0.0"
+                    }
+                },
+                "locate-path": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+                    "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+                    "dev": true,
+                    "requires": {
+                        "p-locate": "^2.0.0",
+                        "path-exists": "^3.0.0"
+                    }
+                },
+                "p-limit": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+                    "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+                    "dev": true,
+                    "requires": {
+                        "p-try": "^1.0.0"
+                    }
+                },
+                "p-locate": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+                    "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+                    "dev": true,
+                    "requires": {
+                        "p-limit": "^1.1.0"
+                    }
+                },
+                "p-try": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+                    "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-babel": {
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-babel/-/eslint-plugin-babel-5.3.1.tgz",
+            "integrity": "sha512-VsQEr6NH3dj664+EyxJwO4FCYm/00JhYb3Sk3ft8o+fpKuIfQ9TaW6uVUfvwMXHcf/lsnRIoyFPsLMyiWCSL/g==",
+            "dev": true,
+            "requires": {
+                "eslint-rule-composer": "^0.3.0"
+            }
+        },
+        "eslint-plugin-es": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
+            "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+            "dev": true,
+            "requires": {
+                "eslint-utils": "^2.0.0",
+                "regexpp": "^3.0.0"
+            },
+            "dependencies": {
+                "eslint-utils": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+                    "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+                    "dev": true,
+                    "requires": {
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-import": {
+            "version": "2.26.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
+            "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
+            "dev": true,
+            "requires": {
+                "array-includes": "^3.1.4",
+                "array.prototype.flat": "^1.2.5",
+                "debug": "^2.6.9",
+                "doctrine": "^2.1.0",
+                "eslint-import-resolver-node": "^0.3.6",
+                "eslint-module-utils": "^2.7.3",
+                "has": "^1.0.3",
+                "is-core-module": "^2.8.1",
+                "is-glob": "^4.0.3",
+                "minimatch": "^3.1.2",
+                "object.values": "^1.1.5",
+                "resolve": "^1.22.0",
+                "tsconfig-paths": "^3.14.1"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "2.6.9",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+                    "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "doctrine": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+                    "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+                    "dev": true,
+                    "requires": {
+                        "esutils": "^2.0.2"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-jsx-a11y": {
+            "version": "6.6.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.0.tgz",
+            "integrity": "sha512-kTeLuIzpNhXL2CwLlc8AHI0aFRwWHcg483yepO9VQiHzM9bZwJdzTkzBszbuPrbgGmq2rlX/FaT2fJQsjUSHsw==",
+            "dev": true,
+            "requires": {
+                "@babel/runtime": "^7.18.3",
+                "aria-query": "^4.2.2",
+                "array-includes": "^3.1.5",
+                "ast-types-flow": "^0.0.7",
+                "axe-core": "^4.4.2",
+                "axobject-query": "^2.2.0",
+                "damerau-levenshtein": "^1.0.8",
+                "emoji-regex": "^9.2.2",
+                "has": "^1.0.3",
+                "jsx-ast-utils": "^3.3.1",
+                "language-tags": "^1.0.5",
+                "minimatch": "^3.1.2",
+                "semver": "^6.3.0"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "9.2.2",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+                    "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+                    "dev": true
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-node": {
+            "version": "11.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
+            "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+            "dev": true,
+            "requires": {
+                "eslint-plugin-es": "^3.0.0",
+                "eslint-utils": "^2.0.0",
+                "ignore": "^5.1.1",
+                "minimatch": "^3.0.4",
+                "resolve": "^1.10.1",
+                "semver": "^6.1.0"
+            },
+            "dependencies": {
+                "eslint-utils": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+                    "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+                    "dev": true,
+                    "requires": {
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                },
+                "ignore": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+                    "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+                    "dev": true
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-prettier": {
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+            "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+            "dev": true,
+            "requires": {
+                "prettier-linter-helpers": "^1.0.0"
+            }
+        },
+        "eslint-plugin-react": {
+            "version": "7.30.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz",
+            "integrity": "sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==",
+            "dev": true,
+            "requires": {
+                "array-includes": "^3.1.5",
+                "array.prototype.flatmap": "^1.3.0",
+                "doctrine": "^2.1.0",
+                "estraverse": "^5.3.0",
+                "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+                "minimatch": "^3.1.2",
+                "object.entries": "^1.1.5",
+                "object.fromentries": "^2.0.5",
+                "object.hasown": "^1.1.1",
+                "object.values": "^1.1.5",
+                "prop-types": "^15.8.1",
+                "resolve": "^2.0.0-next.3",
+                "semver": "^6.3.0",
+                "string.prototype.matchall": "^4.0.7"
+            },
+            "dependencies": {
+                "doctrine": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+                    "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+                    "dev": true,
+                    "requires": {
+                        "esutils": "^2.0.2"
+                    }
+                },
+                "estraverse": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                    "dev": true
+                },
+                "resolve": {
+                    "version": "2.0.0-next.4",
+                    "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
+                    "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
+                    "dev": true,
+                    "requires": {
+                        "is-core-module": "^2.9.0",
+                        "path-parse": "^1.0.7",
+                        "supports-preserve-symlinks-flag": "^1.0.0"
+                    }
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-react-hooks": {
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
+            "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
+            "dev": true
+        },
+        "eslint-rule-composer": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz",
+            "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==",
+            "dev": true
+        },
+        "eslint-scope": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+            "dev": true,
+            "requires": {
+                "esrecurse": "^4.3.0",
+                "estraverse": "^4.1.1"
+            }
+        },
+        "eslint-utils": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+            "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+            "dev": true,
+            "requires": {
+                "eslint-visitor-keys": "^2.0.0"
+            },
+            "dependencies": {
+                "eslint-visitor-keys": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+                    "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-visitor-keys": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+            "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+            "dev": true
+        },
+        "espree": {
+            "version": "9.3.2",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
+            "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+            "dev": true,
+            "requires": {
+                "acorn": "^8.7.1",
+                "acorn-jsx": "^5.3.2",
+                "eslint-visitor-keys": "^3.3.0"
+            }
+        },
+        "esprima": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+        },
+        "esquery": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+            "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+            "dev": true,
+            "requires": {
+                "estraverse": "^5.1.0"
+            },
+            "dependencies": {
+                "estraverse": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                    "dev": true
+                }
+            }
+        },
+        "esrecurse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+            "dev": true,
+            "requires": {
+                "estraverse": "^5.2.0"
+            },
+            "dependencies": {
+                "estraverse": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                    "dev": true
+                }
             }
         },
         "estraverse": {
@@ -6451,6 +7843,28 @@
                 }
             }
         },
+        "external-editor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+            "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+            "dev": true,
+            "requires": {
+                "chardet": "^0.7.0",
+                "iconv-lite": "^0.4.24",
+                "tmp": "^0.0.33"
+            },
+            "dependencies": {
+                "iconv-lite": {
+                    "version": "0.4.24",
+                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+                    "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+                    "dev": true,
+                    "requires": {
+                        "safer-buffer": ">= 2.1.2 < 3"
+                    }
+                }
+            }
+        },
         "extglob": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
@@ -6554,6 +7968,12 @@
             "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
             "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
         },
+        "fast-diff": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+            "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+            "dev": true
+        },
         "fast-json-stable-stringify": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -6614,6 +8034,24 @@
                 "when": ">= 0.0.1"
             }
         },
+        "figures": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+            "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+            "dev": true,
+            "requires": {
+                "escape-string-regexp": "^1.0.5"
+            }
+        },
+        "file-entry-cache": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+            "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+            "dev": true,
+            "requires": {
+                "flat-cache": "^3.0.4"
+            }
+        },
         "file-loader": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz",
@@ -6818,6 +8256,22 @@
                 "is-buffer": "~2.0.3"
             }
         },
+        "flat-cache": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+            "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+            "dev": true,
+            "requires": {
+                "flatted": "^3.1.0",
+                "rimraf": "^3.0.2"
+            }
+        },
+        "flatted": {
+            "version": "3.2.6",
+            "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
+            "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
+            "dev": true
+        },
         "flexlayout-react": {
             "version": "0.3.11",
             "resolved": "https://registry.npmjs.org/flexlayout-react/-/flexlayout-react-0.3.11.tgz",
@@ -7144,6 +8598,12 @@
                 "functions-have-names": "^1.2.2"
             }
         },
+        "functional-red-black-tree": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+            "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+            "dev": true
+        },
         "functions-have-names": {
             "version": "1.2.3",
             "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
@@ -7645,14 +9105,14 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
         },
         "has-flag": {
             "version": "3.0.0",
@@ -7963,11 +9423,6 @@
                 }
             }
         },
-        "human-signals": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
-            "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
-        },
         "hyntax": {
             "version": "1.1.9",
             "resolved": "https://registry.npmjs.org/hyntax/-/hyntax-1.1.9.tgz",
@@ -8153,7 +9608,7 @@
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
         },
         "inflight": {
             "version": "1.0.6",
@@ -8198,6 +9653,116 @@
                 "css-in-js-utils": "^2.0.0"
             }
         },
+        "inquirer": {
+            "version": "7.3.3",
+            "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
+            "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
+            "dev": true,
+            "requires": {
+                "ansi-escapes": "^4.2.1",
+                "chalk": "^4.1.0",
+                "cli-cursor": "^3.1.0",
+                "cli-width": "^3.0.0",
+                "external-editor": "^3.0.3",
+                "figures": "^3.0.0",
+                "lodash": "^4.17.19",
+                "mute-stream": "0.0.8",
+                "run-async": "^2.4.0",
+                "rxjs": "^6.6.0",
+                "string-width": "^4.1.0",
+                "strip-ansi": "^6.0.0",
+                "through": "^2.3.6"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+                    "dev": true
+                },
+                "ansi-styles": {
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+                    "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+                    "dev": true,
+                    "requires": {
+                        "color-convert": "^2.0.1"
+                    }
+                },
+                "chalk": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+                    "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^4.1.0",
+                        "supports-color": "^7.1.0"
+                    }
+                },
+                "color-convert": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+                    "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+                    "dev": true,
+                    "requires": {
+                        "color-name": "~1.1.4"
+                    }
+                },
+                "color-name": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+                    "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+                    "dev": true
+                },
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+                    "dev": true
+                },
+                "has-flag": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+                    "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^5.0.1"
+                    }
+                },
+                "supports-color": {
+                    "version": "7.2.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+                    "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+                    "dev": true,
+                    "requires": {
+                        "has-flag": "^4.0.0"
+                    }
+                }
+            }
+        },
         "inspect-function": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
@@ -9055,6 +10620,12 @@
             "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
             "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
         },
+        "json-stable-stringify-without-jsonify": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+            "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+            "dev": true
+        },
         "json-stringify-safe": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -9195,6 +10766,30 @@
                 "promise": "^7.0.1"
             }
         },
+        "jsx-ast-utils": {
+            "version": "3.3.2",
+            "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz",
+            "integrity": "sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==",
+            "dev": true,
+            "requires": {
+                "array-includes": "^3.1.5",
+                "object.assign": "^4.1.2"
+            },
+            "dependencies": {
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "dev": true,
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                }
+            }
+        },
         "jszip": {
             "version": "3.10.0",
             "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
@@ -9292,6 +10887,21 @@
                 "graceful-fs": "^4.1.9"
             }
         },
+        "language-subtag-registry": {
+            "version": "0.3.22",
+            "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
+            "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
+            "dev": true
+        },
+        "language-tags": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
+            "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
+            "dev": true,
+            "requires": {
+                "language-subtag-registry": "~0.3.2"
+            }
+        },
         "latest-version": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
@@ -9460,7 +11070,7 @@
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
@@ -9711,7 +11321,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
+            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -9719,7 +11329,7 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
+                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
                 }
             }
         },
@@ -9817,7 +11427,8 @@
         "merge-stream": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
-            "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+            "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+            "dev": true
         },
         "methods": {
             "version": "1.1.2",
@@ -9871,7 +11482,8 @@
         "mimic-fn": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+            "dev": true
         },
         "mimic-response": {
             "version": "2.1.0",
@@ -10301,6 +11913,12 @@
             "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
             "dev": true
         },
+        "mute-stream": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+            "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+            "dev": true
+        },
         "nan": {
             "version": "2.16.0",
             "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
@@ -10339,6 +11957,12 @@
             "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz",
             "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck="
         },
+        "natural-compare": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+            "dev": true
+        },
         "negotiator": {
             "version": "0.6.3",
             "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -14468,6 +16092,28 @@
                 "object-keys": "^1.0.11"
             }
         },
+        "object.entries": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz",
+            "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.1"
+            }
+        },
+        "object.fromentries": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz",
+            "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.1"
+            }
+        },
         "object.getownpropertydescriptors": {
             "version": "2.1.4",
             "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz",
@@ -14479,6 +16125,16 @@
                 "es-abstract": "^1.20.1"
             }
         },
+        "object.hasown": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz",
+            "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.19.5"
+            }
+        },
         "object.pick": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
@@ -14487,6 +16143,17 @@
                 "isobject": "^3.0.1"
             }
         },
+        "object.values": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz",
+            "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.1"
+            }
+        },
         "obuf": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
@@ -14518,6 +16185,7 @@
             "version": "5.1.2",
             "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
             "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+            "dev": true,
             "requires": {
                 "mimic-fn": "^2.1.0"
             }
@@ -14563,9 +16231,9 @@
             }
         },
         "orderedmap": {
-            "version": "1.1.8",
-            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.8.tgz",
-            "integrity": "sha512-eWEYOAggZZpZbJ9CTsqAKOTxlbBHdHZ8pzcfEvNTxGrjQ/m+Q25nSWUiMlT9MTbgpB6FOiBDKqsgJ2FlLDVNaw=="
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.0.0.tgz",
+            "integrity": "sha512-buf4PoAMlh45b8a8gsGy/X6w279TSqkyAS0C0wdTSJwFSU+ljQFJON5I8NfjLHoCXwpSROIo2wr0g33T+kQshQ=="
         },
         "os-homedir": {
             "version": "1.0.2",
@@ -15270,6 +16938,21 @@
             "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
             "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
         },
+        "prettier": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+            "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
+            "dev": true
+        },
+        "prettier-linter-helpers": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+            "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+            "dev": true,
+            "requires": {
+                "fast-diff": "^1.1.2"
+            }
+        },
         "pretty-error": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
@@ -15402,11 +17085,11 @@
             }
         },
         "prosemirror-model": {
-            "version": "1.18.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.0.tgz",
-            "integrity": "sha512-EZLl0mvVIixEL1V538GsPP4Iw+D83gBRdLcjL7VouzoPPWlhhv/0Y0V9ttT5lUhF7jieIrNHOa3XNFs1FwgrOw==",
+            "version": "1.18.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz",
+            "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==",
             "requires": {
-                "orderedmap": "^1.1.0"
+                "orderedmap": "^2.0.0"
             }
         },
         "prosemirror-schema-list": {
@@ -15420,9 +17103,9 @@
             }
         },
         "prosemirror-state": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.0.tgz",
-            "integrity": "sha512-mVDZdjNX/YT5FvypiwbphJe9psA5h+j9apsSszVRFc6oKFoIInvzdujh8QW9f9lwHtSYajLxNiM1hPhd0Sl1XA==",
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz",
+            "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
@@ -15437,9 +17120,9 @@
             }
         },
         "prosemirror-view": {
-            "version": "1.26.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.0.tgz",
-            "integrity": "sha512-tT8ICUAw6YfOTRIwVQJoCdF65BYxoWFjAqmv44Kn4phYjdLJ8Vc92dhL3LnmwAQuZ9yfPHzRR6Lfb3/5EVJHWg==",
+            "version": "1.26.5",
+            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.5.tgz",
+            "integrity": "sha512-SO+AX6WwdbJZHVvuloXI0qfO+YJAnZAat8qrYwfiqTQwL/FewLUnr0m3EXZ6a60hQs8/Q/lzeJXiFR/dOPaaKQ==",
             "requires": {
                 "prosemirror-model": "^1.16.0",
                 "prosemirror-state": "^1.0.0",
@@ -15981,15 +17664,26 @@
             }
         },
         "react-grid-layout": {
-            "version": "0.18.3",
-            "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-0.18.3.tgz",
-            "integrity": "sha512-lHkrk941Tk5nTwZPa9uj6ttHBT0VehSHwEhWbINBJKvM1GRaFNOefvjcuxSyuCI5JWjVUP+Qm3ARt2470AlxMA==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-1.3.4.tgz",
+            "integrity": "sha512-sB3rNhorW77HUdOjB4JkelZTdJGQKuXLl3gNg+BI8gJkTScspL1myfZzW/EM0dLEn+1eH+xW+wNqk0oIM9o7cw==",
             "requires": {
-                "classnames": "2.x",
+                "clsx": "^1.1.1",
                 "lodash.isequal": "^4.0.0",
-                "prop-types": "^15.0.0",
+                "prop-types": "^15.8.1",
                 "react-draggable": "^4.0.0",
-                "react-resizable": "^1.9.0"
+                "react-resizable": "^3.0.4"
+            },
+            "dependencies": {
+                "react-resizable": {
+                    "version": "3.0.4",
+                    "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz",
+                    "integrity": "sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==",
+                    "requires": {
+                        "prop-types": "15.x",
+                        "react-draggable": "^4.0.3"
+                    }
+                }
             }
         },
         "react-icons": {
@@ -16189,9 +17883,9 @@
             }
         },
         "react-refresh-typescript": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.5.tgz",
-            "integrity": "sha512-TlcxF9VMBBS8dHvMD+3dklgArXeIhjbtT7R09ycsXvhPPYmv70J67kiGT8mxe6/tYzkql9aCHaZAY6QYdutLdw=="
+            "version": "2.0.7",
+            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.7.tgz",
+            "integrity": "sha512-KbuW57FauO11e6a9gU836sCm3M3z0b2+J2qPhUudg0QplOfz0eAF3gMeshcUC/ChfNLJCK1SZxvYmUtRxiZE5A=="
         },
         "react-resizable": {
             "version": "1.11.1",
@@ -16298,14 +17992,30 @@
             "integrity": "sha512-EblIqTAsIpkYeM8bZtC4lcpTE0A2zCEGipFB52RgcQq/q+0oryrk7Sxt+sqhIjUu6xMNEVywV8dr74lz5yWO6A=="
         },
         "react-transition-group": {
-            "version": "2.9.0",
-            "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
-            "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+            "version": "4.4.2",
+            "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+            "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
             "requires": {
-                "dom-helpers": "^3.4.0",
+                "@babel/runtime": "^7.5.5",
+                "dom-helpers": "^5.0.1",
                 "loose-envify": "^1.4.0",
-                "prop-types": "^15.6.2",
-                "react-lifecycles-compat": "^3.0.4"
+                "prop-types": "^15.6.2"
+            },
+            "dependencies": {
+                "csstype": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                },
+                "dom-helpers": {
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+                    "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+                    "requires": {
+                        "@babel/runtime": "^7.8.7",
+                        "csstype": "^3.0.2"
+                    }
+                }
             }
         },
         "react-use-measure": {
@@ -16492,6 +18202,12 @@
                 "functions-have-names": "^1.2.2"
             }
         },
+        "regexpp": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+            "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+            "dev": true
+        },
         "registry-auth-token": {
             "version": "3.4.0",
             "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
@@ -16811,6 +18527,16 @@
                 }
             }
         },
+        "restore-cursor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+            "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+            "dev": true,
+            "requires": {
+                "onetime": "^5.1.0",
+                "signal-exit": "^3.0.2"
+            }
+        },
         "ret": {
             "version": "0.1.15",
             "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@@ -16856,6 +18582,12 @@
                 "sdp": "^2.6.0"
             }
         },
+        "run-async": {
+            "version": "2.4.1",
+            "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+            "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+            "dev": true
+        },
         "run-queue": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
@@ -16873,6 +18605,23 @@
                 }
             }
         },
+        "rxjs": {
+            "version": "6.6.7",
+            "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+            "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+            "dev": true,
+            "requires": {
+                "tslib": "^1.9.0"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "1.14.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+                    "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+                    "dev": true
+                }
+            }
+        },
         "safe-buffer": {
             "version": "5.1.2",
             "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -17446,6 +19195,17 @@
             "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
             "dev": true
         },
+        "slice-ansi": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+            "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+            "dev": true,
+            "requires": {
+                "ansi-styles": "^3.2.0",
+                "astral-regex": "^1.0.0",
+                "is-fullwidth-code-point": "^2.0.0"
+            }
+        },
         "sliced": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
@@ -17567,15 +19327,15 @@
             }
         },
         "socket.io": {
-            "version": "2.4.1",
-            "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz",
-            "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==",
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.5.0.tgz",
+            "integrity": "sha512-gGunfS0od3VpwDBpGwVkzSZx6Aqo9uOcf1afJj2cKnKFAoyl16fvhpsUhmUFd4Ldbvl5JvRQed6eQw6oQp6n8w==",
             "requires": {
                 "debug": "~4.1.0",
-                "engine.io": "~3.5.0",
+                "engine.io": "~3.6.0",
                 "has-binary2": "~1.0.2",
                 "socket.io-adapter": "~1.1.0",
-                "socket.io-client": "2.4.0",
+                "socket.io-client": "2.5.0",
                 "socket.io-parser": "~3.4.0"
             },
             "dependencies": {
@@ -17595,9 +19355,9 @@
             "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
         },
         "socket.io-client": {
-            "version": "2.4.0",
-            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
-            "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.5.0.tgz",
+            "integrity": "sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw==",
             "requires": {
                 "backo2": "1.0.2",
                 "component-bind": "1.0.0",
@@ -17623,12 +19383,12 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "socket.io-parser": {
                     "version": "3.3.2",
@@ -18052,6 +19812,22 @@
             "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
             "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
         },
+        "string.prototype.matchall": {
+            "version": "4.0.7",
+            "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz",
+            "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.1",
+                "get-intrinsic": "^1.1.1",
+                "has-symbols": "^1.0.3",
+                "internal-slot": "^1.0.3",
+                "regexp.prototype.flags": "^1.4.1",
+                "side-channel": "^1.0.4"
+            }
+        },
         "string.prototype.trimend": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
@@ -18128,11 +19904,6 @@
             "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
             "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
         },
-        "strip-final-newline": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
-            "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
-        },
         "strip-indent": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
@@ -18240,6 +20011,46 @@
             "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
             "dev": true
         },
+        "table": {
+            "version": "5.4.6",
+            "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+            "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+            "dev": true,
+            "requires": {
+                "ajv": "^6.10.2",
+                "lodash": "^4.17.14",
+                "slice-ansi": "^2.1.0",
+                "string-width": "^3.0.0"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                    "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                }
+            }
+        },
         "table-layout": {
             "version": "0.4.5",
             "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz",
@@ -18373,6 +20184,12 @@
                 }
             }
         },
+        "text-table": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+            "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+            "dev": true
+        },
         "textarea-caret": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.1.0.tgz",
@@ -18451,10 +20268,19 @@
             "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
             "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="
         },
+        "tmp": {
+            "version": "0.0.33",
+            "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+            "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+            "dev": true,
+            "requires": {
+                "os-tmpdir": "~1.0.2"
+            }
+        },
         "to-array": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
-            "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+            "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
         },
         "to-fast-properties": {
             "version": "2.0.0",
@@ -18835,6 +20661,26 @@
                 }
             }
         },
+        "tsconfig-paths": {
+            "version": "3.14.1",
+            "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+            "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
+            "dev": true,
+            "requires": {
+                "@types/json5": "^0.0.29",
+                "json5": "^1.0.1",
+                "minimist": "^1.2.6",
+                "strip-bom": "^3.0.0"
+            },
+            "dependencies": {
+                "strip-bom": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+                    "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+                    "dev": true
+                }
+            }
+        },
         "tslib": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
@@ -18946,12 +20792,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@@ -18966,6 +20806,12 @@
             "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
             "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
         },
+        "type-fest": {
+            "version": "0.20.2",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+            "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+            "dev": true
+        },
         "type-is": {
             "version": "1.6.18",
             "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -18986,9 +20832,9 @@
             "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
         },
         "typescript": {
-            "version": "4.7.3",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
-            "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
+            "version": "4.7.4",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
+            "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
             "dev": true
         },
         "typescript-collections": {
@@ -19415,6 +21261,12 @@
             "resolved": "https://registry.npmjs.org/uuid-js/-/uuid-js-0.7.5.tgz",
             "integrity": "sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA="
         },
+        "v8-compile-cache": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+            "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+            "dev": true
+        },
         "valid-url": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
@@ -19637,17 +21489,17 @@
             }
         },
         "webpack-cli": {
-            "version": "4.9.2",
-            "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz",
-            "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==",
+            "version": "4.10.0",
+            "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
+            "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
             "requires": {
                 "@discoveryjs/json-ext": "^0.5.0",
-                "@webpack-cli/configtest": "^1.1.1",
-                "@webpack-cli/info": "^1.4.1",
-                "@webpack-cli/serve": "^1.6.1",
+                "@webpack-cli/configtest": "^1.2.0",
+                "@webpack-cli/info": "^1.5.0",
+                "@webpack-cli/serve": "^1.7.0",
                 "colorette": "^2.0.14",
                 "commander": "^7.0.0",
-                "execa": "^5.0.0",
+                "cross-spawn": "^7.0.3",
                 "fastest-levenshtein": "^1.0.12",
                 "import-local": "^3.0.2",
                 "interpret": "^2.2.0",
@@ -19670,40 +21522,11 @@
                         "which": "^2.0.1"
                     }
                 },
-                "execa": {
-                    "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
-                    "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
-                    "requires": {
-                        "cross-spawn": "^7.0.3",
-                        "get-stream": "^6.0.0",
-                        "human-signals": "^2.1.0",
-                        "is-stream": "^2.0.0",
-                        "merge-stream": "^2.0.0",
-                        "npm-run-path": "^4.0.1",
-                        "onetime": "^5.1.2",
-                        "signal-exit": "^3.0.3",
-                        "strip-final-newline": "^2.0.0"
-                    }
-                },
                 "interpret": {
                     "version": "2.2.0",
                     "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
                     "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
                 },
-                "is-stream": {
-                    "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-                    "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
-                },
-                "npm-run-path": {
-                    "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
-                    "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
-                    "requires": {
-                        "path-key": "^3.0.0"
-                    }
-                },
                 "path-key": {
                     "version": "3.1.1",
                     "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -20281,6 +22104,15 @@
             "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
             "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
         },
+        "write": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+            "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+            "dev": true,
+            "requires": {
+                "mkdirp": "^0.5.1"
+            }
+        },
         "write-file-atomic": {
             "version": "2.4.3",
             "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
@@ -20445,7 +22277,7 @@
         "yeast": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
-            "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+            "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
         },
         "yn": {
             "version": "2.0.0",
diff --git a/package.json b/package.json
index e4ec2798d..93968dce4 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
         "@types/exif": "^0.6.3",
         "@types/express": "^4.17.13",
         "@types/express-flash": "0.0.0",
-        "@types/express-session": "^1.17.4",
+        "@types/express-session": "^1.17.5",
         "@types/express-validator": "^3.0.0",
         "@types/file-saver": "^2.0.5",
         "@types/google-maps-react": "^2.0.5",
@@ -49,7 +49,7 @@
         "@types/mongoose": "^5.11.97",
         "@types/node": "^10.17.60",
         "@types/nodemailer": "^4.6.6",
-        "@types/passport": "^1.0.7",
+        "@types/passport": "^1.0.9",
         "@types/passport-google-oauth20": "^2.0.11",
         "@types/passport-local": "^1.0.34",
         "@types/pdfjs-dist": "^2.10.378",
@@ -65,12 +65,15 @@
         "@types/prosemirror-transform": "^1.1.5",
         "@types/prosemirror-view": "^1.23.1",
         "@types/rc-switch": "^1.9.2",
-        "@types/react": "^16.14.23",
+        "@types/react": "^18.0.15",
+        "@types/react-icons": "^3.0.0",
+        "@types/react-reconciler": "^0.26.4",
+        "@types/react-transition-group": "^4.4.5",
         "@types/react-autosuggest": "^9.3.14",
         "@types/react-color": "^2.17.6",
         "@types/react-datepicker": "^3.1.8",
-        "@types/react-dom": "^16.9.14",
-        "@types/react-grid-layout": "^0.17.2",
+        "@types/react-dom": "^18.0.6",
+        "@types/react-grid-layout": "^1.3.2",
         "@types/react-measure": "^2.0.8",
         "@types/react-select": "^3.1.2",
         "@types/react-table": "^6.8.9",
@@ -117,7 +120,7 @@
         "ts-node-dev": "^1.1.8",
         "tslint": "^5.20.1",
         "tslint-loader": "^3.6.0",
-        "typescript": "^4.6.2",
+        "typescript": "^4.7.4",
         "webpack": "^5.69.1",
         "webpack-dev-server": "^3.11.3",
         "webpack-hot-middleware": "^2.25.1"
@@ -129,7 +132,7 @@
         "@fortawesome/free-brands-svg-icons": "^5.15.4",
         "@fortawesome/free-regular-svg-icons": "^5.15.4",
         "@fortawesome/free-solid-svg-icons": "^5.15.4",
-        "@fortawesome/react-fontawesome": "^0.1.17",
+        "@fortawesome/react-fontawesome": "^0.1.19",
         "@hig/flyout": "^1.3.1",
         "@hig/theme-context": "^2.1.3",
         "@hig/theme-data": "^2.23.1",
@@ -145,14 +148,13 @@
         "@types/dom-speech-recognition": "0.0.1",
         "@types/formidable": "1.0.31",
         "@types/google-maps": "^3.2.3",
-        "@types/react-icons": "^3.0.0",
-        "@types/react-reconciler": "^0.26.4",
         "@types/reveal": "^3.3.33",
         "@types/supercluster": "^7.1.0",
         "@types/three": "^0.126.2",
         "@types/web": "0.0.53",
         "@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
         "@webscopeio/react-textarea-autocomplete": "^4.9.1",
+        "D": "^1.0.0",
         "adm-zip": "^0.4.16",
         "archiver": "^3.1.1",
         "array-batcher": "^1.2.3",
@@ -167,7 +169,7 @@
         "bootstrap": "^4.6.1",
         "browser-assert": "^1.2.1",
         "bson": "^4.6.1",
-        "canvas": "^2.9.0",
+        "canvas": "^2.9.3",
         "child_process": "^1.0.2",
         "chrome": "^0.1.0",
         "class-transformer": "^0.2.0",
@@ -178,7 +180,6 @@
         "cookie-parser": "^1.4.6",
         "cookie-session": "^2.0.0",
         "cors": "^2.8.5",
-        "D": "^1.0.0",
         "depcheck": "^0.9.2",
         "equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
         "exif": "^0.6.0",
@@ -250,11 +251,11 @@
         "prosemirror-history": "^1.2.0",
         "prosemirror-inputrules": "^1.1.3",
         "prosemirror-keymap": "^1.1.5",
-        "prosemirror-model": "^1.16.1",
+        "prosemirror-model": "^1.18.1",
         "prosemirror-schema-list": "^1.1.6",
-        "prosemirror-state": "^1.3.4",
+        "prosemirror-state": "^1.4.1",
         "prosemirror-transform": "^1.3.4",
-        "prosemirror-view": "^1.23.7",
+        "prosemirror-view": "^1.26.5",
         "pug": "^2.0.4",
         "puppeteer": "^3.3.0",
         "query-string": "^6.14.1",
@@ -268,18 +269,19 @@
         "react-compound-slider": "^2.5.0",
         "react-datepicker": "^3.8.0",
         "react-dom": "^16.14.0",
-        "react-grid-layout": "^0.18.3",
+        "react-grid-layout": "^1.3.4",
         "react-icons": "^4.3.1",
         "react-image-lightbox-with-rotate": "^5.1.1",
         "react-jsx-parser": "^1.29.0",
         "react-loading": "^2.0.3",
         "react-measure": "^2.5.2",
-        "react-refresh-typescript": "^2.0.3",
+        "react-refresh-typescript": "^2.0.7",
         "react-resizable": "^1.11.1",
         "react-resizable-rotatable-draggable": "^0.2.0",
         "react-reveal": "^1.2.2",
         "react-select": "^3.2.0",
         "react-table": "^6.11.5",
+        "react-transition-group": "^4.4.2",
         "readline": "^1.3.0",
         "request": "^2.88.2",
         "request-promise": "^4.2.6",
@@ -288,8 +290,8 @@
         "serializr": "^1.5.4",
         "sharp": "^0.23.4",
         "shelljs": "^0.8.5",
-        "socket.io": "^2.4.1",
-        "socket.io-client": "^2.4.0",
+        "socket.io": "^2.5.0",
+        "socket.io-client": "^2.5.0",
         "solr-node": "^1.2.1",
         "standard-http-error": "^2.0.1",
         "stream-browserify": "^3.0.0",
@@ -306,7 +308,7 @@
         "uuid": "^3.4.0",
         "valid-url": "^1.0.9",
         "web-request": "^1.0.7",
-        "webpack-cli": "^4.9.2",
+        "webpack-cli": "^4.10.0",
         "webpack-dev-middleware": "^5.3.1",
         "webrtc-adapter": "^7.7.1",
         "wikijs": "^6.3.3",
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index c247afa26..0b5957fac 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -1,17 +1,17 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { convertToObject } from "typescript";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { BoolCast, StrCast, Cast } from "../../fields/Types";
-import { addStyleSheet, addStyleSheetRule, Utils } from "../../Utils";
-import { LightboxView } from "../views/LightboxView";
-import { MainViewModal } from "../views/MainViewModal";
-import "./CaptureManager.scss";
-import { SelectionManager } from "./SelectionManager";
-import { undoBatch } from "./UndoManager";
-const higflyout = require("@hig/flyout");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { convertToObject } from 'typescript';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { BoolCast, StrCast, Cast } from '../../fields/Types';
+import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
+import { LightboxView } from '../views/LightboxView';
+import { MainViewModal } from '../views/MainViewModal';
+import './CaptureManager.scss';
+import { SelectionManager } from './SelectionManager';
+import { undoBatch } from './UndoManager';
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
@@ -22,32 +22,31 @@ export class CaptureManager extends React.Component<{}> {
     @observable _document: any;
     @observable isOpen: boolean = false; // whether the CaptureManager is to be displayed or not.
 
-
     constructor(props: {}) {
         super(props);
         CaptureManager.Instance = this;
     }
 
-    public close = action(() => this.isOpen = false);
+    public close = action(() => (this.isOpen = false));
     public open = action((doc: Doc) => {
         this.isOpen = true;
         this._document = doc;
     });
 
-
     @computed get visibilityContent() {
-
-        return <div className="capture-block">
-            <div className="capture-block-title">Visibility</div>
-            <div className="capture-block-radio">
-                <div className="radio-container">
-                    <input type="radio" value="private" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Private
-                </div>
-                <div className="radio-container">
-                    <input type="radio" value="public" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Public
+        return (
+            <div className="capture-block">
+                <div className="capture-block-title">Visibility</div>
+                <div className="capture-block-radio">
+                    <div className="radio-container">
+                        <input type="radio" value="private" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Private
+                    </div>
+                    <div className="radio-container">
+                        <input type="radio" value="public" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Public
+                    </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get linksContent() {
@@ -66,75 +65,79 @@ export class CaptureManager extends React.Component<{}> {
                     order.push(
                         <div className="list-item">
                             <div className="number">{i}</div>
-                            {(l.anchor1 as Doc).title}
+                            {StrCast((l.anchor1 as Doc).title)}
                         </div>
                     );
                 }
             });
         }
 
-        return <div className="capture-block">
-            <div className="capture-block-title">Links</div>
-            <div className="capture-block-list">
-                {order}
+        return (
+            <div className="capture-block">
+                <div className="capture-block-title">Links</div>
+                <div className="capture-block-list">{order}</div>
             </div>
-        </div>;
+        );
     }
 
     @computed get closeButtons() {
-        return <div className="capture-block">
-            <div className="buttons">
-                <div className="save" onClick={() => {
-                    LightboxView.SetLightboxDoc(this._document);
-                    this.close();
-                }}>
-                    Save
-                </div>
-                <div className="cancel" onClick={() => {
-                    const selected = SelectionManager.Views().slice();
-                    SelectionManager.DeselectAll();
-                    selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
-                    this.close();
-                }}>
-                    Cancel
+        return (
+            <div className="capture-block">
+                <div className="buttons">
+                    <div
+                        className="save"
+                        onClick={() => {
+                            LightboxView.SetLightboxDoc(this._document);
+                            this.close();
+                        }}>
+                        Save
+                    </div>
+                    <div
+                        className="cancel"
+                        onClick={() => {
+                            const selected = SelectionManager.Views().slice();
+                            SelectionManager.DeselectAll();
+                            selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
+                            this.close();
+                        }}>
+                        Cancel
+                    </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 
-
-
     private get captureInterface() {
-        return <div className="capture-interface">
-            <div className="capture-t1">
-                <div className="recordButtonOutline" style={{}}>
-                    <div className="recordButtonInner" style={{}}>
+        return (
+            <div className="capture-interface">
+                <div className="capture-t1">
+                    <div className="recordButtonOutline" style={{}}>
+                        <div className="recordButtonInner" style={{}}></div>
                     </div>
+                    Conversation Capture
                 </div>
-                Conversation Capture
-            </div>
-            <div className="capture-t2">
-
-            </div>
-            {this.visibilityContent}
-            {this.linksContent}
-            <div className="close-button" onClick={this.close}>
-                <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
+                <div className="capture-t2"></div>
+                {this.visibilityContent}
+                {this.linksContent}
+                <div className="close-button" onClick={this.close}>
+                    <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
+                </div>
+                {this.closeButtons}
             </div>
-            {this.closeButtons}
-        </div>;
-
+        );
     }
 
     render() {
-        return <MainViewModal
-            contents={this.captureInterface}
-            isDisplayed={this.isOpen}
-            interactive={true}
-            closeOnExternalClick={this.close}
-            dialogueBoxStyle={{ width: "500px", height: "350px", border: "none", background: "whitesmoke" }}
-            overlayStyle={{ background: "black" }}
-            overlayDisplayedOpacity={0.6}
-        />;
+        return (
+            <MainViewModal
+                contents={this.captureInterface}
+                isDisplayed={this.isOpen}
+                interactive={true}
+                closeOnExternalClick={this.close}
+                dialogueBoxStyle={{ width: '500px', height: '350px', border: 'none', background: 'whitesmoke' }}
+                overlayStyle={{ background: 'black' }}
+                overlayDisplayedOpacity={0.6}
+            />
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b2a5fddcd..84efcb966 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -957,7 +957,7 @@ export class CurrentUserUtils {
         }
         new LinkManager();
 
-        DocServer.UPDATE_SERVER_CACHE();
+        setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
         return doc;
     }
     static setupFieldInfos(doc:Doc, field="fieldInfos") {
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 62d656c5d..59334f6a2 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -1,19 +1,19 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import Select from 'react-select';
-import * as RequestPromise from "request-promise";
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
-import { StrCast, Cast } from "../../fields/Types";
-import { Utils } from "../../Utils";
-import { MainViewModal } from "../views/MainViewModal";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import "./GroupManager.scss";
-import { GroupMemberView } from "./GroupMemberView";
-import { SharingManager, User } from "./SharingManager";
-import { listSpec } from "../../fields/Schema";
-import { DateField } from "../../fields/DateField";
+import * as RequestPromise from 'request-promise';
+import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
+import { StrCast, Cast } from '../../fields/Types';
+import { Utils } from '../../Utils';
+import { MainViewModal } from '../views/MainViewModal';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import './GroupManager.scss';
+import { GroupMemberView } from './GroupMemberView';
+import { SharingManager, User } from './SharingManager';
+import { listSpec } from '../../fields/Schema';
+import { DateField } from '../../fields/DateField';
 
 /**
  * Interface for options for the react-select component
@@ -25,7 +25,6 @@ export interface UserOptions {
 
 @observer
 export class GroupManager extends React.Component<{}> {
-
     static Instance: GroupManager;
     @observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
     @observable private users: string[] = []; // list of users populated from the database.
@@ -34,24 +33,26 @@ export class GroupManager extends React.Component<{}> {
     @observable private createGroupModalOpen: boolean = false;
     private inputRef: React.RefObject<HTMLInputElement> = React.createRef(); // the ref for the input box.
     private createGroupButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // the ref for the group creation button
-    @observable private buttonColour: "#979797" | "black" = "#979797";
-    @observable private groupSort: "ascending" | "descending" | "none" = "none";
+    @observable private buttonColour: '#979797' | 'black' = '#979797';
+    @observable private groupSort: 'ascending' | 'descending' | 'none' = 'none';
 
     constructor(props: Readonly<{}>) {
         super(props);
         GroupManager.Instance = this;
     }
 
-    componentDidMount() { this.populateUsers(); }
+    componentDidMount() {
+        this.populateUsers();
+    }
 
     /**
      * Fetches the list of users stored on the database.
      */
     populateUsers = async () => {
-        const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
+        const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
         const raw = JSON.parse(userList) as User[];
         raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email)));
-    }
+    };
 
     /**
      * @returns the options to be rendered in the dropdown menu to add users and create a group.
@@ -68,11 +69,11 @@ export class GroupManager extends React.Component<{}> {
         // SelectionManager.DeselectAll();
         this.isOpen = true;
         this.populateUsers();
-    }
+    };
 
     /**
      * Hides the GroupManager.
-    */
+     */
     @action
     close = () => {
         this.isOpen = false;
@@ -81,26 +82,32 @@ export class GroupManager extends React.Component<{}> {
         // this.users = [];
         this.createGroupModalOpen = false;
         TaskCompletionBox.taskCompleted = false;
-    }
+    };
 
     /**
      * @returns the database of groups.
      */
-    @computed get GroupManagerDoc(): Doc | undefined { return Doc.UserDoc().globalGroupDatabase as Doc; }
+    @computed get GroupManagerDoc(): Doc | undefined {
+        return Doc.UserDoc().globalGroupDatabase as Doc;
+    }
 
     /**
      * @returns a list of all group documents.
      */
-    @computed get allGroups(): Doc[] { return DocListCast(this.GroupManagerDoc?.data); }
+    @computed get allGroups(): Doc[] {
+        return DocListCast(this.GroupManagerDoc?.data);
+    }
 
     /**
      * @returns the members of the admin group.
      */
-    @computed get adminGroupMembers(): string[] { return this.getGroup("Admin") ? JSON.parse(StrCast(this.getGroup("Admin")!.members)) : ""; }
+    @computed get adminGroupMembers(): string[] {
+        return this.getGroup('Admin') ? JSON.parse(StrCast(this.getGroup('Admin')!.members)) : '';
+    }
 
     /**
      * @returns a group document based on the group name.
-     * @param groupName 
+     * @param groupName
      */
     getGroup(groupName: string): Doc | undefined {
         return this.allGroups.find(group => group.title === groupName);
@@ -114,10 +121,9 @@ export class GroupManager extends React.Component<{}> {
         return JSON.parse(StrCast(this.getGroup(group)!.members)) as string[];
     }
 
-
     /**
      * @returns a boolean indicating whether the current user has access to edit group documents.
-     * @param groupDoc 
+     * @param groupDoc
      */
     hasEditAccess(groupDoc: Doc): boolean {
         if (!groupDoc) return false;
@@ -127,12 +133,12 @@ export class GroupManager extends React.Component<{}> {
 
     /**
      * Helper method that sets up the group document.
-     * @param groupName 
-     * @param memberEmails 
+     * @param groupName
+     * @param memberEmails
      */
     createGroupDoc(groupName: string, memberEmails: string[] = []) {
-        const name = groupName.toLowerCase() === "admin" ? "Admin" : groupName;
-        const groupDoc = new Doc("GROUP:" + name, true);
+        const name = groupName.toLowerCase() === 'admin' ? 'Admin' : groupName;
+        const groupDoc = new Doc('GROUP:' + name, true);
         groupDoc.title = name;
         groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]);
         groupDoc.members = JSON.stringify(memberEmails);
@@ -141,12 +147,12 @@ export class GroupManager extends React.Component<{}> {
 
     /**
      * Helper method that adds a group document to the database of group documents and @returns whether it was successfully added or not.
-     * @param groupDoc 
+     * @param groupDoc
      */
     addGroup(groupDoc: Doc): boolean {
         if (this.GroupManagerDoc) {
-            Doc.AddDocToList(this.GroupManagerDoc, "data", groupDoc);
-            this.GroupManagerDoc["data-lastModified"] = new DateField;
+            Doc.AddDocToList(this.GroupManagerDoc, 'data', groupDoc);
+            this.GroupManagerDoc['data-lastModified'] = new DateField();
             return true;
         }
         return false;
@@ -154,20 +160,20 @@ export class GroupManager extends React.Component<{}> {
 
     /**
      * Deletes a group from the database of group documents and @returns whether the group was deleted or not.
-     * @param group 
+     * @param group
      */
     @action
     deleteGroup(group: Doc): boolean {
         if (group) {
             if (this.GroupManagerDoc && this.hasEditAccess(group)) {
-                Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group);
+                Doc.RemoveDocFromList(this.GroupManagerDoc, 'data', group);
                 SharingManager.Instance.removeGroup(group);
                 const members = JSON.parse(StrCast(group.members));
                 if (members.includes(Doc.CurrentUserEmail)) {
                     const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group);
                     index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1);
                 }
-                this.GroupManagerDoc["data-lastModified"] = new DateField;
+                this.GroupManagerDoc['data-lastModified'] = new DateField();
                 if (group === this.currentGroup) {
                     this.currentGroup = undefined;
                 }
@@ -179,8 +185,8 @@ export class GroupManager extends React.Component<{}> {
 
     /**
      * Adds a member to a group.
-     * @param groupDoc 
-     * @param email 
+     * @param groupDoc
+     * @param email
      */
     addMemberToGroup(groupDoc: Doc, email: string) {
         if (this.hasEditAccess(groupDoc)) {
@@ -188,14 +194,14 @@ export class GroupManager extends React.Component<{}> {
             !memberList.includes(email) && memberList.push(email);
             groupDoc.members = JSON.stringify(memberList);
             SharingManager.Instance.shareWithAddedMember(groupDoc, email);
-            this.GroupManagerDoc && (this.GroupManagerDoc["data-lastModified"] = new DateField);
+            this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
         }
     }
 
     /**
      * Removes a member from the group.
-     * @param groupDoc 
-     * @param email 
+     * @param groupDoc
+     * @param email
      */
     removeMemberFromGroup(groupDoc: Doc, email: string) {
         if (this.hasEditAccess(groupDoc)) {
@@ -205,27 +211,27 @@ export class GroupManager extends React.Component<{}> {
                 const user = memberList.splice(index, 1)[0];
                 groupDoc.members = JSON.stringify(memberList);
                 SharingManager.Instance.removeMember(groupDoc, email);
-                this.GroupManagerDoc && (this.GroupManagerDoc["data-lastModified"] = new DateField);
+                this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
             }
         }
     }
 
     /**
      * Handles changes in the users selected in the "Select users" dropdown.
-     * @param selectedOptions 
+     * @param selectedOptions
      */
     @action
     handleChange = (selectedOptions: any) => {
         this.selectedUsers = selectedOptions as UserOptions[];
-    }
+    };
 
     /**
      * Creates the group when the enter key has been pressed (when in the input).
-     * @param e 
+     * @param e
      */
     handleKeyDown = (e: React.KeyboardEvent) => {
-        e.key === "Enter" && this.createGroup();
-    }
+        e.key === 'Enter' && this.createGroup();
+    };
 
     /**
      * Handles the input of required fields in the setup of a group and resets the relevant variables.
@@ -234,32 +240,37 @@ export class GroupManager extends React.Component<{}> {
     createGroup = () => {
         const { value } = this.inputRef.current!;
         if (!value) {
-            alert("Please enter a group name");
+            alert('Please enter a group name');
             return;
         }
-        if (["admin", "public", "override"].includes(value.toLowerCase())) {
-            if (value.toLowerCase() !== "admin" || (value.toLowerCase() === "admin" && this.getGroup("Admin"))) {
+        if (['admin', 'public', 'override'].includes(value.toLowerCase())) {
+            if (value.toLowerCase() !== 'admin' || (value.toLowerCase() === 'admin' && this.getGroup('Admin'))) {
                 alert(`You cannot override the ${value.charAt(0).toUpperCase() + value.slice(1)} group`);
                 return;
             }
         }
         if (this.getGroup(value)) {
-            alert("Please select a unique group name");
+            alert('Please select a unique group name');
             return;
         }
-        this.createGroupDoc(value, this.selectedUsers?.map(user => user.value));
+        this.createGroupDoc(
+            value,
+            this.selectedUsers?.map(user => user.value)
+        );
         this.selectedUsers = null;
-        this.inputRef.current!.value = "";
-        this.buttonColour = "#979797";
+        this.inputRef.current!.value = '';
+        this.buttonColour = '#979797';
 
         const { left, width, top } = this.createGroupButtonRef.current!.getBoundingClientRect();
         TaskCompletionBox.popupX = left - 2 * width;
         TaskCompletionBox.popupY = top;
-        TaskCompletionBox.textDisplayed = "Group created!";
+        TaskCompletionBox.textDisplayed = 'Group created!';
         TaskCompletionBox.taskCompleted = true;
-        setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
-
-    }
+        setTimeout(
+            action(() => (TaskCompletionBox.taskCompleted = false)),
+            2000
+        );
+    };
 
     /**
      * @returns the MainViewModal which allows the user to create groups.
@@ -268,50 +279,43 @@ export class GroupManager extends React.Component<{}> {
         const contents = (
             <div className="group-create">
                 <div className="group-heading" style={{ marginBottom: 0 }}>
-                    <p><b>New Group</b></p>
-                    <div className={"close-button"} onClick={action(() => {
-                        this.createGroupModalOpen = false; TaskCompletionBox.taskCompleted = false;
-                    })}>
-                        <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
+                    <p>
+                        <b>New Group</b>
+                    </p>
+                    <div
+                        className={'close-button'}
+                        onClick={action(() => {
+                            this.createGroupModalOpen = false;
+                            TaskCompletionBox.taskCompleted = false;
+                        })}>
+                        <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
                 </div>
-                <input
-                    className="group-input"
-                    ref={this.inputRef}
-                    onKeyDown={this.handleKeyDown}
-                    autoFocus
-                    type="text"
-                    placeholder="Group name"
-                    onChange={action(() => this.buttonColour = this.inputRef.current?.value ? "black" : "#979797")} />
+                <input className="group-input" ref={this.inputRef} onKeyDown={this.handleKeyDown} autoFocus type="text" placeholder="Group name" onChange={action(() => (this.buttonColour = this.inputRef.current?.value ? 'black' : '#979797'))} />
                 <Select
                     isMulti
                     options={this.options}
                     onChange={this.handleChange}
-                    placeholder={"Select users"}
+                    placeholder={'Select users'}
                     value={this.selectedUsers}
                     closeMenuOnSelect={false}
                     styles={{
                         dropdownIndicator: (base, state) => ({
                             ...base,
                             transition: '0.5s all ease',
-                            transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined
+                            transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined,
                         }),
-                        multiValue: (base) => ({
+                        multiValue: base => ({
                             ...base,
-                            maxWidth: "50%",
+                            maxWidth: '50%',
 
                             '&:hover': {
-                                maxWidth: "unset"
-                            }
-                        })
+                                maxWidth: 'unset',
+                            },
+                        }),
                     }}
                 />
-                <button
-                    ref={this.createGroupButtonRef}
-                    onClick={this.createGroup}
-                    style={{ background: this.buttonColour }}
-                    disabled={this.buttonColour === "#979797"}
-                >
+                <button ref={this.createGroupButtonRef} onClick={this.createGroup} style={{ background: this.buttonColour }} disabled={this.buttonColour === '#979797'}>
                     Create
                 </button>
             </div>
@@ -322,8 +326,12 @@ export class GroupManager extends React.Component<{}> {
                 isDisplayed={this.createGroupModalOpen}
                 interactive={true}
                 contents={contents}
-                dialogueBoxStyle={{ width: "90%", height: "70%" }}
-                closeOnExternalClick={action(() => { this.createGroupModalOpen = false; this.selectedUsers = null; TaskCompletionBox.taskCompleted = false; })}
+                dialogueBoxStyle={{ width: '90%', height: '70%' }}
+                closeOnExternalClick={action(() => {
+                    this.createGroupModalOpen = false;
+                    this.selectedUsers = null;
+                    TaskCompletionBox.taskCompleted = false;
+                })}
             />
         );
     }
@@ -332,7 +340,6 @@ export class GroupManager extends React.Component<{}> {
      * A getter that @returns the main interface for the GroupManager.
      */
     private get groupInterface() {
-
         const sortGroups = (d1: Doc, d2: Doc) => {
             const g1 = StrCast(d1.title);
             const g2 = StrCast(d2.title);
@@ -340,62 +347,50 @@ export class GroupManager extends React.Component<{}> {
             return g1 < g2 ? -1 : g1 === g2 ? 0 : 1;
         };
 
-        const groups = this.groupSort === "ascending" ? this.allGroups.sort(sortGroups) : this.groupSort === "descending" ? this.allGroups.sort(sortGroups).reverse() : this.allGroups;
+        const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups;
 
         return (
             <div className="group-interface">
                 {this.groupCreationModal}
-                {this.currentGroup ?
-                    <GroupMemberView
-                        group={this.currentGroup}
-                        onCloseButtonClick={action(() => this.currentGroup = undefined)}
-                    />
-                    : null}
+                {this.currentGroup ? <GroupMemberView group={this.currentGroup} onCloseButtonClick={action(() => (this.currentGroup = undefined))} /> : null}
                 <div className="group-heading">
-                    <p><b>Manage Groups</b></p>
-                    <button onClick={action(() => this.createGroupModalOpen = true)}>
-                        <FontAwesomeIcon icon={"plus"} size={"sm"} /> Create Group
+                    <p>
+                        <b>Manage Groups</b>
+                    </p>
+                    <button onClick={action(() => (this.createGroupModalOpen = true))}>
+                        <FontAwesomeIcon icon={'plus'} size={'sm'} /> Create Group
                     </button>
-                    <div className={"close-button"} onClick={this.close}>
-                        <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
+                    <div className={'close-button'} onClick={this.close}>
+                        <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
                 </div>
                 <div className="main-container">
-                    <div
-                        className="sort-groups"
-                        onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}>
-                        Name {this.groupSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
-                            : this.groupSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
-                                : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />
-                        }
+                    <div className="sort-groups" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
+                        Name{' '}
+                        {this.groupSort === 'ascending' ? (
+                            <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
+                        ) : this.groupSort === 'descending' ? (
+                            <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
+                        ) : (
+                            <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
+                        )}
                     </div>
                     <div className="group-body">
-                        {groups.map(group =>
-                            <div
-                                className="group-row"
-                                key={StrCast(group.title || group.groupName)}
-                            >
-                                <div className="group-name" >{group.title || group.groupName}</div>
-                                <div className="group-info" onClick={action(() => this.currentGroup = group)}>
-                                    <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
+                        {groups.map(group => (
+                            <div className="group-row" key={StrCast(group.title || group.groupName)}>
+                                <div className="group-name">{StrCast(group.title || group.groupName)}</div>
+                                <div className="group-info" onClick={action(() => (this.currentGroup = group))}>
+                                    <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
                                 </div>
                             </div>
-                        )}
+                        ))}
                     </div>
                 </div>
-
             </div>
         );
     }
 
     render() {
-        return <MainViewModal
-            contents={this.groupInterface}
-            isDisplayed={this.isOpen}
-            interactive={true}
-            dialogueBoxStyle={{ zIndex: 1002 }}
-            overlayStyle={{ zIndex: 1001 }}
-            closeOnExternalClick={this.close}
-        />;
+        return <MainViewModal contents={this.groupInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxStyle={{ zIndex: 1002 }} overlayStyle={{ zIndex: 1001 }} closeOnExternalClick={this.close} />;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx
index a209874a3..d4620ae3f 100644
--- a/src/client/util/ScrollBox.tsx
+++ b/src/client/util/ScrollBox.tsx
@@ -1,21 +1,24 @@
-import React = require("react");
+import React = require('react');
 
-export class ScrollBox extends React.Component {
+export class ScrollBox extends React.Component<React.PropsWithChildren<{}>> {
     onWheel = (e: React.WheelEvent) => {
-        if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight) { // If the element has a scroll bar, then we don't want the containing collection to zoom
+        if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
+            // If the element has a scroll bar, then we don't want the containing collection to zoom
             e.stopPropagation();
         }
-    }
+    };
 
     render() {
         return (
-            <div style={{
-                overflow: "auto",
-                width: "100%",
-                height: "100%",
-            }} onWheel={this.onWheel}>
+            <div
+                style={{
+                    overflow: 'auto',
+                    width: '100%',
+                    height: '100%',
+                }}
+                onWheel={this.onWheel}>
                 {this.props.children}
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d77633b8d..1acb3ab38 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,29 +1,29 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { intersection } from "lodash";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import Select from "react-select";
-import * as RequestPromise from "request-promise";
-import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util";
-import { Utils } from "../../Utils";
-import { DocServer } from "../DocServer";
-import { CollectionView } from "../views/collections/CollectionView";
-import { DictationOverlay } from "../views/DictationOverlay";
-import { MainViewModal } from "../views/MainViewModal";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import { SearchBox } from "../views/search/SearchBox";
-import { CurrentUserUtils } from "./CurrentUserUtils";
-import { DocumentManager } from "./DocumentManager";
-import { GroupManager, UserOptions } from "./GroupManager";
-import { GroupMemberView } from "./GroupMemberView";
-import { SelectionManager } from "./SelectionManager";
-import "./SharingManager.scss";
-import { LinkManager } from "./LinkManager";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { intersection } from 'lodash';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import Select from 'react-select';
+import * as RequestPromise from 'request-promise';
+import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
+import { Utils } from '../../Utils';
+import { DocServer } from '../DocServer';
+import { CollectionView } from '../views/collections/CollectionView';
+import { DictationOverlay } from '../views/DictationOverlay';
+import { MainViewModal } from '../views/MainViewModal';
+import { DocumentView } from '../views/nodes/DocumentView';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import { SearchBox } from '../views/search/SearchBox';
+import { CurrentUserUtils } from './CurrentUserUtils';
+import { DocumentManager } from './DocumentManager';
+import { GroupManager, UserOptions } from './GroupManager';
+import { GroupMemberView } from './GroupMemberView';
+import { SelectionManager } from './SelectionManager';
+import './SharingManager.scss';
+import { LinkManager } from './LinkManager';
 
 export interface User {
     email: string;
@@ -44,19 +44,19 @@ interface GroupedOptions {
 // const DefaultColor = "black";
 
 // used to differentiate between individuals and groups when sharing
-const indType = "!indType/";
-const groupType = "!groupType/";
+const indType = '!indType/';
+const groupType = '!groupType/';
 
-const storage = "data";
+const storage = 'data';
 
 /**
  * A user who also has a sharing doc.
  */
 interface ValidatedUser {
-    user: User;         // database minimal info to identify / communicate with a user (email, sharing doc id)
-    sharingDoc: Doc;    // document to share/message another user
+    user: User; // database minimal info to identify / communicate with a user (email, sharing doc id)
+    sharingDoc: Doc; // document to share/message another user
     linkDatabase: Doc;
-    userColor: string;  // stored on the sharinDoc, extracted for convenience?
+    userColor: string; // stored on the sharinDoc, extracted for convenience?
 }
 
 @observer
@@ -71,8 +71,8 @@ export class SharingManager extends React.Component<{}> {
     @observable private overlayOpacity = 0.4; // for the modal
     @observable private selectedUsers: UserOptions[] | null = null; // users (individuals/groups) selected to share with
     @observable private permissions: SharingPermissions = SharingPermissions.Edit; // the permission with which to share with other users
-    @observable private individualSort: "ascending" | "descending" | "none" = "none"; // sorting options for the list of individuals
-    @observable private groupSort: "ascending" | "descending" | "none" = "none"; // sorting options for the list of groups
+    @observable private individualSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of individuals
+    @observable private groupSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of groups
     private shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the share button, used for the position of the popup
     private distributeAclsButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the distribute button, used for the position of the popup
     // if both showUserOptions and showGroupOptions are false then both are displayed
@@ -89,7 +89,7 @@ export class SharingManager extends React.Component<{}> {
         [AclAugment, SharingPermissions.Augment],
         [AclSelfEdit, SharingPermissions.SelfEdit],
         [AclEdit, SharingPermissions.Edit],
-        [AclAdmin, SharingPermissions.Admin]
+        [AclAdmin, SharingPermissions.Admin],
     ]);
 
     // private get linkVisible() {
@@ -105,17 +105,20 @@ export class SharingManager extends React.Component<{}> {
             this.isOpen = this.targetDoc !== undefined;
             this.permissions = SharingPermissions.Augment;
         });
-    }
+    };
 
     public close = action(() => {
         this.isOpen = false;
         this.selectedUsers = null; // resets the list of users and selected users (in the react-select component)
         TaskCompletionBox.taskCompleted = false;
-        setTimeout(action(() => {
-            // this.copied = false;
-            DictationOverlay.Instance.hasActiveModal = false;
-            this.targetDoc = undefined;
-        }), 500);
+        setTimeout(
+            action(() => {
+                // this.copied = false;
+                DictationOverlay.Instance.hasActiveModal = false;
+                this.targetDoc = undefined;
+            }),
+            500
+        );
     });
 
     constructor(props: {}) {
@@ -136,7 +139,7 @@ export class SharingManager extends React.Component<{}> {
     populateUsers = async () => {
         if (!this.populating) {
             this.populating = true;
-            const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
+            const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
             const raw = JSON.parse(userList) as User[];
             const sharingDocs: ValidatedUser[] = [];
             const evaluating = raw.map(async user => {
@@ -146,7 +149,8 @@ export class SharingManager extends React.Component<{}> {
                     const linkDatabase = await DocServer.GetRefField(user.linkDatabaseId);
                     if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
                         await DocListCastAsync(linkDatabase.data);
-                        (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => { // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
+                        (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => {
+                            // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
                             const a1 = await Cast(link?.anchor1, Doc, null);
                             const a2 = await Cast(link?.anchor2, Doc, null);
                         });
@@ -166,7 +170,7 @@ export class SharingManager extends React.Component<{}> {
                 this.populating = false;
             });
         }
-    }
+    };
 
     /**
      * Shares the document with a user.
@@ -179,31 +183,31 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-        return !docs.map(doc => {
-            doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
-            if (permission === SharingPermissions.None) {
-                if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
-            }
-            else {
-                if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
-            }
+        return !docs
+            .map(doc => {
+                doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
+
+                if (permission === SharingPermissions.None) {
+                    if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
+                } else {
+                    if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
+                }
 
-            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
+                distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
 
-            this.setDashboardBackground(doc, permission as SharingPermissions);
-            if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
-            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc));
-        }).some(success => !success);
-    }
+                this.setDashboardBackground(doc, permission as SharingPermissions);
+                if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
+                else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
+            })
+            .some(success => !success);
+    };
 
     /**
      * Sets the permission on the target for the group.
-     * @param group 
-     * @param permission 
+     * @param group
+     * @param permission
      */
     setInternalGroupSharing = (group: Doc | { title: string }, permission: string, targetDoc?: Doc) => {
-
         const target = targetDoc || this.targetDoc!;
         const key = normalizeEmail(StrCast(group.title));
         const acl = `acl-${key}`;
@@ -212,38 +216,41 @@ export class SharingManager extends React.Component<{}> {
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
         // ! ensures it returns true if document has been shared successfully, false otherwise
-        return !docs.map(doc => {
-            doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
-            if (permission === SharingPermissions.None) {
-                if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1;
-            }
-            else {
-                if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1;
-            }
+        return !docs
+            .map(doc => {
+                doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
+
+                if (permission === SharingPermissions.None) {
+                    if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1;
+                } else {
+                    if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1;
+                }
 
-            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-            this.setDashboardBackground(doc, permission as SharingPermissions);
+                distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
+                this.setDashboardBackground(doc, permission as SharingPermissions);
 
-            if (group instanceof Doc) {
-                const members: string[] = JSON.parse(StrCast(group.members));
-                const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
+                if (group instanceof Doc) {
+                    const members: string[] = JSON.parse(StrCast(group.members));
+                    const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
 
-                // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
-                group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : group.docsShared = new List<Doc>([doc]);
+                    // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
+                    group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : (group.docsShared = new List<Doc>([doc]));
 
-                return users.map(({ user, sharingDoc }) => {
-                    if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
-                    else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists
-                }).some(success => !success);
-            }
-        }).some(success => success);
-    }
+                    return users
+                        .map(({ user, sharingDoc }) => {
+                            if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
+                            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc); // remove the doc from the list if it already exists
+                        })
+                        .some(success => !success);
+                }
+            })
+            .some(success => success);
+    };
 
     /**
      * Shares the documents shared with a group with a new user who has been added to that group.
-     * @param group 
-     * @param emailId 
+     * @param group
+     * @param emailId
      */
     shareWithAddedMember = (group: Doc, emailId: string, retry: boolean = true) => {
         const user = this.users.find(({ user: { email } }) => email === emailId)!;
@@ -255,30 +262,30 @@ export class SharingManager extends React.Component<{}> {
                     DocListCastAsync(group.docsShared).then(dl => {
                         const filtered = dl?.filter(doc => !userdocs?.includes(doc));
                         filtered && userdocs?.push(...filtered);
-                    }));
+                    })
+                );
             }
         }
-    }
+    };
 
     /**
      * Called from the properties sidebar to change permissions of a user.
      */
     shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => {
-        if (shareWith !== "Public" && shareWith !== "Override") {
-            const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
+        if (shareWith !== 'Public' && shareWith !== 'Override') {
+            const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith));
             docs.forEach(doc => {
                 if (user) this.setInternalSharing(user, permission, doc);
                 else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
             });
-        }
-        else {
+        } else {
             const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
                 if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
             });
         }
-    }
+    };
 
     /**
      * Sets the background of the Dashboard if it has been shared as a visual indicator
@@ -287,22 +294,21 @@ export class SharingManager extends React.Component<{}> {
         if (Doc.IndexOf(doc, DocListCast(CurrentUserUtils.MyDashboards.data)) !== -1) {
             if (permission !== SharingPermissions.None) {
                 doc.isShared = true;
-                doc.backgroundColor = "green";
-            }
-            else {
+                doc.backgroundColor = 'green';
+            } else {
                 const acls = doc[DataSym][AclSym];
-                if (Object.keys(acls).every(key => key === `acl-${Doc.CurrentUserEmailNormalized}` ? true : [AclUnset, AclPrivate].includes(acls[key]))) {
+                if (Object.keys(acls).every(key => (key === `acl-${Doc.CurrentUserEmailNormalized}` ? true : [AclUnset, AclPrivate].includes(acls[key])))) {
                     doc.isShared = undefined;
                     doc.backgroundColor = undefined;
                 }
             }
         }
-    }
+    };
 
     /**
      * Removes the documents shared with a user through a group when the user is removed from the group.
-     * @param group 
-     * @param emailId 
+     * @param group
+     * @param emailId
      */
     removeMember = (group: Doc, emailId: string) => {
         const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
@@ -315,11 +321,11 @@ export class SharingManager extends React.Component<{}> {
                 })
             );
         }
-    }
+    };
 
     /**
      * Removes a group's permissions from documents that have been shared with it.
-     * @param group 
+     * @param group
      */
     removeGroup = (group: Doc) => {
         if (group.docsShared) {
@@ -335,9 +341,7 @@ export class SharingManager extends React.Component<{}> {
                 users.forEach(({ sharingDoc }) => Doc.RemoveDocFromList(sharingDoc, storage, doc));
             });
         }
-    }
-
-
+    };
 
     // private setExternalSharing = (permission: string) => {
     //     const targetDoc = this.targetDoc;
@@ -367,23 +371,23 @@ export class SharingManager extends React.Component<{}> {
      */
     private sharingOptions(uniform: boolean, override?: boolean) {
         const dropdownValues: string[] = Object.values(SharingPermissions);
-        if (!uniform) dropdownValues.unshift("-multiple-");
-        if (override) dropdownValues.unshift("None");
-        return dropdownValues.filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
-        (
-            <option key={permission} value={permission}>
-                {permission}
-            </option>
-        )
-        );
+        if (!uniform) dropdownValues.unshift('-multiple-');
+        if (override) dropdownValues.unshift('None');
+        return dropdownValues
+            .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
+            .map(permission => (
+                <option key={permission} value={permission}>
+                    {permission}
+                </option>
+            ));
     }
 
     private focusOn = (contents: string) => {
-        const title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
+        const title = this.targetDoc ? StrCast(this.targetDoc.title) : '';
         const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc];
         return (
             <span
-                className={"focus-span"}
+                className={'focus-span'}
                 title={title}
                 onClick={() => {
                     let context: Opt<CollectionView>;
@@ -404,12 +408,11 @@ export class SharingManager extends React.Component<{}> {
                         this.dialogueBoxOpacity = 1;
                         this.overlayOpacity = 0.4;
                     }
-                })}
-            >
+                })}>
                 {contents}
             </span>
         );
-    }
+    };
 
     /**
      * Handles changes in the users selected in react-select
@@ -417,7 +420,7 @@ export class SharingManager extends React.Component<{}> {
     @action
     handleUsersChange = (selectedOptions: any) => {
         this.selectedUsers = selectedOptions as UserOptions[];
-    }
+    };
 
     /**
      * Handles changes in the permission chosen to share with someone with
@@ -425,7 +428,7 @@ export class SharingManager extends React.Component<{}> {
     @action
     handlePermissionsChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
         this.permissions = event.currentTarget.value as SharingPermissions;
-    }
+    };
 
     /**
      * Calls the relevant method for sharing, displays the popup, and resets the relevant variables.
@@ -436,8 +439,7 @@ export class SharingManager extends React.Component<{}> {
             this.selectedUsers.forEach(user => {
                 if (user.value.includes(indType)) {
                     this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
-                }
-                else {
+                } else {
                     this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
                 }
             });
@@ -445,13 +447,16 @@ export class SharingManager extends React.Component<{}> {
             const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect();
             TaskCompletionBox.popupX = left - 1.5 * width;
             TaskCompletionBox.popupY = top - 1.5 * height;
-            TaskCompletionBox.textDisplayed = "Document shared!";
+            TaskCompletionBox.textDisplayed = 'Document shared!';
             TaskCompletionBox.taskCompleted = true;
-            setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
+            setTimeout(
+                action(() => (TaskCompletionBox.taskCompleted = false)),
+                2000
+            );
 
             this.selectedUsers = null;
         }
-    }
+    };
 
     // distributeOverCollection = (targetDoc?: Doc) => {
     //     const target = targetDoc || this.targetDoc!;
@@ -471,7 +476,7 @@ export class SharingManager extends React.Component<{}> {
         const { email: e1 } = u1.user;
         const { email: e2 } = u2.user;
         return e1 < e2 ? -1 : e1 === e2 ? 0 : 1;
-    }
+    };
 
     /**
      * Sorting algorithm to sort groups.
@@ -480,7 +485,7 @@ export class SharingManager extends React.Component<{}> {
         const g1 = StrCast(group1.title);
         const g2 = StrCast(group2.title);
         return g1 < g2 ? -1 : g1 === g2 ? 0 : 1;
-    }
+    };
 
     /**
      * @returns the main interface of the SharingManager.
@@ -488,28 +493,29 @@ export class SharingManager extends React.Component<{}> {
     @computed get sharingInterface() {
         TraceMobx();
         const groupList = GroupManager.Instance?.allGroups || [];
-        const sortedUsers = this.users.slice().sort(this.sortUsers).map(({ user: { email } }) => ({ label: email, value: indType + email }));
-        const sortedGroups = groupList.slice().sort(this.sortGroups).map(({ title }) => ({ label: StrCast(title), value: groupType + StrCast(title) }));
+        const sortedUsers = this.users
+            .slice()
+            .sort(this.sortUsers)
+            .map(({ user: { email } }) => ({ label: email, value: indType + email }));
+        const sortedGroups = groupList
+            .slice()
+            .sort(this.sortGroups)
+            .map(({ title }) => ({ label: StrCast(title), value: groupType + StrCast(title) }));
 
         // the next block handles the users shown (individuals/groups/both)
         const options: GroupedOptions[] = [];
         if (GroupManager.Instance) {
             if ((this.showUserOptions && this.showGroupOptions) || (!this.showUserOptions && !this.showGroupOptions)) {
-                options.push(
-                    { label: 'Individuals', options: sortedUsers },
-                    { label: 'Groups', options: sortedGroups });
-            }
-            else if (this.showUserOptions) options.push({ label: 'Individuals', options: sortedUsers });
+                options.push({ label: 'Individuals', options: sortedUsers }, { label: 'Groups', options: sortedGroups });
+            } else if (this.showUserOptions) options.push({ label: 'Individuals', options: sortedUsers });
             else options.push({ label: 'Groups', options: sortedGroups });
         }
 
-        const users = this.individualSort === "ascending" ? this.users.slice().sort(this.sortUsers) : this.individualSort === "descending" ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
-        const groups = this.groupSort === "ascending" ? groupList.slice().sort(this.sortGroups) : this.groupSort === "descending" ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
+        const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
+        const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
 
         // handles the case where multiple documents are selected
-        let docs = SelectionManager.Views().length < 2 ?
-            [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]]
-            : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]);
+        let docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]));
 
         if (this.myDocAcls) {
             const newDocs: Doc[] = [];
@@ -524,107 +530,78 @@ export class SharingManager extends React.Component<{}> {
         const admin = this.myDocAcls ? Boolean(docs.length) : effectiveAcls.every(acl => acl === AclAdmin);
 
         // users in common between all docs
-        const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym])));
+        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym]))));
 
         // the list of users shared with
-        const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email).map(({ user, linkDatabase, sharingDoc, userColor }) => {
-            const userKey = `acl-${normalizeEmail(user.email)}`;
-            const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]);
-            const permissions = uniform ? StrCast(targetDoc?.[userKey]) : "-multiple-";
-
-            return !permissions ? (null) : (
-                <div
-                    key={userKey}
-                    className={"container"}
-                >
-                    <span className={"padding"}>{user.email}</span>
-                    <div className="edit-actions">
-                        {admin || this.myDocAcls ? (
-                            <select
-                                className={"permissions-dropdown"}
-                                value={permissions}
-                                onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value)}
-                            >
-                                {this.sharingOptions(uniform)}
-                            </select>
-                        ) : (
-                            <div className={"permissions-dropdown"}>
-                                {permissions}
-                            </div>
-                        )}
+        const userListContents: (JSX.Element | null)[] = users
+            .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+            .map(({ user, linkDatabase, sharingDoc, userColor }) => {
+                const userKey = `acl-${normalizeEmail(user.email)}`;
+                const uniform = docs.every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]));
+                const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+
+                return !permissions ? null : (
+                    <div key={userKey} className={'container'}>
+                        <span className={'padding'}>{user.email}</span>
+                        <div className="edit-actions">
+                            {admin || this.myDocAcls ? (
+                                <select className={'permissions-dropdown'} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value)}>
+                                    {this.sharingOptions(uniform)}
+                                </select>
+                            ) : (
+                                <div className={'permissions-dropdown'}>{permissions}</div>
+                            )}
+                        </div>
                     </div>
-                </div>
-            );
-        });
+                );
+            });
 
         // checks if every doc has the same author
         const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author);
 
         // the owner of the doc and the current user are placed at the top of the user list.
         userListContents.unshift(
-            sameAuthor ?
-                (
-                    <div
-                        key={"owner"}
-                        className={"container"}
-                    >
-                        <span className={"padding"}>{targetDoc?.author === Doc.CurrentUserEmail ? "Me" : targetDoc?.author}</span>
-                        <div className="edit-actions">
-                            <div className={"permissions-dropdown"}>
-                                Owner
-                            </div>
-                        </div>
+            sameAuthor ? (
+                <div key={'owner'} className={'container'}>
+                    <span className={'padding'}>{targetDoc?.author === Doc.CurrentUserEmail ? 'Me' : targetDoc?.author}</span>
+                    <div className="edit-actions">
+                        <div className={'permissions-dropdown'}>Owner</div>
                     </div>
-                ) : null,
-            sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail ?
-                (
-                    <div
-                        key={"me"}
-                        className={"container"}
-                    >
-                        <span className={"padding"}>Me</span>
-                        <div className="edit-actions">
-                            <div className={"permissions-dropdown"}>
-                                {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? this.AclMap.get(effectiveAcls[0])! : "-multiple-"}
-                            </div>
-                        </div>
+                </div>
+            ) : null,
+            sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail ? (
+                <div key={'me'} className={'container'}>
+                    <span className={'padding'}>Me</span>
+                    <div className="edit-actions">
+                        <div className={'permissions-dropdown'}>{effectiveAcls.every(acl => acl === effectiveAcls[0]) ? this.AclMap.get(effectiveAcls[0])! : '-multiple-'}</div>
                     </div>
-                ) : null
+                </div>
+            ) : null
         );
 
-
         // the list of groups shared with
-        const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true);
-        groupListMap.unshift({ title: "Public" });//, { title: "ALL" });
+        const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
+        groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
         const groupListContents = groupListMap.map(group => {
             const groupKey = `acl-${StrCast(group.title)}`;
-            const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]);
-            const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : "-multiple-";
-
-            return !permissions ? (null) : (
-                <div
-                    key={groupKey}
-                    className={"container"}
-                >
-                    <div className={"padding"}>{group.title}</div>
-                    {group instanceof Doc ?
-                        (<div className="group-info" onClick={action(() => GroupManager.Instance.currentGroup = group)}>
-                            <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
-                        </div>)
-                        : (null)}
+            const uniform = docs.every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
+            const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
+
+            return !permissions ? null : (
+                <div key={groupKey} className={'container'}>
+                    <div className={'padding'}>{StrCast(group.title)}</div>
+                    {group instanceof Doc ? (
+                        <div className="group-info" onClick={action(() => (GroupManager.Instance.currentGroup = group))}>
+                            <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
+                        </div>
+                    ) : null}
                     <div className="edit-actions">
                         {admin || this.myDocAcls ? (
-                            <select
-                                className={"permissions-dropdown"}
-                                value={permissions}
-                                onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}
-                            >
-                                {this.sharingOptions(uniform, group.title === "Override")}
+                            <select className={'permissions-dropdown'} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}>
+                                {this.sharingOptions(uniform, group.title === 'Override')}
                             </select>
                         ) : (
-                            <div className={"permissions-dropdown"}>
-                                {permissions}
-                            </div>
+                            <div className={'permissions-dropdown'}>{permissions}</div>
                         )}
                     </div>
                 </div>
@@ -632,102 +609,95 @@ export class SharingManager extends React.Component<{}> {
         });
 
         return (
-            <div className={"sharing-interface"}>
-                {GroupManager.Instance?.currentGroup ?
-                    <GroupMemberView
-                        group={GroupManager.Instance.currentGroup}
-                        onCloseButtonClick={action(() => GroupManager.Instance.currentGroup = undefined)}
-                    /> :
-                    null}
+            <div className={'sharing-interface'}>
+                {GroupManager.Instance?.currentGroup ? <GroupMemberView group={GroupManager.Instance.currentGroup} onCloseButtonClick={action(() => (GroupManager.Instance.currentGroup = undefined))} /> : null}
                 <div className="sharing-contents">
-                    <p className={"share-title"}><b>Share </b>{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, "this document") : "-multiple-")}</p>
-                    <div className={"close-button"} onClick={this.close}>
-                        <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
+                    <p className={'share-title'}>
+                        <b>Share </b>
+                        {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}
+                    </p>
+                    <div className={'close-button'} onClick={this.close}>
+                        <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
                     {/* {this.linkVisible ?
                         <div>
                             {this.sharingUrl}
                         </div> :
                         (null)} */}
-                    {<div className="share-container">
-                        <div className="share-setup">
-                            <Select
-                                className="user-search"
-                                placeholder="Enter user or group name..."
-                                isMulti
-                                isSearchable
-                                closeMenuOnSelect={false}
-                                options={options}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={this.handleUsersChange}
-                                value={this.selectedUsers}
-                                styles={{
-                                    indicatorSeparator: () => ({
-                                        visibility: "hidden"
-                                    })
-                                }}
-                            />
-                            <select className="permissions-select" onChange={this.handlePermissionsChange} value={this.permissions}>
-                                {this.sharingOptions(true)}
-                            </select>
-                            <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
-                                Share
-                            </button>
-                        </div>
-                        <div className="sort-checkboxes">
-                            <input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label>
-                            <input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label>
-                        </div>
+                    {
+                        <div className="share-container">
+                            <div className="share-setup">
+                                <Select
+                                    className="user-search"
+                                    placeholder="Enter user or group name..."
+                                    isMulti
+                                    isSearchable
+                                    closeMenuOnSelect={false}
+                                    options={options}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={this.handleUsersChange}
+                                    value={this.selectedUsers}
+                                    styles={{
+                                        indicatorSeparator: () => ({
+                                            visibility: 'hidden',
+                                        }),
+                                    }}
+                                />
+                                <select className="permissions-select" onChange={this.handlePermissionsChange} value={this.permissions}>
+                                    {this.sharingOptions(true)}
+                                </select>
+                                <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
+                                    Share
+                                </button>
+                            </div>
+                            <div className="sort-checkboxes">
+                                <input type="checkbox" onChange={action(() => (this.showUserOptions = !this.showUserOptions))} /> <label style={{ marginRight: 10 }}>Individuals</label>
+                                <input type="checkbox" onChange={action(() => (this.showGroupOptions = !this.showGroupOptions))} /> <label>Groups</label>
+                            </div>
 
-                        <div className="acl-container">
-                            {Doc.noviceMode ? (null) :
-                                <div className="layoutDoc-acls">
-                                    <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
-                                </div>}
+                            <div className="acl-container">
+                                {Doc.noviceMode ? null : (
+                                    <div className="layoutDoc-acls">
+                                        <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
+                                    </div>
+                                )}
+                            </div>
                         </div>
-                    </div>
                     }
                     <div className="main-container">
-                        <div className={"individual-container"}>
-                            <div
-                                className="user-sort"
-                                onClick={action(() => this.individualSort = this.individualSort === "ascending" ? "descending" : this.individualSort === "descending" ? "none" : "ascending")}>
-                                Individuals {this.individualSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
-                                    : this.individualSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
-                                        : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />}
-                            </div>
-                            <div className={"users-list"}>
-                                {userListContents}
+                        <div className={'individual-container'}>
+                            <div className="user-sort" onClick={action(() => (this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}>
+                                Individuals{' '}
+                                {this.individualSort === 'ascending' ? (
+                                    <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
+                                ) : this.individualSort === 'descending' ? (
+                                    <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
+                                ) : (
+                                    <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
+                                )}
                             </div>
+                            <div className={'users-list'}>{userListContents}</div>
                         </div>
-                        <div className={"group-container"}>
-                            <div
-                                className="user-sort"
-                                onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}>
-                                Groups {this.groupSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
-                                    : this.groupSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
-                                        : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />}
-
-                            </div>
-                            <div className={"groups-list"}>
-                                {groupListContents}
+                        <div className={'group-container'}>
+                            <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
+                                Groups{' '}
+                                {this.groupSort === 'ascending' ? (
+                                    <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
+                                ) : this.groupSort === 'descending' ? (
+                                    <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
+                                ) : (
+                                    <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
+                                )}
                             </div>
+                            <div className={'groups-list'}>{groupListContents}</div>
                         </div>
                     </div>
-
                 </div>
             </div>
         );
     }
 
     render() {
-        return <MainViewModal
-            contents={this.sharingInterface}
-            isDisplayed={this.isOpen}
-            interactive={true}
-            dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
-            overlayDisplayedOpacity={this.overlayOpacity}
-            closeOnExternalClick={this.close}
-        />;
+        return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 31fa5b157..542f85228 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -4,42 +4,46 @@
 
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
-import { AssignAllExtensions } from "../../extensions/General/Extensions";
-import { Docs } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { LinkManager } from "../util/LinkManager";
+import { AssignAllExtensions } from '../../extensions/General/Extensions';
+import { Docs } from '../documents/Documents';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { LinkManager } from '../util/LinkManager';
 import { ReplayMovements } from '../util/ReplayMovements';
-import { TrackMovements } from "../util/TrackMovements";
-import { CollectionView } from "./collections/CollectionView";
-import { MainView } from "./MainView";
+import { TrackMovements } from '../util/TrackMovements';
+import { CollectionView } from './collections/CollectionView';
+import { MainView } from './MainView';
 
 AssignAllExtensions();
 
 (async () => {
-    MainView.Live = window.location.search.includes("live");
-    window.location.search.includes("safe") && CollectionView.SetSafeMode(true);
+    MainView.Live = window.location.search.includes('live');
+    window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
     const info = await CurrentUserUtils.loadCurrentUser();
-    if (info.id !== "__guest__") {
+    if (info.id !== '__guest__') {
         // a guest will not have an id registered
         await CurrentUserUtils.loadUserDocument(info.id);
     } else {
         await Docs.Prototypes.initialize();
         new LinkManager();
     }
-    document.getElementById('root')!.addEventListener('wheel', event => {
-        if (event.ctrlKey) {
-            event.preventDefault();
-        }
-    }, true);
+    document.getElementById('root')!.addEventListener(
+        'wheel',
+        event => {
+            if (event.ctrlKey) {
+                event.preventDefault();
+            }
+        },
+        true
+    );
     const startload = (document as any).startLoad;
-    const loading = Date.now() - (startload ? Number(startload) : (Date.now() - 3000));
-    console.log("Load Time = " + loading);
+    const loading = Date.now() - (startload ? Number(startload) : Date.now() - 3000);
+    console.log('Loading Time = ' + loading);
     const d = new Date();
-    d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000));
-    const expires = "expires=" + d.toUTCString();
+    d.setTime(d.getTime() + 100 * 24 * 60 * 60 * 1000);
+    const expires = 'expires=' + d.toUTCString();
     document.cookie = `loadtime=${loading};${expires};path=/`;
     new LinkManager();
     new TrackMovements();
     new ReplayMovements();
     ReactDOM.render(<MainView />, document.getElementById('root'));
-})();
\ No newline at end of file
+})();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4940c5f9d..6c0a67de2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -32,7 +32,7 @@ import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOpti
 import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
 import { CollectionViewType } from './collections/CollectionView';
-import "./collections/TreeView.scss";
+import './collections/TreeView.scss';
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
 import { DashboardView } from './DashboardView';
@@ -45,7 +45,7 @@ import { KeyManager } from './GlobalKeyHandler';
 import { InkTranscription } from './InkTranscription';
 import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
-import "./MainView.scss";
+import './MainView.scss';
 import { AudioBox } from './nodes/AudioBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
@@ -63,7 +63,7 @@ import { PreviewCursor } from './PreviewCursor';
 import { PropertiesView } from './PropertiesView';
 import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
-const _global = (window /* browser */ || global /* node */) as any;
+const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
 export class MainView extends React.Component {
@@ -73,30 +73,54 @@ export class MainView extends React.Component {
     @observable public LastButton: Opt<Doc>;
     @observable private _windowWidth: number = 0;
     @observable private _windowHeight: number = 0;
-    @observable private _dashUIWidth: number = 0;   // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
-    @observable private _dashUIHeight: number = 0;  // height of entire main dashboard region including top menu buttons
-    @observable private _panelContent: string = "none";
+    @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
+    @observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons
+    @observable private _panelContent: string = 'none';
     @observable private _sidebarContent: any = CurrentUserUtils.MyLeftSidebarPanel;
     @observable private _leftMenuFlyoutWidth: number = 0;
 
-    @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
-    @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
-    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; }
-    @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
-    @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); }
-    @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
-    @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
-    @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
-    @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
-    @computed private get headerBarDoc() { return CurrentUserUtils.MyHeaderBar; }
-    @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
+    @computed private get dashboardTabHeight() {
+        return 27;
+    } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
+    @computed private get topOfDashUI() {
+        return Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', ''));
+    }
+    @computed private get topOfHeaderBarDoc() {
+        return this.topOfDashUI;
+    }
+    @computed private get topOfSidebarDoc() {
+        return this.topOfDashUI + this.topMenuHeight();
+    }
+    @computed private get topOfMainDoc() {
+        return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight();
+    }
+    @computed private get topOfMainDocContent() {
+        return this.topOfMainDoc + this.dashboardTabHeight;
+    }
+    @computed private get leftScreenOffsetOfMainDocView() {
+        return this.leftMenuWidth() - 2;
+    }
+    @computed private get userDoc() {
+        return Doc.UserDoc();
+    }
+    @computed private get colorScheme() {
+        return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
+    }
+    @computed private get mainContainer() {
+        return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard;
+    }
+    @computed private get headerBarDoc() {
+        return CurrentUserUtils.MyHeaderBar;
+    }
+    @computed public get mainFreeform(): Opt<Doc> {
+        return (docs => (docs?.length > 1 ? docs[1] : undefined))(DocListCast(this.mainContainer!.data));
+    }
 
     headerBarDocWidth = () => this.mainDocViewWidth();
     headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0;
     topMenuHeight = () => 35;
     topMenuWidth = returnZero; // value is ignored ...
-    leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", ""));
+    leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace('px', ''));
     leftMenuHeight = () => this._dashUIHeight;
     leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
     leftMenuFlyoutHeight = () => this._dashUIHeight;
@@ -106,89 +130,325 @@ export class MainView extends React.Component {
     mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight();
 
     componentDidMount() {
-        document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
-        const ele = document.getElementById("loader");
-        const prog = document.getElementById("dash-progress");
+        document.getElementById('root')?.addEventListener('scroll', e => (ele => (ele.scrollLeft = ele.scrollTop = 0))(document.getElementById('root')!));
+        const ele = document.getElementById('loader');
+        const prog = document.getElementById('dash-progress');
         if (ele && prog) {
             // remove from DOM
             setTimeout(() => {
                 clearTimeout();
-                prog.style.transition = "1s";
-                prog.style.width = "100%";
+                prog.style.transition = '1s';
+                prog.style.width = '100%';
             }, 0);
-            setTimeout(() => ele.outerHTML = '', 1000);
+            setTimeout(() => (ele.outerHTML = ''), 1000);
         }
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "showSidebar", "sidebarWidthPercent", "viewTransition",
-                "panX", "panY", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap",
-                "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
+            DocServer.setPlaygroundFields([
+                'dataTransition',
+                'treeViewOpen',
+                'showSidebar',
+                'sidebarWidthPercent',
+                'viewTransition',
+                'panX',
+                'panY',
+                'nativeWidth',
+                'nativeHeight',
+                'text-scrollHeight',
+                'text-height',
+                'hideMinimap',
+                'viewScale',
+                'scrollTop',
+                'hidden',
+                'curPage',
+                'viewType',
+                'chromeHidden',
+                'nativeWidth',
+            ]); // can play with these fields on someone else's
         }
-        DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
+        DocServer.GetRefField('rtfProto').then(
+            proto =>
+                proto instanceof Doc &&
+                reaction(
+                    () => StrCast(proto.BROADCAST_MESSAGE),
+                    msg => msg && alert(msg)
+                )
+        );
 
         const tag = document.createElement('script');
-        tag.src = "https://www.youtube.com/iframe_api";
+        tag.src = 'https://www.youtube.com/iframe_api';
         const firstScriptTag = document.getElementsByTagName('script')[0];
         firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
-        window.removeEventListener("keydown", KeyManager.Instance.handle);
-        window.addEventListener("keydown", KeyManager.Instance.handle);
-        window.removeEventListener("keyup", KeyManager.Instance.unhandle);
-        window.addEventListener("keyup", KeyManager.Instance.unhandle);
-        window.addEventListener("paste", KeyManager.Instance.paste as any);
-        document.addEventListener("dash", (e: any) => {  // event used by chrome plugin to tell Dash which document to focus on
+        window.removeEventListener('keydown', KeyManager.Instance.handle);
+        window.addEventListener('keydown', KeyManager.Instance.handle);
+        window.removeEventListener('keyup', KeyManager.Instance.unhandle);
+        window.addEventListener('keyup', KeyManager.Instance.unhandle);
+        window.addEventListener('paste', KeyManager.Instance.paste as any);
+        document.addEventListener('dash', (e: any) => {
+            // event used by chrome plugin to tell Dash which document to focus on
             const id = FormattedTextBox.GetDocFromUrl(e.detail);
-            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined, []) : (null));
+            DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.jumpToDocument(doc, false, undefined, []) : null));
         });
-        document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
+        document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener);
         this.initEventListeners();
     }
 
     componentWillUnMount() {
-        window.removeEventListener("keyup", KeyManager.Instance.unhandle);
-        window.removeEventListener("keydown", KeyManager.Instance.handle);
-        window.removeEventListener("pointerdown", this.globalPointerDown);
-        window.removeEventListener("paste", KeyManager.Instance.paste as any);
-        document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener);
+        window.removeEventListener('keyup', KeyManager.Instance.unhandle);
+        window.removeEventListener('keydown', KeyManager.Instance.handle);
+        window.removeEventListener('pointerdown', this.globalPointerDown);
+        window.removeEventListener('paste', KeyManager.Instance.paste as any);
+        document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener);
     }
 
     constructor(props: Readonly<{}>) {
         super(props);
         MainView.Instance = this;
-        CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
+        CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || ({} as any);
 
         // causes errors to be generated when modifying an observable outside of an action
-        configure({ enforceActions: "observed" });
+        configure({ enforceActions: 'observed' });
 
-        if (window.location.pathname !== "/home") {
-            const pathname = window.location.pathname.substr(1).split("/");
-            if (pathname.length > 1 && pathname[0] === "doc") {
+        if (window.location.pathname !== '/home') {
+            const pathname = window.location.pathname.substr(1).split('/');
+            if (pathname.length > 1 && pathname[0] === 'doc') {
                 CurrentUserUtils.MainDocId = pathname[1];
                 !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field)));
             }
         }
 
-        library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
-        fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
-        fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
-        fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
-        fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
-        fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
-        fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
-        fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
-        fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
-        fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideoSlash, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag,
-        fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
-        fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
-        fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
-        fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
-        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, fa.faAngleDoubleDown, fa.faAngleDoubleLeft, fa.faAngleDoubleUp, faBuffer as any, fa.faExpand, fa.faUndo,
-        fa.faSlidersH, fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
-        fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
-        fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
-        fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
-        fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown, fa.faSquareRootAlt, fa.faVolumeMute]);
+        library.add(
+            ...[
+                fa.faEdit,
+                fa.faTrash,
+                fa.faTrashAlt,
+                fa.faShare,
+                fa.faDownload,
+                fa.faExpandArrowsAlt,
+                fa.faLayerGroup,
+                fa.faExternalLinkAlt,
+                fa.faCalendar,
+                fa.faSquare,
+                far.faSquare as any,
+                fa.faConciergeBell,
+                fa.faWindowRestore,
+                fa.faFolder,
+                fa.faMapPin,
+                fa.faMapMarker,
+                fa.faFingerprint,
+                fa.faCrosshairs,
+                fa.faDesktop,
+                fa.faUnlock,
+                fa.faLock,
+                fa.faLaptopCode,
+                fa.faMale,
+                fa.faCopy,
+                fa.faHandPointLeft,
+                fa.faHandPointRight,
+                fa.faCompass,
+                fa.faSnowflake,
+                fa.faMicrophone,
+                fa.faKeyboard,
+                fa.faQuestion,
+                fa.faTasks,
+                fa.faPalette,
+                fa.faAngleLeft,
+                fa.faAngleRight,
+                fa.faBell,
+                fa.faCamera,
+                fa.faExpand,
+                fa.faCaretDown,
+                fa.faCaretLeft,
+                fa.faCaretRight,
+                fa.faCaretSquareDown,
+                fa.faCaretSquareRight,
+                fa.faArrowsAltH,
+                fa.faPlus,
+                fa.faMinus,
+                fa.faTerminal,
+                fa.faToggleOn,
+                fa.faFile,
+                fa.faLocationArrow,
+                fa.faSearch,
+                fa.faFileDownload,
+                fa.faFileUpload,
+                fa.faStop,
+                fa.faCalculator,
+                fa.faWindowMaximize,
+                fa.faAddressCard,
+                fa.faQuestionCircle,
+                fa.faArrowLeft,
+                fa.faArrowRight,
+                fa.faArrowDown,
+                fa.faArrowUp,
+                fa.faBolt,
+                fa.faBullseye,
+                fa.faCaretUp,
+                fa.faCat,
+                fa.faCheck,
+                fa.faChevronRight,
+                fa.faChevronLeft,
+                fa.faChevronDown,
+                fa.faChevronUp,
+                fa.faClone,
+                fa.faCloudUploadAlt,
+                fa.faCommentAlt,
+                fa.faCompressArrowsAlt,
+                fa.faCut,
+                fa.faEllipsisV,
+                fa.faEraser,
+                fa.faExclamation,
+                fa.faFileAlt,
+                fa.faFileAudio,
+                fa.faFileVideo,
+                fa.faFilePdf,
+                fa.faFilm,
+                fa.faFilter,
+                fa.faFont,
+                fa.faGlobeAmericas,
+                fa.faGlobeAsia,
+                fa.faHighlighter,
+                fa.faLongArrowAltRight,
+                fa.faMousePointer,
+                fa.faMusic,
+                fa.faObjectGroup,
+                fa.faPause,
+                fa.faPen,
+                fa.faPenNib,
+                fa.faPhone,
+                fa.faPlay,
+                fa.faPortrait,
+                fa.faRedoAlt,
+                fa.faStamp,
+                fa.faStickyNote,
+                fa.faArrowsAltV,
+                fa.faTimesCircle,
+                fa.faThumbtack,
+                fa.faTree,
+                fa.faTv,
+                fa.faUndoAlt,
+                fa.faVideoSlash,
+                fa.faVideo,
+                fa.faAsterisk,
+                fa.faBrain,
+                fa.faImage,
+                fa.faPaintBrush,
+                fa.faTimes,
+                fa.faFlag,
+                fa.faEye,
+                fa.faArrowsAlt,
+                fa.faQuoteLeft,
+                fa.faSortAmountDown,
+                fa.faAlignLeft,
+                fa.faAlignCenter,
+                fa.faAlignRight,
+                fa.faHeading,
+                fa.faRulerCombined,
+                fa.faFillDrip,
+                fa.faLink,
+                fa.faUnlink,
+                fa.faBold,
+                fa.faItalic,
+                fa.faClipboard,
+                fa.faUnderline,
+                fa.faStrikethrough,
+                fa.faSuperscript,
+                fa.faSubscript,
+                fa.faIndent,
+                fa.faEyeDropper,
+                fa.faPaintRoller,
+                fa.faBars,
+                fa.faBrush,
+                fa.faShapes,
+                fa.faEllipsisH,
+                fa.faHandPaper,
+                fa.faMap,
+                fa.faUser,
+                faHireAHelper as any,
+                fa.faTrashRestore,
+                fa.faUsers,
+                fa.faWrench,
+                fa.faCog,
+                fa.faMap,
+                fa.faBellSlash,
+                fa.faExpandAlt,
+                fa.faArchive,
+                fa.faBezierCurve,
+                fa.faCircle,
+                far.faCircle as any,
+                fa.faLongArrowAltRight,
+                fa.faPenFancy,
+                fa.faAngleDoubleRight,
+                fa.faAngleDoubleDown,
+                fa.faAngleDoubleLeft,
+                fa.faAngleDoubleUp,
+                faBuffer as any,
+                fa.faExpand,
+                fa.faUndo,
+                fa.faSlidersH,
+                fa.faAngleUp,
+                fa.faAngleDown,
+                fa.faPlayCircle,
+                fa.faClock,
+                fa.faRocket,
+                fa.faExchangeAlt,
+                fa.faHashtag,
+                fa.faAlignJustify,
+                fa.faCheckSquare,
+                fa.faListUl,
+                fa.faWindowMinimize,
+                fa.faWindowRestore,
+                fa.faTextWidth,
+                fa.faTextHeight,
+                fa.faClosedCaptioning,
+                fa.faInfoCircle,
+                fa.faTag,
+                fa.faSyncAlt,
+                fa.faPhotoVideo,
+                fa.faArrowAltCircleDown,
+                fa.faArrowAltCircleUp,
+                fa.faArrowAltCircleLeft,
+                fa.faArrowAltCircleRight,
+                fa.faStopCircle,
+                fa.faCheckCircle,
+                fa.faGripVertical,
+                fa.faSortUp,
+                fa.faSortDown,
+                fa.faTable,
+                fa.faTh,
+                fa.faThList,
+                fa.faProjectDiagram,
+                fa.faSignature,
+                fa.faColumns,
+                fa.faChevronCircleUp,
+                fa.faUpload,
+                fa.faBorderAll,
+                fa.faBraille,
+                fa.faChalkboard,
+                fa.faPencilAlt,
+                fa.faEyeSlash,
+                fa.faSmile,
+                fa.faIndent,
+                fa.faOutdent,
+                fa.faChartBar,
+                fa.faBan,
+                fa.faPhoneSlash,
+                fa.faGripLines,
+                fa.faSave,
+                fa.faBookmark,
+                fa.faList,
+                fa.faListOl,
+                fa.faFolderPlus,
+                fa.faLightbulb,
+                fa.faBookOpen,
+                fa.faMapMarkerAlt,
+                fa.faSearchPlus,
+                fa.faVolumeUp,
+                fa.faVolumeDown,
+                fa.faSquareRootAlt,
+                fa.faVolumeMute,
+            ]
+        );
         this.initAuthenticationRouters();
     }
 
@@ -197,175 +457,211 @@ export class MainView extends React.Component {
         const targets = document.elementsFromPoint(e.x, e.y);
         if (targets.length) {
             const targClass = targets[0].className.toString();
-            !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
-            !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
+            !targClass.includes('contextMenu') && ContextMenu.Instance.closeMenu();
+            !['timeline-menu-desc', 'timeline-menu-item', 'timeline-menu-input'].includes(targClass) && TimelineMenu.Instance.closeMenu();
         }
     });
 
     initEventListeners = () => {
-        window.addEventListener("drop", e => e.preventDefault(), false);  // prevent default behavior of navigating to a new web page
-        window.addEventListener("dragover", e => e.preventDefault(), false);
+        window.addEventListener('drop', e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page
+        window.addEventListener('dragover', e => e.preventDefault(), false);
         // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
-        document.addEventListener("pointerdown", this.globalPointerDown);
-        document.addEventListener("click", (e: MouseEvent) => {
-            if (!e.cancelBubble) {
-                const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join();
-                if (pathstr?.includes("libraryFlyout")) {
-                    SelectionManager.DeselectAll();
+        document.addEventListener('pointerdown', this.globalPointerDown);
+        document.addEventListener(
+            'click',
+            (e: MouseEvent) => {
+                if (!e.cancelBubble) {
+                    const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join();
+                    if (pathstr?.includes('libraryFlyout')) {
+                        SelectionManager.DeselectAll();
+                    }
                 }
-            }
-        }, false);
+            },
+            false
+        );
         document.oncontextmenu = () => false;
-    }
+    };
 
     initAuthenticationRouters = async () => {
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
-            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(), { fireImmediately: true });
-        } 
+            reaction(
+                () => CurrentUserUtils.GuestTarget,
+                target => target && CurrentUserUtils.createNewDashboard(),
+                { fireImmediately: true }
+            );
+        }
         // else {
         //     PromiseValue(this.userDoc.activeDashboard).then(dash => {
         //         if (dash instanceof Doc) CurrentUserUtils.openDashboard(dash);
         //         else CurrentUserUtils.createNewDashboard();
         //     });
         // }
-    }
+    };
 
     @action
     createNewPresentation = async () => {
-        const pres = Docs.Create.PresDocument({ title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
-        CollectionDockingView.AddSplit(pres, "left");
+        const pres = Docs.Create.PresDocument({ title: 'Untitled Trail', _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: 'alias', _chromeHidden: true, boxShadow: '0 0' });
+        CollectionDockingView.AddSplit(pres, 'left');
         CurrentUserUtils.ActivePresentation = pres;
-        Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", pres);
-    }
+        Doc.AddDocToList(CurrentUserUtils.MyTrails, 'data', pres);
+    };
 
     @action
     createNewFolder = async () => {
-        const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
-        Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder);
-    }
+        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
+        Doc.AddDocToList(CurrentUserUtils.MyFilesystem, 'data', folder);
+    };
 
     @observable _exploreMode = false;
     @computed get exploreMode() {
-        return () => this._exploreMode ? ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)",
-            { documentView: "any", clientX: "number", clientY: "number" })! : undefined;
+        return () => (this._exploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined);
     }
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
 
     @computed get headerBarDocView() {
-        return <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
-            <DocumentView key="headerBarDoc"
-                Document={this.headerBarDoc}
-                DataDoc={undefined}
-                addDocument={undefined}
-                addDocTab={this.addDocTabFunc}
-                pinToPres={emptyFunction}
-                docViewPath={returnEmptyDoclist}
-                styleProvider={DefaultStyleProvider}
-                rootSelected={returnTrue}
-                removeDocument={returnFalse}
-                fitContentsToBox={returnTrue}
-                isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
-                isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
-                ScreenToLocalTransform={this.headerBarScreenXf}
-                childHideResizeHandles={returnTrue}
-                dontRegisterView={true}
-                hideResizeHandles={true}
-                PanelWidth={this.headerBarDocWidth}
-                PanelHeight={this.headerBarDocHeight}
-                renderDepth={0}
-                focus={DocUtils.DefaultFocus}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-            /></div>;
+        return (
+            <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
+                <DocumentView
+                    key="headerBarDoc"
+                    Document={this.headerBarDoc}
+                    DataDoc={undefined}
+                    addDocument={undefined}
+                    addDocTab={this.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    docViewPath={returnEmptyDoclist}
+                    styleProvider={DefaultStyleProvider}
+                    rootSelected={returnTrue}
+                    removeDocument={returnFalse}
+                    fitContentsToBox={returnTrue}
+                    isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
+                    isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
+                    ScreenToLocalTransform={this.headerBarScreenXf}
+                    childHideResizeHandles={returnTrue}
+                    dontRegisterView={true}
+                    hideResizeHandles={true}
+                    PanelWidth={this.headerBarDocWidth}
+                    PanelHeight={this.headerBarDocHeight}
+                    renderDepth={0}
+                    focus={DocUtils.DefaultFocus}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                />
+            </div>
+        );
     }
     @computed get mainDocView() {
-        return <>
-            {this.headerBarDocView}
-            <DocumentView key="main"
-                Document={this.mainContainer!}
-                DataDoc={undefined}
-                addDocument={undefined}
-                addDocTab={this.addDocTabFunc}
-                pinToPres={emptyFunction}
-                docViewPath={returnEmptyDoclist}
-                styleProvider={undefined}
-                rootSelected={returnTrue}
-                isContentActive={returnTrue}
-                removeDocument={undefined}
-                ScreenToLocalTransform={Transform.Identity}
-                PanelWidth={this.mainDocViewWidth}
-                PanelHeight={this.mainDocViewHeight}
-                focus={DocUtils.DefaultFocus}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                suppressSetHeight={true}
-                renderDepth={-1}
-            /></>;
+        return (
+            <>
+                {this.headerBarDocView}
+                <DocumentView
+                    key="main"
+                    Document={this.mainContainer!}
+                    DataDoc={undefined}
+                    addDocument={undefined}
+                    addDocTab={this.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    docViewPath={returnEmptyDoclist}
+                    styleProvider={undefined}
+                    rootSelected={returnTrue}
+                    isContentActive={returnTrue}
+                    removeDocument={undefined}
+                    ScreenToLocalTransform={Transform.Identity}
+                    PanelWidth={this.mainDocViewWidth}
+                    PanelHeight={this.mainDocViewHeight}
+                    focus={DocUtils.DefaultFocus}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    suppressSetHeight={true}
+                    renderDepth={-1}
+                />
+            </>
+        );
     }
 
     @computed get dockingContent() {
-        return <div key="docking" className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
-            style={{
-                minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
-                transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
-            }}>
-            {!this.mainContainer ? (null) : this.mainDocView}
-        </div>;
+        return (
+            <div
+                key="docking"
+                className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? '-flyout' : ''}`}
+                onDrop={e => {
+                    e.stopPropagation();
+                    e.preventDefault();
+                }}
+                style={{
+                    minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
+                    transform: LightboxView.LightboxDoc ? 'scale(0.0001)' : undefined,
+                }}>
+                {!this.mainContainer ? null : this.mainDocView}
+            </div>
+        );
     }
 
     @action
     onPropertiesPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e,
-            action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false),
+        setupMoveUpEvents(
+            this,
+            e,
+            action(e => ((CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false)),
             action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
-            action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0), false);
-    }
+            action(() => (CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0)),
+            false
+        );
+    };
 
     @action
     onFlyoutPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e,
-            action(e => (this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
+        setupMoveUpEvents(
+            this,
+            e,
+            action(e => ((this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false)),
             () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(),
-            this.closeFlyout);
-    }
+            this.closeFlyout
+        );
+    };
 
     sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
     addDocTabFunc = (doc: Doc, location: string): boolean => {
-        const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
-        const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+        const locationFields = doc._viewType === CollectionViewType.Docking ? ['dashboard'] : location.split(':');
+        const locationParams = locationFields.length > 1 ? locationFields[1] : '';
         if (doc.dockingConfig) return CurrentUserUtils.openDashboard(doc);
         switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(doc);
-            case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
-            case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
-            case "lightbox": return LightboxView.AddDocTab(doc, location);
-            case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams);
-            case "inPlace":
-            case "add":
+            case 'dashboard':
+                return CurrentUserUtils.openDashboard(doc);
+            case 'close':
+                return CollectionDockingView.CloseSplit(doc, locationParams);
+            case 'fullScreen':
+                return CollectionDockingView.OpenFullScreen(doc);
+            case 'lightbox':
+                return LightboxView.AddDocTab(doc, location);
+            case 'toggle':
+                return CollectionDockingView.ToggleSplit(doc, locationParams);
+            case 'inPlace':
+            case 'add':
             default:
                 return CollectionDockingView.AddSplit(doc, locationParams);
         }
-    }
-
+    };
 
     @computed get flyout() {
-        return !this._leftMenuFlyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}>
-            {this.docButtons}
-        </div> :
-            <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }} >
-                <div className="mainView-contentArea" >
+        return !this._leftMenuFlyoutWidth ? (
+            <div key="flyout" className={`mainView-libraryFlyout-out`}>
+                {this.docButtons}
+            </div>
+        ) : (
+            <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }}>
+                <div className="mainView-contentArea">
                     <DocumentView
                         Document={this._sidebarContent.proto || this._sidebarContent}
                         DataDoc={undefined}
@@ -393,37 +689,40 @@ export class MainView extends React.Component {
                     />
                 </div>
                 {this.docButtons}
-            </div>;
+            </div>
+        );
     }
 
     @computed get leftMenuPanel() {
-        return <div key="menu" className="mainView-leftMenuPanel">
-            <DocumentView
-                Document={CurrentUserUtils.MyLeftSidebarMenu}
-                DataDoc={undefined}
-                addDocument={undefined}
-                addDocTab={this.addDocTabFunc}
-                pinToPres={emptyFunction}
-                rootSelected={returnTrue}
-                removeDocument={returnFalse}
-                ScreenToLocalTransform={this.sidebarScreenToLocal}
-                PanelWidth={this.leftMenuWidth}
-                PanelHeight={this.leftMenuHeight}
-                renderDepth={0}
-                docViewPath={returnEmptyDoclist}
-                focus={DocUtils.DefaultFocus}
-                styleProvider={DefaultStyleProvider}
-                isContentActive={returnTrue}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                scriptContext={this}
-            />
-        </div>;
+        return (
+            <div key="menu" className="mainView-leftMenuPanel">
+                <DocumentView
+                    Document={CurrentUserUtils.MyLeftSidebarMenu}
+                    DataDoc={undefined}
+                    addDocument={undefined}
+                    addDocTab={this.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    rootSelected={returnTrue}
+                    removeDocument={returnFalse}
+                    ScreenToLocalTransform={this.sidebarScreenToLocal}
+                    PanelWidth={this.leftMenuWidth}
+                    PanelHeight={this.leftMenuHeight}
+                    renderDepth={0}
+                    docViewPath={returnEmptyDoclist}
+                    focus={DocUtils.DefaultFocus}
+                    styleProvider={DefaultStyleProvider}
+                    isContentActive={returnTrue}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    scriptContext={this}
+                />
+            </div>
+        );
     }
 
     @action
@@ -432,69 +731,78 @@ export class MainView extends React.Component {
         const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title;
         this.closeFlyout();
         if (willOpen) {
-            switch (this._panelContent = title) {
-                case "Settings":
+            switch ((this._panelContent = title)) {
+                case 'Settings':
                     SettingsManager.Instance.open();
                     break;
-                case "Help":
+                case 'Help':
                     break;
                 default:
                     this.expandFlyout(button);
             }
         }
         return true;
-    }
+    };
 
     @computed get mainInnerContent() {
         const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth();
         const width = this.propertiesWidth() + leftMenuFlyoutWidth;
-        return <>
-            {this.leftMenuPanel}
-            <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
-                {this.flyout}
-                <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
-                    <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
-                </div>
-                <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
-
-                    {this.dockingContent}
-
-                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
-                        <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
+        return (
+            <>
+                {this.leftMenuPanel}
+                <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
+                    {this.flyout}
+                    <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} onPointerDown={this.onFlyoutPointerDown}>
+                        <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? 'white' : 'black'} style={{ opacity: '50%' }} size="sm" />
                     </div>
-                    <div className="properties-container" style={{ width: this.propertiesWidth() }}>
-                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+                    <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
+                        {this.dockingContent}
+
+                        <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
+                            <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
+                        </div>
+                        <div className="properties-container" style={{ width: this.propertiesWidth() }}>
+                            {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+                        </div>
                     </div>
                 </div>
-            </div>
-        </>;
+            </>
+        );
     }
 
-
     @computed get mainDashboardArea() {
-        return !this.userDoc ? (null) :
-            <div className="mainView-dashboardArea" ref={r => {
-                r && new _global.ResizeObserver(action(() => {
-                    this._dashUIWidth = r.getBoundingClientRect().width;
-                    this._dashUIHeight = r.getBoundingClientRect().height;
-                })).observe(r);
-            }} style={{
-                color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black",
-                height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`,
-                width: "100%",
-            }} >
+        return !this.userDoc ? null : (
+            <div
+                className="mainView-dashboardArea"
+                ref={r => {
+                    r &&
+                        new _global.ResizeObserver(
+                            action(() => {
+                                this._dashUIWidth = r.getBoundingClientRect().width;
+                                this._dashUIHeight = r.getBoundingClientRect().height;
+                            })
+                        ).observe(r);
+                }}
+                style={{
+                    color: this.colorScheme === ColorScheme.Dark ? 'rgb(205,205,205)' : 'black',
+                    height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`,
+                    width: '100%',
+                }}>
                 {this.mainInnerContent}
-            </div>;
+            </div>
+        );
     }
 
-
     expandFlyout = action((button: Doc) => {
-        // bcz: What's going on here!?  
+        // bcz: What's going on here!?
         // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab.  All of the div below the CollectionFreeFormView's main div
         // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be.
         // interactively adjusting the flyout fixes the problem.  So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);)
-        this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250);
-        setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0);
+        this._leftMenuFlyoutWidth = this._leftMenuFlyoutWidth || 250;
+        setTimeout(
+            action(() => (this._leftMenuFlyoutWidth += 0.5)),
+            0
+        );
 
         this._sidebarContent.proto = button.target as any;
         this.LastButton = button;
@@ -502,29 +810,29 @@ export class MainView extends React.Component {
 
     closeFlyout = action(() => {
         this.LastButton = undefined;
-        this._panelContent = "none";
+        this._panelContent = 'none';
         this._sidebarContent.proto = undefined;
         this._leftMenuFlyoutWidth = 0;
     });
 
-    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(CurrentUserUtils.MyDockedBtns, "data", doc), true);
+    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(CurrentUserUtils.MyDockedBtns, 'data', doc), true);
     moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
-    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(CurrentUserUtils.MyDockedBtns, "data", doc), true);
+    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(CurrentUserUtils.MyDockedBtns, 'data', doc), true);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
         return new Transform(-translateX, -translateY, 1 / scale);
-    }
+    };
 
     @computed get docButtons() {
-        return !CurrentUserUtils.MyDockedBtns ? (null) :
-            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
+        return !CurrentUserUtils.MyDockedBtns ? null : (
+            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? '42px' : undefined }}>
                 <CollectionLinearView
                     Document={CurrentUserUtils.MyDockedBtns}
                     DataDoc={undefined}
-                    fieldKey={"data"}
-                    dropAction={"alias"}
+                    fieldKey={'data'}
+                    dropAction={'alias'}
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
@@ -550,51 +858,62 @@ export class MainView extends React.Component {
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined} />
-                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div> : <></>}
-            </div>;
+                    ContainingCollectionDoc={undefined}
+                />
+                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : <></>}
+            </div>
+        );
     }
     @computed get snapLines() {
-        return !SnappingManager.GetShowSnapLines() ? (null) : <div className="mainView-snapLines">
-            <svg style={{ width: "100%", height: "100%" }}>
-                {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-            </svg>
-        </div>;
+        return !SnappingManager.GetShowSnapLines() ? null : (
+            <div className="mainView-snapLines">
+                <svg style={{ width: '100%', height: '100%' }}>
+                    {SnappingManager.horizSnapLines().map(l => (
+                        <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={'1 1'} />
+                    ))}
+                    {SnappingManager.vertSnapLines().map(l => (
+                        <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={'1 1'} />
+                    ))}
+                </svg>
+            </div>
+        );
     }
 
     @computed get inkResources() {
-        return <svg width={0} height={0}>
-            <defs>
-                <filter id="inkSelectionHalo">
-                    <feColorMatrix type="matrix"
-                        result="color"
-                        values="1 0 0 0 0
+        return (
+            <svg width={0} height={0}>
+                <defs>
+                    <filter id="inkSelectionHalo">
+                        <feColorMatrix
+                            type="matrix"
+                            result="color"
+                            values="1 0 0 0 0
                 0 0 0 0 0
                 0 0 0 0 0
-                0 0 0 1 0">
-                    </feColorMatrix>
-                    <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
-                    <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
-                    <feMerge>
-                        <feMergeNode in="bg"></feMergeNode>
-                        <feMergeNode in="offset"></feMergeNode>
-                        <feMergeNode in="SourceGraphic"></feMergeNode>
-                    </feMerge>
-                </filter>
-            </defs>
-        </svg>;
+                0 0 0 1 0"></feColorMatrix>
+                        <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
+                        <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
+                        <feMerge>
+                            <feMergeNode in="bg"></feMergeNode>
+                            <feMergeNode in="offset"></feMergeNode>
+                            <feMergeNode in="SourceGraphic"></feMergeNode>
+                        </feMerge>
+                    </filter>
+                </defs>
+            </svg>
+        );
     }
 
-    @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
-        return !DocumentLinksButton.invisibleWebDoc ? null :
+    @computed get invisibleWebBox() {
+        // see note under the makeLink method in HypothesisUtils.ts
+        return !DocumentLinksButton.invisibleWebDoc ? null : (
             <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
                 <WebBox
-                    fieldKey={"data"}
+                    fieldKey={'data'}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     Document={DocumentLinksButton.invisibleWebDoc}
-                    dropAction={"move"}
+                    dropAction={'move'}
                     styleProvider={undefined}
                     isSelected={returnFalse}
                     select={returnFalse}
@@ -615,108 +934,131 @@ export class MainView extends React.Component {
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
-            </div>;
+            </div>
+        );
     }
 
     render() {
-        return (<div className={`mainView-container${this.colorScheme}`}
-            onScroll={() => ((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)}
-            ref={r => {
-                r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r);
-            }}>
-            {this.inkResources}
-            <DictationOverlay />
-            <SharingManager />
-            <SettingsManager />
-            <CaptureManager />
-            <GroupManager />
-            <GoogleAuthenticationManager />
-            <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfHeaderBarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
-            <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
-            <TopBar />
-            {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
-            {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
-            {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
-            
-            {((page:string) => {
-                switch (page) {
-                    case "dashboard":
-                    default:return <>
-                            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
-                                    <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
-                            </div> 
-                            <GestureOverlay> {this.mainDashboardArea}   </GestureOverlay>
-                        </>;
-                    case "home":  return <DashboardView/>;
-                } })(CurrentUserUtils.ActivePage)
-            }
-            
-            <PreviewCursor />
-            <TaskCompletionBox />
-            <ContextMenu />
-            <RadialMenu />
-            <AnchorMenu />
-            <DashFieldViewMenu />
-            <MarqueeOptionsMenu />
-            <OverlayView />
-            <TimelineMenu />
-            <RichTextMenu />
-            <InkTranscription />
-            {this.snapLines}
-            <div className="mainView-webRef" ref={this.makeWebRef} />
-            <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
-        </div >);
+        return (
+            <div
+                className={`mainView-container${this.colorScheme}`}
+                onScroll={() => (ele => (ele.scrollTop = ele.scrollLeft = 0))(document.getElementById('root')!)}
+                ref={r => {
+                    r &&
+                        new _global.ResizeObserver(
+                            action(() => {
+                                this._windowWidth = r.getBoundingClientRect().width;
+                                this._windowHeight = r.getBoundingClientRect().height;
+                            })
+                        ).observe(r);
+                }}>
+                {this.inkResources}
+                <DictationOverlay />
+                <SharingManager />
+                <SettingsManager />
+                <CaptureManager />
+                <GroupManager />
+                <GoogleAuthenticationManager />
+                <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfHeaderBarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
+                <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
+                <TopBar />
+                {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
+                {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocumentLinksButton.LinkEditorDocView = undefined))} docView={DocumentLinksButton.LinkEditorDocView} /> : null}
+                {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null}
+
+                {((page: string) => {
+                    switch (page) {
+                        case 'dashboard':
+                        default:
+                            return (
+                                <>
+                                    <div style={{ position: 'relative', display: LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 1999 }}>
+                                        <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+                                    </div>
+                                    <GestureOverlay> {this.mainDashboardArea} </GestureOverlay>
+                                </>
+                            );
+                        case 'home':
+                            return <DashboardView />;
+                    }
+                })(CurrentUserUtils.ActivePage)}
+
+                <PreviewCursor />
+                <TaskCompletionBox />
+                <ContextMenu />
+                <RadialMenu />
+                <AnchorMenu />
+                <DashFieldViewMenu />
+                <MarqueeOptionsMenu />
+                <OverlayView />
+                <TimelineMenu />
+                <RichTextMenu />
+                <InkTranscription />
+                {this.snapLines}
+                <div className="mainView-webRef" ref={this.makeWebRef} />
+                <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
+            </div>
+        );
     }
 
     makeWebRef = (ele: HTMLDivElement) => {
-        reaction(() => DocumentLinksButton.invisibleWebDoc,
+        reaction(
+            () => DocumentLinksButton.invisibleWebDoc,
             invisibleDoc => {
                 ReactDOM.unmountComponentAtNode(ele);
-                invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" >
-                    <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
-                        <WebBox
-                            fieldKey={"data"}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined}
-                            Document={invisibleDoc}
-                            dropAction={"move"}
-                            isSelected={returnFalse}
-                            docViewPath={returnEmptyDoclist}
-                            select={returnFalse}
-                            rootSelected={returnFalse}
-                            renderDepth={0}
-                            setHeight={returnFalse}
-                            styleProvider={undefined}
-                            addDocTab={returnFalse}
-                            pinToPres={returnFalse}
-                            ScreenToLocalTransform={Transform.Identity}
-                            bringToFront={returnFalse}
-                            isContentActive={emptyFunction}
-                            whenChildContentsActiveChanged={returnFalse}
-                            focus={returnFalse}
-                            PanelWidth={() => 500}
-                            PanelHeight={() => 800}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
-                        />
-                    </div>;
-                </span>, ele);
+                invisibleDoc &&
+                    ReactDOM.render(
+                        <span title="Drag as document" className="invisible-webbox">
+                            <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
+                                <WebBox
+                                    fieldKey={'data'}
+                                    ContainingCollectionView={undefined}
+                                    ContainingCollectionDoc={undefined}
+                                    Document={invisibleDoc}
+                                    dropAction={'move'}
+                                    isSelected={returnFalse}
+                                    docViewPath={returnEmptyDoclist}
+                                    select={returnFalse}
+                                    rootSelected={returnFalse}
+                                    renderDepth={0}
+                                    setHeight={returnFalse}
+                                    styleProvider={undefined}
+                                    addDocTab={returnFalse}
+                                    pinToPres={returnFalse}
+                                    ScreenToLocalTransform={Transform.Identity}
+                                    bringToFront={returnFalse}
+                                    isContentActive={emptyFunction}
+                                    whenChildContentsActiveChanged={returnFalse}
+                                    focus={returnFalse}
+                                    PanelWidth={() => 500}
+                                    PanelHeight={() => 800}
+                                    docFilters={returnEmptyFilter}
+                                    docRangeFilters={returnEmptyFilter}
+                                    searchFilterDocs={returnEmptyDoclist}
+                                />
+                            </div>
+                            ;
+                        </span>,
+                        ele
+                    );
 
                 let success = false;
                 const onSuccess = () => {
                     success = true;
                     clearTimeout(interval);
-                    document.removeEventListener("editSuccess", onSuccess);
+                    document.removeEventListener('editSuccess', onSuccess);
                 };
 
                 // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
                 // so we keep simulating clicks until annotations have loaded and editing is successful
                 const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500);
                 setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
-                document.addEventListener("editSuccess", onSuccess);
-            });
-    }
+                document.addEventListener('editSuccess', onSuccess);
+            }
+        );
+    };
 }
 
-ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file
+ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) {
+    MainView.Instance.selectMenu(doc);
+});
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 789756a78..436cb688f 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -4,7 +4,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
 
 const HOLD_DURATION = 1000;
 
-export abstract class Touchable<T = {}> extends React.Component<T> {
+export abstract class Touchable<T = {}> extends React.Component<React.PropsWithChildren<T>> {
     //private holdTimer: NodeJS.Timeout | undefined;
     private moveDisposer?: InteractionUtils.MultiTouchEventDisposer;
     private endDisposer?: InteractionUtils.MultiTouchEventDisposer;
@@ -25,7 +25,6 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
      */
     @action
     protected onTouchStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): void => {
-
         const actualPts: React.Touch[] = [];
         const te = me.touchEvent;
         // loop through all touches on screen
@@ -60,7 +59,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
                 case 1:
                     this.handle1PointerDown(te, me);
                     te.persist();
-                    // -- code for radial menu -- 
+                    // -- code for radial menu --
                     // if (this.holdTimer) {
                     //     clearTimeout(this.holdTimer)
                     //     this.holdTimer = undefined;
@@ -71,11 +70,11 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
                     break;
             }
         }
-    }
+    };
 
     /**
-    * Handle touch move event
-    */
+     * Handle touch move event
+     */
     @action
     protected onTouch = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         const te = me.touchEvent;
@@ -85,8 +84,12 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
         if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return;
         this._touchDrag = true;
         switch (myTouches.length) {
-            case 1: this.handle1PointerMove(te, me); break;
-            case 2: this.handle2PointersMove(te, me); break;
+            case 1:
+                this.handle1PointerMove(te, me);
+                break;
+            case 2:
+                this.handle2PointersMove(te, me);
+                break;
         }
 
         for (const pt of me.touches) {
@@ -94,7 +97,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
                 this.prevPoints.set(pt.identifier, pt);
             }
         }
-    }
+    };
 
     @action
     protected onTouchEnd = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
@@ -110,7 +113,6 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
         this._touchDrag = false;
         te.stopPropagation();
 
-
         // if (e.targetTouches.length === 0) {
         //     this.prevPoints.clear();
         // }
@@ -119,36 +121,36 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
             this.cleanUpInteractions();
         }
         e.stopPropagation();
-    }
+    };
 
     cleanUpInteractions = (): void => {
         this.removeMoveListeners();
         this.removeEndListeners();
-    }
+    };
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
         this.removeMoveListeners();
         this.addMoveListeners();
         this.removeEndListeners();
         this.addEndListeners();
-    }
+    };
 
     handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
         this.removeMoveListeners();
         this.addMoveListeners();
         this.removeEndListeners();
         this.addEndListeners();
-    }
+    };
 
     handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
         e.stopPropagation();
@@ -159,30 +161,30 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
         this.removeHoldEndListeners();
         this.addHoldMoveListeners();
         this.addHoldEndListeners();
-    }
+    };
 
     addMoveListeners = () => {
         const handler = (e: Event) => this.onTouch(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
-        document.addEventListener("dashOnTouchMove", handler);
-        this.moveDisposer = () => document.removeEventListener("dashOnTouchMove", handler);
-    }
+        document.addEventListener('dashOnTouchMove', handler);
+        this.moveDisposer = () => document.removeEventListener('dashOnTouchMove', handler);
+    };
     addEndListeners = () => {
         const handler = (e: Event) => this.onTouchEnd(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
-        document.addEventListener("dashOnTouchEnd", handler);
-        this.endDisposer = () => document.removeEventListener("dashOnTouchEnd", handler);
-    }
+        document.addEventListener('dashOnTouchEnd', handler);
+        this.endDisposer = () => document.removeEventListener('dashOnTouchEnd', handler);
+    };
 
     addHoldMoveListeners = () => {
         const handler = (e: Event) => this.handle1PointerHoldMove(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
-        document.addEventListener("dashOnTouchHoldMove", handler);
-        this.holdMoveDisposer = () => document.removeEventListener("dashOnTouchHoldMove", handler);
-    }
+        document.addEventListener('dashOnTouchHoldMove', handler);
+        this.holdMoveDisposer = () => document.removeEventListener('dashOnTouchHoldMove', handler);
+    };
 
     addHoldEndListeners = () => {
         const handler = (e: Event) => this.handle1PointerHoldEnd(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
-        document.addEventListener("dashOnTouchHoldEnd", handler);
-        this.holdEndDisposer = () => document.removeEventListener("dashOnTouchHoldEnd", handler);
-    }
+        document.addEventListener('dashOnTouchHoldEnd', handler);
+        this.holdEndDisposer = () => document.removeEventListener('dashOnTouchHoldEnd', handler);
+    };
 
     removeMoveListeners = () => this.moveDisposer?.();
     removeEndListeners = () => this.endDisposer?.();
@@ -192,7 +194,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
     handle1PointerHoldMove = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         // e.stopPropagation();
         // me.touchEvent.stopPropagation();
-    }
+    };
 
     handle1PointerHoldEnd = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         e.stopPropagation();
@@ -202,10 +204,10 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
 
         me.touchEvent.stopPropagation();
         me.touchEvent.preventDefault();
-    }
+    };
 
     handleHandDown = (e: React.TouchEvent) => {
         // e.stopPropagation();
         // e.preventDefault();
-    }
-}
\ No newline at end of file
+    };
+}
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index e80ba6f36..7a393a4f7 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -1,19 +1,19 @@
-import { faBackward, faForward, faGripLines, faPauseCircle, faPlayCircle } from "@fortawesome/free-solid-svg-icons";
+import { faBackward, faForward, faGripLines, faPauseCircle, faPlayCircle } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction, trace } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
-import { FieldViewProps } from "../nodes/FieldView";
-import { KeyframeFunc } from "./Keyframe";
-import "./Timeline.scss";
-import { TimelineOverview } from "./TimelineOverview";
-import { Track } from "./Track";
-import clamp from "../../util/clamp";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { IconLookup } from "@fortawesome/fontawesome-svg-core";
+import { action, computed, observable, runInAction, trace } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from '../../../Utils';
+import { FieldViewProps } from '../nodes/FieldView';
+import { KeyframeFunc } from './Keyframe';
+import './Timeline.scss';
+import { TimelineOverview } from './TimelineOverview';
+import { Track } from './Track';
+import clamp from '../../util/clamp';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 
 /**
  * Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are 
@@ -35,10 +35,8 @@ import { IconLookup } from "@fortawesome/fontawesome-svg-core";
     @author Andrew Kim 
  */
 
-
 @observer
 export class Timeline extends React.Component<FieldViewProps> {
-
     //readonly constants
     private readonly DEFAULT_TICK_SPACING: number = 50;
     private readonly MAX_TITLE_HEIGHT = 75;
@@ -57,8 +55,7 @@ export class Timeline extends React.Component<FieldViewProps> {
     @observable private _roundToggleRef = React.createRef<HTMLDivElement>();
     @observable private _roundToggleContainerRef = React.createRef<HTMLDivElement>();
 
-
-    //boolean vars and instance vars 
+    //boolean vars and instance vars
     @observable private _currentBarX: number = 0;
     @observable private _windSpeed: number = 1;
     @observable private _isPlaying: boolean = false; //scrubber playing
@@ -73,13 +70,13 @@ export class Timeline extends React.Component<FieldViewProps> {
     @observable private _titleHeight = 0;
 
     /**
-     * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit. 
+     * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit.
      */
     @computed
     private get children(): Doc[] {
         const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this.props.Document.type) as any);
         if (annotatedDoc) {
-            return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + "-annotations"]);
+            return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + '-annotations']);
         }
         return DocListCast(this.props.Document[this.props.fieldKey]);
     }
@@ -91,7 +88,8 @@ export class Timeline extends React.Component<FieldViewProps> {
         this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //check if relHeight is less than Maxheight. Else, just set relheight to max
         this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; //offset
         this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; //twice the titleheight + offset
-        if (!this.props.Document.AnimationLength) { //if animation length did not exist
+        if (!this.props.Document.AnimationLength) {
+            //if animation length did not exist
             this.props.Document.AnimationLength = this._time; //set it to default time
         } else {
             this._time = NumCast(this.props.Document.AnimationLength); //else, set time to animationlength stored from before
@@ -116,24 +114,29 @@ export class Timeline extends React.Component<FieldViewProps> {
     drawTicks = () => {
         const ticks = [];
         for (let i = 0; i < this._time / this._tickIncrement; i++) {
-            ticks.push(<div key={Utils.GenerateGuid()} className="tick" style={{ transform: `translate(${i * this._tickSpacing}px)`, position: "absolute", pointerEvents: "none" }}> <p className="number-label">{this.toReadTime(i * this._tickIncrement)}</p></div>);
+            ticks.push(
+                <div key={Utils.GenerateGuid()} className="tick" style={{ transform: `translate(${i * this._tickSpacing}px)`, position: 'absolute', pointerEvents: 'none' }}>
+                    {' '}
+                    <p className="number-label">{this.toReadTime(i * this._tickIncrement)}</p>
+                </div>
+            );
         }
         return ticks;
-    }
+    };
 
     /**
      * changes the scrubber to actual pixel position
      */
     @action
     changeCurrentBarX = (pixel: number) => {
-        pixel <= 0 ? this._currentBarX = 0 : pixel >= this._totalLength ? this._currentBarX = this._totalLength : this._currentBarX = pixel;
-    }
+        pixel <= 0 ? (this._currentBarX = 0) : pixel >= this._totalLength ? (this._currentBarX = this._totalLength) : (this._currentBarX = pixel);
+    };
 
     //for playing
     onPlay = (e: React.MouseEvent) => {
         e.stopPropagation();
         this.play();
-    }
+    };
 
     /**
      * when playbutton is clicked
@@ -149,8 +152,7 @@ export class Timeline extends React.Component<FieldViewProps> {
         this._isPlaying = !this._isPlaying;
         this._playButton = this._isPlaying ? faPauseCircle : faPlayCircle;
         this._isPlaying && playTimeline();
-    }
-
+    };
 
     /**
      * fast forward the timeline scrubbing
@@ -159,30 +161,32 @@ export class Timeline extends React.Component<FieldViewProps> {
     windForward = (e: React.MouseEvent) => {
         e.preventDefault();
         e.stopPropagation();
-        if (this._windSpeed < 64) { //max speed is 32 
+        if (this._windSpeed < 64) {
+            //max speed is 32
             this._windSpeed = this._windSpeed * 2;
         }
-    }
+    };
 
     /**
-     * rewind the timeline scrubbing 
+     * rewind the timeline scrubbing
      */
     @action
     windBackward = (e: React.MouseEvent) => {
         e.preventDefault();
         e.stopPropagation();
-        if (this._windSpeed > 1 / 16) { // min speed is 1/8
+        if (this._windSpeed > 1 / 16) {
+            // min speed is 1/8
             this._windSpeed = this._windSpeed / 2;
         }
-    }
+    };
 
     /**
-     * scrubber down 
+     * scrubber down
      */
     @action
     onScrubberDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.onScrubberMove, emptyFunction, emptyFunction);
-    }
+    };
 
     /**
      * when there is any scrubber movement
@@ -194,16 +198,15 @@ export class Timeline extends React.Component<FieldViewProps> {
         const offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale;
         this.changeCurrentBarX(offsetX + this._visibleStart); //changes scrubber to clicked scrubber position
         return false;
-    }
+    };
 
     /**
      * when panning the timeline (in editing mode)
      */
     @action
     onPanDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, (e) =>
-            this.changeCurrentBarX(this._trackbox.current!.scrollLeft + e.clientX - this._trackbox.current!.getBoundingClientRect().left));
-    }
+        setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, e => this.changeCurrentBarX(this._trackbox.current!.scrollLeft + e.clientX - this._trackbox.current!.getBoundingClientRect().left));
+    };
 
     /**
      * when moving the timeline (in editing mode)
@@ -218,29 +221,34 @@ export class Timeline extends React.Component<FieldViewProps> {
         if (this._visibleStart + this._visibleLength + 20 >= this._totalLength) {
             this._visibleStart -= e.movementX;
             this._totalLength -= e.movementX;
-            this._time -= KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this._tickSpacing, this._tickIncrement);
+            this._time -= KeyframeFunc.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
             this.props.Document.AnimationLength = this._time;
         }
         return false;
-    }
-
+    };
 
     @action
     movePanX = (pixel: number) => {
         this._infoContainer.current!.scrollLeft = pixel;
         this._visibleStart = this._infoContainer.current!.scrollLeft;
-    }
+    };
 
     /**
      * resizing timeline (in editing mode) (the hamburger drag icon)
      */
     onResizeDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, action((e) => {
-            const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
-            this._containerHeight = clamp(this.MIN_CONTAINER_HEIGHT, this._containerHeight + offset, this.MAX_CONTAINER_HEIGHT);
-            return false;
-        }), emptyFunction, emptyFunction);
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            action(e => {
+                const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
+                this._containerHeight = clamp(this.MIN_CONTAINER_HEIGHT, this._containerHeight + offset, this.MAX_CONTAINER_HEIGHT);
+                return false;
+            }),
+            emptyFunction,
+            emptyFunction
+        );
+    };
 
     /**
      * for displaying time to standard min:sec
@@ -251,19 +259,18 @@ export class Timeline extends React.Component<FieldViewProps> {
         const inSeconds = Math.round(time * 100) / 100;
 
         const min = Math.floor(inSeconds / 60);
-        const sec = (Math.round((inSeconds % 60) * 100) / 100);
+        const sec = Math.round((inSeconds % 60) * 100) / 100;
         let secString = sec.toFixed(2);
 
         if (Math.floor(sec / 10) === 0) {
-            secString = "0" + secString;
+            secString = '0' + secString;
         }
 
         return `${min}:${secString}`;
-    }
-
+    };
 
     /**
-     * timeline zoom function 
+     * timeline zoom function
      * use mouse middle button to zoom in/out the timeline
      */
     @action
@@ -271,17 +278,16 @@ export class Timeline extends React.Component<FieldViewProps> {
         e.preventDefault();
         e.stopPropagation();
         const offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left;
-        const prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement);
-        const prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement);
+        const prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+        const prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
         this.zoom(e.deltaY < 0);
-        const currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement);
-        const currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement);
+        const currPixel = KeyframeFunc.convertPixelTime(prevTime, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+        const currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
         this._infoContainer.current!.scrollLeft = currPixel - offset;
         this._visibleStart = currPixel - offset > 0 ? currPixel - offset : 0;
         this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) : 0;
         this.changeCurrentBarX(currCurrent);
-    }
-
+    };
 
     resetView(doc: Doc) {
         doc._panX = doc._customOriginX ?? 0;
@@ -324,7 +330,7 @@ export class Timeline extends React.Component<FieldViewProps> {
             this._tickSpacing = spacingChange;
             this._tickIncrement = incrementChange;
         }
-    }
+    };
 
     /**
      * tool box includes the toggle buttons at the top of the timeline (both editing mode and play mode)
@@ -333,60 +339,90 @@ export class Timeline extends React.Component<FieldViewProps> {
         const size = 40 * scale; //50 is default
         const iconSize = 25;
         const width: number = this.props.PanelWidth();
-        const modeType = this.props.Document.isATOn ? "Author" : "Play";
+        const modeType = this.props.Document.isATOn ? 'Author' : 'Play';
 
         //decides if information should be omitted because the timeline is very small
         // if its less than 950 pixels then it's going to be overlapping
-        let modeString = modeType, overviewString = "", lengthString = "";
+        let modeString = modeType,
+            overviewString = '',
+            lengthString = '';
         if (width < 850) {
-            modeString = "Mode: " + modeType;
-            overviewString = "Overview:";
-            lengthString = "Length: ";
+            modeString = 'Mode: ' + modeType;
+            overviewString = 'Overview:';
+            lengthString = 'Length: ';
         }
 
         return (
             <div key="timeline_toolbox" className="timeline-toolbox" style={{ height: `${size}px` }}>
                 <div className="playbackControls">
-                    <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
-                    <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
-                    <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
+                    <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation">
+                        {' '}
+                        <FontAwesomeIcon icon={faBackward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} />{' '}
+                    </div>
+                    <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause">
+                        {' '}
+                        <FontAwesomeIcon icon={this._playButton as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} />{' '}
+                    </div>
+                    <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation">
+                        {' '}
+                        <FontAwesomeIcon icon={faForward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} />{' '}
+                    </div>
                 </div>
                 <div className="grid-box overview-tool">
                     <div className="overview-box">
-                        <div key="overview-text" className="animation-text">{overviewString}</div>
-                        <TimelineOverview tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} time={this._time} parent={this} isAuthoring={BoolCast(this.props.Document.isATOn)} currentBarX={this._currentBarX} totalLength={this._totalLength} visibleLength={this._visibleLength} visibleStart={this._visibleStart} changeCurrentBarX={this.changeCurrentBarX} movePanX={this.movePanX} />
+                        <div key="overview-text" className="animation-text">
+                            {overviewString}
+                        </div>
+                        <TimelineOverview
+                            tickSpacing={this._tickSpacing}
+                            tickIncrement={this._tickIncrement}
+                            time={this._time}
+                            parent={this}
+                            isAuthoring={BoolCast(this.props.Document.isATOn)}
+                            currentBarX={this._currentBarX}
+                            totalLength={this._totalLength}
+                            visibleLength={this._visibleLength}
+                            visibleStart={this._visibleStart}
+                            changeCurrentBarX={this.changeCurrentBarX}
+                            movePanX={this.movePanX}
+                        />
                     </div>
                     <div className="mode-box overview-tool">
-                        <div key="animation-text" className="animation-text">{modeString}</div>
+                        <div key="animation-text" className="animation-text">
+                            {modeString}
+                        </div>
                         <div key="round-toggle" ref={this._roundToggleContainerRef} className="round-toggle">
-                            <div key="round-toggle-slider" ref={this._roundToggleRef} className="round-toggle-slider" onPointerDown={this.toggleChecked}> </div>
+                            <div key="round-toggle-slider" ref={this._roundToggleRef} className="round-toggle-slider" onPointerDown={this.toggleChecked}>
+                                {' '}
+                            </div>
                         </div>
                     </div>
-                    <div className="time-box overview-tool" style={{ display: "flex" }}>
+                    <div className="time-box overview-tool" style={{ display: 'flex' }}>
                         {this.timeIndicator(lengthString, totalTime)}
-                        <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this.props.Document)}><FontAwesomeIcon icon="compress-arrows-alt" size="lg" /></div>
-                        <div className="resetView-tool" style={{ display: this.props.Document.isATOn ? "flex" : "none" }} title="Set Default View" onClick={() => this.setView(this.props.Document)}><FontAwesomeIcon icon="expand-arrows-alt" size="lg" /></div>
+                        <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this.props.Document)}>
+                            <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
+                        </div>
+                        <div className="resetView-tool" style={{ display: this.props.Document.isATOn ? 'flex' : 'none' }} title="Set Default View" onClick={() => this.setView(this.props.Document)}>
+                            <FontAwesomeIcon icon="expand-arrows-alt" size="lg" />
+                        </div>
                     </div>
                 </div>
             </div>
         );
-    }
+    };
 
     timeIndicator(lengthString: string, totalTime: number) {
         if (this.props.Document.isATOn) {
-            return (
-                <div key="time-text" className="animation-text" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden", display: this.props.Document.isATOn ? "flex" : "none" }}>{`Total: ${this.toReadTime(totalTime)}`}</div>
-            );
-        }
-        else {
+            return <div key="time-text" className="animation-text" style={{ visibility: this.props.Document.isATOn ? 'visible' : 'hidden', display: this.props.Document.isATOn ? 'flex' : 'none' }}>{`Total: ${this.toReadTime(totalTime)}`}</div>;
+        } else {
             const ctime = `Current: ${this.getCurrentTime()}`;
             const ttime = `Total: ${this.toReadTime(this._time)}`;
             return (
-                <div style={{ flexDirection: "column" }}>
-                    <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
+                <div style={{ flexDirection: 'column' }}>
+                    <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this.props.Document.isATOn ? 'block' : 'none' }}>
                         {ctime}
                     </div>
-                    <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
+                    <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this.props.Document.isATOn ? 'block' : 'none' }}>
                         {ttime}
                     </div>
                 </div>
@@ -402,7 +438,7 @@ export class Timeline extends React.Component<FieldViewProps> {
         e.preventDefault();
         e.stopPropagation();
         this.toggleHandle();
-    }
+    };
 
     /**
      * turns on the toggle button (the purple slide button that changes from editing mode and play mode
@@ -415,23 +451,22 @@ export class Timeline extends React.Component<FieldViewProps> {
         this.props.Document.isATOn = !this.props.Document.isATOn;
         if (!BoolCast(this.props.Document.isATOn)) {
             //turning on playmode...
-            roundToggle.style.transform = "translate(0px, 0px)";
-            roundToggle.style.animationName = "turnoff";
-            roundToggleContainer.style.animationName = "turnoff";
-            roundToggleContainer.style.backgroundColor = "white";
+            roundToggle.style.transform = 'translate(0px, 0px)';
+            roundToggle.style.animationName = 'turnoff';
+            roundToggleContainer.style.animationName = 'turnoff';
+            roundToggleContainer.style.backgroundColor = 'white';
             timelineContainer.style.top = `${-this._containerHeight}px`;
             this.toPlay();
         } else {
             //turning on authoring mode...
-            roundToggle.style.transform = "translate(20px, 0px)";
-            roundToggle.style.animationName = "turnon";
-            roundToggleContainer.style.animationName = "turnon";
-            roundToggleContainer.style.backgroundColor = "#9acedf";
-            timelineContainer.style.top = "0px";
+            roundToggle.style.transform = 'translate(20px, 0px)';
+            roundToggle.style.animationName = 'turnon';
+            roundToggleContainer.style.animationName = 'turnon';
+            roundToggleContainer.style.backgroundColor = '#9acedf';
+            timelineContainer.style.top = '0px';
             this.toAuthoring();
         }
-    }
-
+    };
 
     @action.bound
     changeLengths() {
@@ -443,9 +478,9 @@ export class Timeline extends React.Component<FieldViewProps> {
 
     // @computed
     getCurrentTime = () => {
-        const current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement);
+        const current = KeyframeFunc.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
         return this.toReadTime(current > this._time ? this._time : current);
-    }
+    };
 
     @observable private mapOfTracks: (Track | null)[] = [];
 
@@ -465,22 +500,22 @@ export class Timeline extends React.Component<FieldViewProps> {
             }
         });
         return longestTime;
-    }
+    };
 
     @action
     toAuthoring = () => {
         this._time = Math.ceil((this.findLongestTime() ?? 1) / 100000) * 100000;
-        this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement);
-    }
+        this._totalLength = KeyframeFunc.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+    };
 
     @action
     toPlay = () => {
         this._time = this.findLongestTime();
-        this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement);
-    }
+        this._totalLength = KeyframeFunc.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+    };
 
     /**
-     * if you have any question here, just shoot me an email or text. 
+     * if you have any question here, just shoot me an email or text.
      * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region)
      */
     render() {
@@ -488,23 +523,46 @@ export class Timeline extends React.Component<FieldViewProps> {
 
         // change visible and total width
         return (
-            <div style={{ visibility: "visible" }}>
-                <div key="timeline_wrapper" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden", left: "0px", top: "0px", position: "absolute", width: "100%", transform: "translate(0px, 0px)" }}>
+            <div style={{ visibility: 'visible' }}>
+                <div key="timeline_wrapper" style={{ visibility: this.props.Document.isATOn ? 'visible' : 'hidden', left: '0px', top: '0px', position: 'absolute', width: '100%', transform: 'translate(0px, 0px)' }}>
                     <div key="timeline_container" className="timeline-container" ref={this._timelineContainer} style={{ height: `${this._containerHeight}px`, top: `0px` }}>
                         <div key="timeline_info" className="info-container" onPointerDown={this.onPanDown} ref={this._infoContainer} onWheel={this.onWheelZoom}>
                             {this.drawTicks()}
                             <div key="timeline_scrubber" className="scrubber" style={{ transform: `translate(${this._currentBarX}px)` }}>
-                                <div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown} ></div>
+                                <div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown}></div>
                             </div>
                             <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}>
-                                {this.children.map(doc =>
-                                    <Track ref={ref => this.mapOfTracks.push(ref)} node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} transform={this.props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} collection={this.props.Document} timelineVisible={true} />
-                                )}
+                                {this.children.map(doc => (
+                                    <Track
+                                        ref={ref => this.mapOfTracks.push(ref)}
+                                        node={doc}
+                                        currentBarX={this._currentBarX}
+                                        changeCurrentBarX={this.changeCurrentBarX}
+                                        transform={this.props.ScreenToLocalTransform()}
+                                        time={this._time}
+                                        tickSpacing={this._tickSpacing}
+                                        tickIncrement={this._tickIncrement}
+                                        collection={this.props.Document}
+                                        timelineVisible={true}
+                                    />
+                                ))}
                             </div>
                         </div>
                         <div className="currentTime">Current: {this.getCurrentTime()}</div>
                         <div key="timeline_title" className="title-container" ref={this._titleContainer}>
-                            {this.children.map(doc => <div style={{ height: `${(this._titleHeight)}px` }} className="datapane" onPointerOver={() => { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}><p>{doc.title}</p></div>)}
+                            {this.children.map(doc => (
+                                <div
+                                    style={{ height: `${this._titleHeight}px` }}
+                                    className="datapane"
+                                    onPointerOver={() => {
+                                        Doc.BrushDoc(doc);
+                                    }}
+                                    onPointerOut={() => {
+                                        Doc.UnBrushDoc(doc);
+                                    }}>
+                                    <p>{StrCast(doc.title)}</p>
+                                </div>
+                            ))}
                         </div>
                         <div key="timeline_resize" onPointerDown={this.onResizeDown}>
                             <FontAwesomeIcon className="resize" icon={faGripLines as IconLookup} />
@@ -515,4 +573,4 @@ export class Timeline extends React.Component<FieldViewProps> {
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 37589974b..7d40cab8c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,37 +1,36 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { CursorProperty } from "csstype";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
-import { Docs, DocUtils } from "../../documents/Documents";
-import { DragManager, dropActionType } from "../../util/DragManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { EditableView } from "../EditableView";
-import { LightboxView } from "../LightboxView";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
-import { StyleProp } from "../StyleProvider";
-import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
-import "./CollectionStackingView.scss";
-import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
-import { CollectionSubView } from "./CollectionSubView";
-import { CollectionViewType } from "./CollectionView";
-import { FieldViewProps } from "../nodes/FieldView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
-const _global = (window /* browser */ || global /* node */) as any;
-
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { CursorProperty } from 'csstype';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DragManager, dropActionType } from '../../util/DragManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch } from '../../util/UndoManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { EditableView } from '../EditableView';
+import { LightboxView } from '../LightboxView';
+import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView';
+import { StyleProp } from '../StyleProvider';
+import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
+import './CollectionStackingView.scss';
+import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
+import { CollectionSubView } from './CollectionSubView';
+import { CollectionViewType } from './CollectionView';
+import { FieldViewProps } from '../nodes/FieldView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionStackingViewProps = {
     chromeHidden?: boolean;
@@ -46,27 +45,50 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     _draggerRef = React.createRef<HTMLDivElement>();
     _pivotFieldDisposer?: IReactionDisposer;
     _autoHeightDisposer?: IReactionDisposer;
-    _docXfs: { height: () => number, width: () => number, stackedDocTransform: () => Transform }[] = [];
+    _docXfs: { height: () => number; width: () => number; stackedDocTransform: () => Transform }[] = [];
     _columnStart: number = 0;
     @observable _heightMap = new Map<string, number>();
-    @observable _cursor: CursorProperty = "grab";
+    @observable _cursor: CursorProperty = 'grab';
     @observable _scroll = 0; // used to force the document decoration to update when scrolling
-    @computed get chromeHidden() { return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); }
-    @computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null); }
-    @computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
-    @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => (pair.layout instanceof Doc) && !pair.layout.hidden).map(pair => pair.layout); }
-    @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); }
-    @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
-    @computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); }
-    @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
-    @computed get isStackingView() { return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking; }
-    @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
-    @computed get showAddAGroup() { return this.pivotField && !this.chromeHidden; }
+    @computed get chromeHidden() {
+        return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
+    }
+    @computed get columnHeaders() {
+        return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null);
+    }
+    @computed get pivotField() {
+        return StrCast(this.layoutDoc._pivotField);
+    }
+    @computed get filteredChildren() {
+        return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
+    }
+    @computed get headerMargin() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
+    }
+    @computed get xMargin() {
+        return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
+    }
+    @computed get yMargin() {
+        return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5);
+    } // 2 * this.gridGap)); }
+    @computed get gridGap() {
+        return NumCast(this.layoutDoc._gridGap, 10);
+    }
+    @computed get isStackingView() {
+        return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking;
+    }
+    @computed get numGroupColumns() {
+        return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1;
+    }
+    @computed get showAddAGroup() {
+        return this.pivotField && !this.chromeHidden;
+    }
     @computed get columnWidth() {
-        return Math.min(this.props.PanelWidth() - 2 * this.xMargin,
-            this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
+        return Math.min(this.props.PanelWidth() - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
+    }
+    @computed get NodeWidth() {
+        return this.props.PanelWidth() - this.gridGap;
     }
-    @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
 
     constructor(props: any) {
         super(props);
@@ -84,21 +106,23 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const width = () => this.getDocWidth(d);
             const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
             const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
-            return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} style={style} >
-                {this.getDisplayDoc(d, width)}
-            </div>;
+            return (
+                <div className={`collectionStackingView-${this.isStackingView ? 'columnDoc' : 'masonryDoc'}`} key={d[Id]} style={style}>
+                    {this.getDisplayDoc(d, width)}
+                </div>
+            );
         });
-    }
+    };
     @action
     setDocHeight = (key: string, sectionHeight: number) => {
         this._heightMap.set(key, sectionHeight);
-    }
+    };
 
     get Sections() {
         if (!this.pivotField || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
 
         if (this.columnHeaders === undefined) {
-            setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>(), 0);
+            setTimeout(() => (this.layoutDoc._columnHeaders = new List<SchemaHeaderField>()), 0);
             return new Map<SchemaHeaderField, Doc[]>();
         }
         const columnHeaders = Array.from(this.columnHeaders);
@@ -114,8 +138,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
             if (existingHeader) {
                 fields.get(existingHeader)!.push(d);
-            }
-            else {
+            } else {
                 const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
                 fields.set(newSchemaHeader, [d]);
                 columnHeaders.push(newSchemaHeader);
@@ -124,13 +147,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         });
         // remove all empty columns if hideHeadings is set
         if (this.layoutDoc._columnsHideIfEmpty) {
-            Array.from(fields.keys()).filter(key => !fields.get(key)!.length).map(header => {
-                fields.delete(header);
-                columnHeaders.splice(columnHeaders.indexOf(header), 1);
-                changed = true;
-            });
+            Array.from(fields.keys())
+                .filter(key => !fields.get(key)!.length)
+                .map(header => {
+                    fields.delete(header);
+                    columnHeaders.splice(columnHeaders.indexOf(header), 1);
+                    changed = true;
+                });
         }
-        changed && setTimeout(action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)), 0);
+        changed &&
+            setTimeout(
+                action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)),
+                0
+            );
         return fields;
     }
 
@@ -140,13 +169,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         // reset section headers when a new filter is inputted
         this._pivotFieldDisposer = reaction(
             () => this.pivotField,
-            () => this.layoutDoc._columnHeaders = new List()
+            () => (this.layoutDoc._columnHeaders = new List())
+        );
+        this._autoHeightDisposer = reaction(
+            () => this.layoutDoc._autoHeight,
+            autoHeight =>
+                autoHeight &&
+                this.props.setHeight?.(
+                    Math.min(
+                        NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
+                        this.headerMargin + (this.isStackingView ? Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))) : this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0))
+                    )
+                )
         );
-        this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight,
-            autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
-                this.headerMargin + (this.isStackingView ?
-                    Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))) :
-                    this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0)))));
     }
 
     componentWillUnmount() {
@@ -158,45 +193,50 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     @action
     moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => {
         return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
-    }
+    };
     createRef = (ele: HTMLDivElement | null) => {
         this._masonryGridRef = ele;
         this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
-    }
+    };
 
-    @computed get onChildClickHandler() { return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
-    @computed get onChildDoubleClickHandler() { return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); }
+    @computed get onChildClickHandler() {
+        return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
+    }
+    @computed get onChildDoubleClickHandler() {
+        return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
+    }
 
     addDocTab = (doc: Doc, where: string) => {
-        if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+        if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
             this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
             return true;
         }
         return this.props.addDocTab(doc, where);
-    }
+    };
 
     scrollToBottom = () => {
         smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight);
-    }
+    };
 
     focusDocument = (doc: Doc, options?: DocFocusOptions) => {
         Doc.BrushDoc(doc);
 
         let focusSpeed = 0;
-        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName("documentView-node")).find((node: any) => node.id === doc[Id]);
+        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
         if (found) {
             const top = found.getBoundingClientRect().top;
             const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
             if (Math.floor(localTop[1]) !== 0) {
-                smoothScroll(focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500, this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+                smoothScroll((focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
             }
         }
         const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
         this.props.focus(this.rootDoc, {
-            willZoom: options?.willZoom, scale: options?.scale, afterFocus: (didFocus: boolean) =>
-                new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed))
+            willZoom: options?.willZoom,
+            scale: options?.scale,
+            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
         });
-    }
+    };
 
     styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
         if (property === StyleProp.Opacity && doc) {
@@ -208,85 +248,88 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             }
         }
         return this.props.styleProvider?.(doc, props, property);
-    }
+    };
     @undoBatch
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && ["Enter"].includes(e.key) && e.ctrlKey) {
+        if (docView && ['Enter'].includes(e.key) && e.ctrlKey) {
             e.stopPropagation?.();
-            const below = !e.altKey && e.key !== "Tab";
+            const below = !e.altKey && e.key !== 'Tab';
             const layoutKey = StrCast(docView.LayoutFieldKey);
             const newDoc = Doc.MakeCopy(docView.rootDoc, true);
             const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
             newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) {
+            if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
                 newDoc[layoutKey] = docView.rootDoc[layoutKey];
             }
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
             return this.addDocument?.(newDoc);
         }
-    }
+    };
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined;
+    isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined);
     getDisplayDoc(doc: Doc, width: () => number) {
-        const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc;
+        const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
         const height = () => this.getDocHeight(doc);
 
         let dref: Opt<DocumentView>;
         const stackedDocTransform = () => this.getDocTransform(doc, dref);
         this._docXfs.push({ stackedDocTransform, width, height });
-        return <DocumentView ref={r => dref = r || undefined}
-            Document={doc}
-            DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
-            renderDepth={this.props.renderDepth + 1}
-            PanelWidth={width}
-            PanelHeight={height}
-            styleProvider={this.styleProvider}
-            docViewPath={this.props.docViewPath}
-            fitWidth={this.props.childFitWidth}
-            isContentActive={this.isChildContentActive}
-            onKey={this.onKeyDown}
-            isDocumentActive={this.isContentActive}
-            LayoutTemplate={this.props.childLayoutTemplate}
-            LayoutTemplateString={this.props.childLayoutString}
-            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}
-            dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
-            rootSelected={this.rootSelected}
-            showTitle={this.props.childShowTitle}
-            dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
-            onClick={this.onChildClickHandler}
-            onDoubleClick={this.onChildDoubleClickHandler}
-            ScreenToLocalTransform={stackedDocTransform}
-            focus={this.focusDocument}
-            docFilters={this.childDocFilters}
-            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            hideResizeHandles={this.props.childHideResizeHandles?.()}
-            hideTitle={this.props.childHideTitle?.()}
-            docRangeFilters={this.childDocRangeFilters}
-            searchFilterDocs={this.searchFilterDocs}
-            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-            ContainingCollectionView={this.props.CollectionView}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
-            whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-            addDocTab={this.addDocTab}
-            bringToFront={returnFalse}
-            scriptContext={this.props.scriptContext}
-            pinToPres={this.props.pinToPres}
-        />;
+        return (
+            <DocumentView
+                ref={r => (dref = r || undefined)}
+                Document={doc}
+                DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
+                renderDepth={this.props.renderDepth + 1}
+                PanelWidth={width}
+                PanelHeight={height}
+                styleProvider={this.styleProvider}
+                docViewPath={this.props.docViewPath}
+                fitWidth={this.props.childFitWidth}
+                isContentActive={this.isChildContentActive}
+                onKey={this.onKeyDown}
+                isDocumentActive={this.isContentActive}
+                LayoutTemplate={this.props.childLayoutTemplate}
+                LayoutTemplateString={this.props.childLayoutString}
+                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}
+                dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
+                rootSelected={this.rootSelected}
+                showTitle={this.props.childShowTitle}
+                dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+                onClick={this.onChildClickHandler}
+                onDoubleClick={this.onChildDoubleClickHandler}
+                ScreenToLocalTransform={stackedDocTransform}
+                focus={this.focusDocument}
+                docFilters={this.childDocFilters}
+                hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+                hideResizeHandles={this.props.childHideResizeHandles?.()}
+                hideTitle={this.props.childHideTitle?.()}
+                docRangeFilters={this.childDocRangeFilters}
+                searchFilterDocs={this.searchFilterDocs}
+                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                ContainingCollectionView={this.props.CollectionView}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
+                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                addDocTab={this.addDocTab}
+                bringToFront={returnFalse}
+                scriptContext={this.props.scriptContext}
+                pinToPres={this.props.pinToPres}
+            />
+        );
     }
 
     getDocTransform(doc: Doc, dref?: DocumentView) {
         const y = this._scroll; // required for document decorations to update when the text box container is scrolled
         const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
-        // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off 
-        return new Transform(- translateX + (dref?.centeringX || 0), - translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
+        // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off
+        return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
     }
     getDocWidth(d?: Doc) {
         if (!d) return 0;
@@ -300,37 +343,42 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     getDocHeight(d?: Doc) {
         if (!d || d.hidden) return 0;
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
-        const childDataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc;
-        const maxHeight = (lim => lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim)(NumCast(this.layoutDoc.childLimitHeight, -1));
+        const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS ? undefined : this.props.DataDoc;
+        const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
         const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
         const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
         if (nw && nh) {
             const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
-            return Math.min(
-                maxHeight,
-                docWid * nh / nw);
+            return Math.min(maxHeight, (docWid * nh) / nw);
         }
         const childHeight = NumCast(childLayoutDoc._height);
-        const panelHeight = (childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+        const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
     columnDividerDown = (e: React.PointerEvent) => {
-        runInAction(() => this._cursor = "grabbing");
-        setupMoveUpEvents(this, e, this.onDividerMove, action(() => this._cursor = "grab"), emptyFunction);
-    }
+        runInAction(() => (this._cursor = 'grabbing'));
+        setupMoveUpEvents(
+            this,
+            e,
+            this.onDividerMove,
+            action(() => (this._cursor = 'grab')),
+            emptyFunction
+        );
+    };
     @action
     onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
         this.layoutDoc._columnWidth = Math.max(10, this.columnWidth + delta[0]);
         return false;
-    }
+    };
 
     @computed get columnDragger() {
-        return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef}
-            style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }} >
-            <FontAwesomeIcon icon={"arrows-alt-h"} />
-        </div>;
+        return (
+            <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
+                <FontAwesomeIcon icon={'arrows-alt-h'} />
+            </div>
+        );
     }
 
     @undoBatch
@@ -341,7 +389,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         let dropAfter = 0;
         if (de.complete.docDragData) {
             this._docXfs.map((cd, i) => {
-                const pos = cd.stackedDocTransform().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+                const pos = cd
+                    .stackedDocTransform()
+                    .inverse()
+                    .transformPoint(-2 * this.gridGap, -2 * this.gridGap);
                 const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height());
                 if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && (i === this._docXfs.length - 1 || where[1] < pos1[1])) {
                     dropInd = i;
@@ -358,21 +409,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 DragManager.docsBeingDragged = [];
                 if (docs && newDocs.length) {
                     const insertInd = dropInd === -1 ? docs.length : dropInd + dropAfter;
-                    const offset = newDocs.reduce((off, ndoc) => this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off, 0);
+                    const offset = newDocs.reduce((off, ndoc) => (this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off), 0);
                     newDocs.filter(ndoc => docs.indexOf(ndoc) !== -1).forEach(ndoc => docs.splice(docs.indexOf(ndoc), 1));
                     docs.splice(insertInd - offset, 0, ...newDocs);
                 }
             }
-        }
-        else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
-            const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, _fitWidth: true, title: "dropped annotation" });
+        } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed
             e.stopPropagation();
-        }
-        else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
+        } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
         return false;
-    }
+    };
 
     @undoBatch
     internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData) {
@@ -390,7 +439,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         const where = [e.clientX, e.clientY];
         let targInd = -1;
         this._docXfs.map((cd, i) => {
-            const pos = cd.stackedDocTransform().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+            const pos = cd
+                .stackedDocTransform()
+                .inverse()
+                .transformPoint(-2 * this.gridGap, -2 * this.gridGap);
             const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height());
             if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
                 targInd = i;
@@ -406,98 +458,103 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 }
             }
         });
-    }
+    };
     headings = () => Array.from(this.Sections);
     refList: any[] = [];
     sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
         const key = this.pivotField;
-        let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+        let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined = undefined;
         if (this.pivotField) {
             const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
             if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
                 type = types[0];
             }
         }
-        return <CollectionStackingViewFieldColumn
-            unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
-            observeHeight={ref => {
-                if (ref) {
-                    this.refList.push(ref);
-                    this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
-                            const height = this.headerMargin +
-                                Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
-                                    Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
-                            if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
-                                this.props.setHeight?.(height);
-                            }
-                        }
-                    }));
-                    this.observer.observe(ref);
-                }
-            }}
-            addDocument={this.addDocument}
-            chromeHidden={this.chromeHidden}
-            columnHeaders={this.columnHeaders}
-            Document={this.props.Document}
-            DataDoc={this.props.DataDoc}
-            renderChildren={this.children}
-            columnWidth={this.columnWidth}
-            numGroupColumns={this.numGroupColumns}
-            gridGap={this.gridGap}
-            pivotField={this.pivotField}
-            key={heading?.heading ?? ""}
-            headings={this.headings}
-            heading={heading?.heading ?? ""}
-            headingObject={heading}
-            docList={docList}
-            yMargin={this.yMargin}
-            type={type}
-            createDropTarget={this.createDashEventsTarget}
-            screenToLocalTransform={this.props.ScreenToLocalTransform}
-        />;
-    }
+        return (
+            <CollectionStackingViewFieldColumn
+                unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
+                observeHeight={ref => {
+                    if (ref) {
+                        this.refList.push(ref);
+                        this.observer = new _global.ResizeObserver(
+                            action((entries: any) => {
+                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                    const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
+                                    if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
+                                        this.props.setHeight?.(height);
+                                    }
+                                }
+                            })
+                        );
+                        this.observer.observe(ref);
+                    }
+                }}
+                addDocument={this.addDocument}
+                chromeHidden={this.chromeHidden}
+                columnHeaders={this.columnHeaders}
+                Document={this.props.Document}
+                DataDoc={this.props.DataDoc}
+                renderChildren={this.children}
+                columnWidth={this.columnWidth}
+                numGroupColumns={this.numGroupColumns}
+                gridGap={this.gridGap}
+                pivotField={this.pivotField}
+                key={heading?.heading ?? ''}
+                headings={this.headings}
+                heading={heading?.heading ?? ''}
+                headingObject={heading}
+                docList={docList}
+                yMargin={this.yMargin}
+                type={type}
+                createDropTarget={this.createDashEventsTarget}
+                screenToLocalTransform={this.props.ScreenToLocalTransform}
+            />
+        );
+    };
 
     sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => {
         const key = this.pivotField;
-        let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+        let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined = undefined;
         const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
         if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
             type = types[0];
         }
-        const rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length,
-            Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
-        return <CollectionMasonryViewFieldRow
-            showHandle={first}
-            Document={this.props.Document}
-            chromeHidden={this.chromeHidden}
-            pivotField={this.pivotField}
-            unobserveHeight={(ref) => this.refList.splice(this.refList.indexOf(ref), 1)}
-            observeHeight={(ref) => {
-                if (ref) {
-                    this.refList.push(ref);
-                    this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
-                            const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
-                            this.props.setHeight?.(this.headerMargin + height);
-                        }
-                    }));
-                    this.observer.observe(ref);
-                }
-            }}
-            key={heading ? heading.heading : ""}
-            rows={rows}
-            headings={this.headings}
-            heading={heading ? heading.heading : ""}
-            headingObject={heading}
-            docList={docList}
-            parent={this}
-            type={type}
-            createDropTarget={this.createDashEventsTarget}
-            screenToLocalTransform={this.props.ScreenToLocalTransform}
-            setDocHeight={this.setDocHeight}
-        />;
-    }
+        const rows = () => (!this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))));
+        return (
+            <CollectionMasonryViewFieldRow
+                showHandle={first}
+                Document={this.props.Document}
+                chromeHidden={this.chromeHidden}
+                pivotField={this.pivotField}
+                unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
+                observeHeight={ref => {
+                    if (ref) {
+                        this.refList.push(ref);
+                        this.observer = new _global.ResizeObserver(
+                            action((entries: any) => {
+                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                    const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
+                                    this.props.setHeight?.(this.headerMargin + height);
+                                }
+                            })
+                        );
+                        this.observer.observe(ref);
+                    }
+                }}
+                key={heading ? heading.heading : ''}
+                rows={rows}
+                headings={this.headings}
+                heading={heading ? heading.heading : ''}
+                headingObject={heading}
+                docList={docList}
+                parent={this}
+                type={type}
+                createDropTarget={this.createDashEventsTarget}
+                screenToLocalTransform={this.props.ScreenToLocalTransform}
+                setDocHeight={this.setDocHeight}
+            />
+        );
+    };
 
     @action
     addGroup = (value: string) => {
@@ -507,25 +564,25 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             return true;
         }
         return false;
-    }
+    };
 
     sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
-        const descending = StrCast(this.layoutDoc._columnsSort) === "descending";
+        const descending = StrCast(this.layoutDoc._columnsSort) === 'descending';
         const firstEntry = descending ? b : a;
         const secondEntry = descending ? a : b;
         return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
-    }
+    };
 
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         if (!e.isPropagationStopped()) {
             const subItems: ContextMenuProps[] = [];
-            subItems.push({ description: `${this.layoutDoc._columnsFill ? "Variable Size" : "Autosize"} Column`, event: () => this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill, icon: "plus" });
-            subItems.push({ description: `${this.layoutDoc._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
-            subItems.push({ description: "Clear All", event: () => this.dataDoc.data = new List([]), icon: "times" });
-            ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
+            subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
+            subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+            subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
+            ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
         }
-    }
+    };
 
     @computed get renderedSections() {
         TraceMobx();
@@ -534,7 +591,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const entries = Array.from(this.Sections.entries());
             sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
         }
-        return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0));
+        return sections.map((section, i) => (this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0)));
     }
 
     @computed get buttonMenu() {
@@ -544,85 +601,90 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const width: number = NumCast(menuDoc._width, 30);
             const height: number = NumCast(menuDoc._height, 30);
             console.log(menuDoc.title, width, height);
-            return (<div className="buttonMenu-docBtn"
-                style={{ width: width, height: height }}>
-                <DocumentView
-                    Document={menuDoc}
-                    DataDoc={menuDoc}
-                    isContentActive={this.props.isContentActive}
-                    isDocumentActive={returnTrue}
-                    addDocument={this.props.addDocument}
-                    moveDocument={this.props.moveDocument}
-                    addDocTab={this.props.addDocTab}
-                    pinToPres={emptyFunction}
-                    rootSelected={this.props.isSelected}
-                    removeDocument={this.props.removeDocument}
-                    ScreenToLocalTransform={Transform.Identity}
-                    PanelWidth={() => 35}
-                    PanelHeight={() => 35}
-                    renderDepth={this.props.renderDepth}
-                    focus={emptyFunction}
-                    styleProvider={this.props.styleProvider}
-                    docViewPath={returnEmptyDoclist}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    bringToFront={emptyFunction}
-                    docFilters={this.props.docFilters}
-                    docRangeFilters={this.props.docRangeFilters}
-                    searchFilterDocs={this.props.searchFilterDocs}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
-                />
-            </div>
+            return (
+                <div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
+                    <DocumentView
+                        Document={menuDoc}
+                        DataDoc={menuDoc}
+                        isContentActive={this.props.isContentActive}
+                        isDocumentActive={returnTrue}
+                        addDocument={this.props.addDocument}
+                        moveDocument={this.props.moveDocument}
+                        addDocTab={this.props.addDocTab}
+                        pinToPres={emptyFunction}
+                        rootSelected={this.props.isSelected}
+                        removeDocument={this.props.removeDocument}
+                        ScreenToLocalTransform={Transform.Identity}
+                        PanelWidth={() => 35}
+                        PanelHeight={() => 35}
+                        renderDepth={this.props.renderDepth}
+                        focus={emptyFunction}
+                        styleProvider={this.props.styleProvider}
+                        docViewPath={returnEmptyDoclist}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        bringToFront={emptyFunction}
+                        docFilters={this.props.docFilters}
+                        docRangeFilters={this.props.docRangeFilters}
+                        searchFilterDocs={this.props.searchFilterDocs}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                    />
+                </div>
             );
         }
     }
 
+    @computed get nativeWidth() {
+        return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc);
+    }
+    @computed get nativeHeight() {
+        return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc);
+    }
 
-    @computed get nativeWidth() { return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc); }
-    @computed get nativeHeight() { return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc); }
-
-    @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; }
+    @computed get scaling() {
+        return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight;
+    }
 
-    @computed get backgroundEvents() { return SnappingManager.GetIsDragging(); }
+    @computed get backgroundEvents() {
+        return SnappingManager.GetIsDragging();
+    }
     observer: any;
     render() {
         TraceMobx();
         const editableViewProps = {
-            GetValue: () => "",
+            GetValue: () => '',
             SetValue: this.addGroup,
-            contents: "+ ADD A GROUP"
+            contents: '+ ADD A GROUP',
         };
         const buttonMenu = this.rootDoc.buttonMenu;
         const noviceExplainer = this.rootDoc.explainer;
         return (
             <>
-                {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
-                    {buttonMenu ? this.buttonMenu : null}
-                    {Doc.noviceMode && noviceExplainer ?
-                        <div className="documentExplanation">
-                            {noviceExplainer}
-                        </div>
-                        : null
-                    }
-                </div> : null}
-                <div className="collectionStackingMasonry-cont" >
-                    <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
+                {buttonMenu || noviceExplainer ? (
+                    <div className="documentButtonMenu">
+                        {buttonMenu ? this.buttonMenu : null}
+                        {Doc.noviceMode && noviceExplainer ? <div className="documentExplanation">{StrCast(noviceExplainer)}</div> : null}
+                    </div>
+                ) : null}
+                <div className="collectionStackingMasonry-cont">
+                    <div
+                        className={this.isStackingView ? 'collectionStackingView' : 'collectionMasonryView'}
                         ref={this.createRef}
                         style={{
-                            overflowY: this.props.isContentActive() ? "auto" : "hidden",
+                            overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
                             background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
-                            pointerEvents: this.backgroundEvents ? "all" : undefined
+                            pointerEvents: this.backgroundEvents ? 'all' : undefined,
                         }}
-                        onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
+                        onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
                         onDrop={this.onExternalDrop.bind(this)}
                         onContextMenu={this.onContextMenu}
-                        onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
+                        onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}>
                         {this.renderedSections}
-                        {!this.showAddAGroup ? (null) :
-                            <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
-                                style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
+                        {!this.showAddAGroup ? null : (
+                            <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" style={{ width: !this.isStackingView ? '100%' : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
                                 <EditableView {...editableViewProps} />
-                            </div>}
+                            </div>
+                        )}
                         {/* {this.chromeHidden || !this.props.isSelected() ? (null) :
                             <Switch
                                 onChange={this.onToggle}
@@ -634,7 +696,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     </div>
                 </div>
             </>
-
         );
     }
 }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ba72fb7b9..f5b9162d3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,5 +1,5 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
 import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
@@ -11,27 +11,27 @@ import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnF
 import { DocUtils } from '../../documents/Documents';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { EditableView } from "../EditableView";
+import { EditableView } from '../EditableView';
 import { DocumentView } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView } from "./CollectionSubView";
-import "./CollectionTreeView.scss";
-import { TreeView } from "./TreeView";
-import React = require("react");
-import { FieldViewProps } from "../nodes/FieldView";
-const _global = (window /* browser */ || global /* node */) as any;
+import { CollectionSubView } from './CollectionSubView';
+import './CollectionTreeView.scss';
+import { TreeView } from './TreeView';
+import React = require('react');
+import { FieldViewProps } from '../nodes/FieldView';
+const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionTreeViewProps = {
-    treeViewExpandedView?: "fields" | "layout" | "links" | "data";
+    treeViewExpandedView?: 'fields' | 'layout' | 'links' | 'data';
     treeViewOpen?: boolean;
     treeViewHideTitle?: boolean;
     treeViewHideHeaderFields?: boolean;
@@ -45,9 +45,9 @@ export type collectionTreeViewProps = {
 };
 
 export enum TreeViewType {
-    outline = "outline",
-    fileSystem = "fileSystem",
-    default = "default"
+    outline = 'outline',
+    fileSystem = 'fileSystem',
+    default = 'default',
 }
 
 @observer
@@ -61,13 +61,28 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     private observer: any; // observer for monitoring tree view items.
     private static expandViewLabelSize = 20;
 
-    @computed get doc() { return this.props.Document; }
-    @computed get dataDoc() { return this.props.DataDoc || this.doc; }
-    @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); }
-    @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; }
-    @computed get outlineMode() { return this.doc.treeViewType === TreeViewType.outline; }
-    @computed get fileSysMode() { return this.doc.treeViewType === TreeViewType.fileSystem; }
-    @computed get dashboardMode() { return this.doc === CurrentUserUtils.MyDashboards; }
+    @computed get doc() {
+        return this.props.Document;
+    }
+    @computed get dataDoc() {
+        return this.props.DataDoc || this.doc;
+    }
+    @computed get treeViewtruncateTitleWidth() {
+        return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth());
+    }
+    @computed get treeChildren() {
+        TraceMobx();
+        return this.props.childDocuments || this.childDocs;
+    }
+    @computed get outlineMode() {
+        return this.doc.treeViewType === TreeViewType.outline;
+    }
+    @computed get fileSysMode() {
+        return this.doc.treeViewType === TreeViewType.fileSystem;
+    }
+    @computed get dashboardMode() {
+        return this.doc === CurrentUserUtils.MyDashboards;
+    }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
 
@@ -75,11 +90,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
 
     // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
     @observable _isAnyChildContentActive = false;
-    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
-    isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.ActiveTool !== InkTool.None ||
-        (this.props.isContentActive?.() || this.props.Document.forceActive ||
-            this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
-            this.props.rootSelected(outsideReaction)) ? true : false)
+    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
+    isContentActive = (outsideReaction?: boolean) =>
+        CurrentUserUtils.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction)
+            ? true
+            : false;
 
     componentWillUnmount() {
         this._isDisposing = true;
@@ -89,47 +104,51 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     componentDidMount() {
-        this._disposers.autoheight = reaction(() => this.rootDoc.autoHeight,
+        this._disposers.autoheight = reaction(
+            () => this.rootDoc.autoHeight,
             auto => auto && this.computeHeight(),
-            { fireImmediately: true });
+            { fireImmediately: true }
+        );
     }
 
     computeHeight = () => {
         if (!this._isDisposing) {
-            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace("px", ""));
-            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.marginBot());
+            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', ''));
+            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot());
             this.layoutDoc._autoHeightMargins = bodyHeight;
             this.props.setHeight?.(bodyHeight + titleHeight);
         }
-    }
+    };
     unobserveHeight = (ref: any) => {
         this.refList.delete(ref);
         this.rootDoc.autoHeight && this.computeHeight();
-    }
+    };
     observeHeight = (ref: any) => {
         if (ref) {
             this.refList.add(ref);
-            this.observer = new _global.ResizeObserver(action((entries: any) => {
-                if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
-                    this.computeHeight();
-                }
-            }));
+            this.observer = new _global.ResizeObserver(
+                action((entries: any) => {
+                    if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
+                        this.computeHeight();
+                    }
+                })
+            );
             this.rootDoc.autoHeight && this.computeHeight();
             this.observer.observe(ref);
         }
-    }
+    };
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this._treedropDisposer?.();
-        if (this._mainEle = ele) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
-    }
+        if ((this._mainEle = ele)) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
+    };
 
     protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
         const dragData = de.complete.docDragData;
         if (dragData) {
             const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d));
-            dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction;
+            dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction;
         }
-    }
+    };
 
     @action
     remove = (doc: Doc | Doc[]): boolean => {
@@ -149,7 +168,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             return true;
         }
         return false;
-    }
+    };
 
     @action
     addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
@@ -161,81 +180,85 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             }, true);
         if (this.doc.resolvedDataDoc instanceof Promise) return false;
         return relativeTo === undefined ? this.props.addDocument?.(docs) || false : doAddDoc(docs);
-    }
+    };
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         if (!Doc.noviceMode) {
             const layoutItems: ContextMenuProps[] = [];
-            layoutItems.push({ description: "Make tree state " + (this.doc.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" });
-            layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
-            layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" });
-            ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
-            const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
-            const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-            onClicks.push({ description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" });
-            !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+            layoutItems.push({ description: 'Make tree state ' + (this.doc.treeViewOpenIsTransient ? 'persistent' : 'transient'), event: () => (this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient), icon: 'paint-brush' });
+            layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields), icon: 'paint-brush' });
+            layoutItems.push({ description: (this.doc.treeViewHideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle), icon: 'paint-brush' });
+            ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
+            const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...');
+            const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
+            onClicks.push({ description: 'Edit onChecked Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, 'onCheckedClick'), 'edit onCheckedClick'), icon: 'edit' });
+            !existingOnClick && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
         }
-    }
+    };
     onTreeDrop = (e: React.DragEvent, addDocs?: (docs: Doc[]) => void) => this.onExternalDrop(e, {}, addDocs);
 
     @undoBatch
     makeTextCollection = (childDocs: Doc[]) => {
         this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true);
-    }
+    };
 
     get editableTitle() {
-        return <EditableView
-            contents={this.dataDoc.title}
-            display={"block"}
-            maxHeight={72}
-            height={"auto"}
-            GetValue={() => StrCast(this.dataDoc.title)}
-            SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
-                if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
-                this.dataDoc.title = value;
-                return true;
-            })} />;
+        return (
+            <EditableView
+                contents={this.dataDoc.title}
+                display={'block'}
+                maxHeight={72}
+                height={'auto'}
+                GetValue={() => StrCast(this.dataDoc.title)}
+                SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
+                    if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
+                    this.dataDoc.title = value;
+                    return true;
+                })}
+            />
+        );
     }
 
-
     onKey = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
-        if (this.outlineMode && e.key === "Enter") {
+        if (this.outlineMode && e.key === 'Enter') {
             e.stopPropagation();
             this.makeTextCollection(this.treeChildren);
             return true;
         }
-    }
+    };
     get documentTitle() {
-        return <FormattedTextBox
-            {...this.props}
-            fieldKey={"text"}
-            renderDepth={this.props.renderDepth + 1}
-            isContentActive={this.isContentActive}
-            isDocumentActive={this.isContentActive}
-            rootSelected={returnTrue}
-            forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
-            PanelWidth={this.documentTitleWidth}
-            PanelHeight={this.documentTitleHeight}
-            scaling={returnOne}
-            onKey={this.onKey}
-            docFilters={returnEmptyFilter}
-            docRangeFilters={returnEmptyFilter}
-            searchFilterDocs={returnEmptyDoclist}
-            ContainingCollectionDoc={this.doc}
-            ContainingCollectionView={this.props.CollectionView}
-            addDocument={returnFalse}
-            moveDocument={returnFalse}
-            removeDocument={returnFalse}
-            whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-            bringToFront={returnFalse}
-        />;
+        return (
+            <FormattedTextBox
+                {...this.props}
+                fieldKey={'text'}
+                renderDepth={this.props.renderDepth + 1}
+                isContentActive={this.isContentActive}
+                isDocumentActive={this.isContentActive}
+                rootSelected={returnTrue}
+                forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
+                PanelWidth={this.documentTitleWidth}
+                PanelHeight={this.documentTitleHeight}
+                scaling={returnOne}
+                onKey={this.onKey}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionDoc={this.doc}
+                ContainingCollectionView={this.props.CollectionView}
+                addDocument={returnFalse}
+                moveDocument={returnFalse}
+                removeDocument={returnFalse}
+                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                bringToFront={returnFalse}
+            />
+        );
     }
     childContextMenuItems = () => {
         const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
         const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
-    }
+    };
     @computed get treeViewElements() {
         TraceMobx();
         const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
@@ -266,36 +289,34 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             this.props.treeViewSkipFields,
             true,
             this.whenChildContentsActiveChanged,
-            this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null),
+            this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, 'boolean', null),
             this.observeHeight,
             this.unobserveHeight,
             this.childContextMenuItems(),
             //TODO: [AL] add these
             this.props.AddToMap,
             this.props.RemFromMap,
-            this.props.hierarchyIndex,
+            this.props.hierarchyIndex
         );
     }
     @computed get titleBar() {
-        return this.dataDoc === null ? (null) :
-            <div className="collectionTreeView-titleBar" key={this.doc[Id]}
-                style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}}
-                ref={r => this._titleRef = r}>
+        return this.dataDoc === null ? null : (
+            <div className="collectionTreeView-titleBar" key={this.doc[Id]} style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}} ref={r => (this._titleRef = r)}>
                 {this.outlineMode ? this.documentTitle : this.editableTitle}
-            </div>;
+            </div>
+        );
     }
 
     @computed get noviceExplainer() {
-        return !Doc.noviceMode || !this.rootDoc.explainer ? (null) :
-            <div className="documentExplanation"> {this.rootDoc.explainer} </div>;
+        return !Doc.noviceMode || !this.rootDoc.explainer ? null : <div className="documentExplanation"> {StrCast(this.rootDoc.explainer)} </div>;
     }
 
     return35 = () => 35;
     @computed get buttonMenu() {
         const menuDoc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
         // To create a multibutton menu add a CollectionLinearView
-        return !menuDoc ? null :
-            (<div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+        return !menuDoc ? null : (
+            <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
                 <DocumentView
                     Document={menuDoc}
                     DataDoc={menuDoc}
@@ -322,11 +343,16 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                 />
-            </div>);
+            </div>
+        );
     }
 
-    @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
-    @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
+    @computed get nativeWidth() {
+        return Doc.NativeWidth(this.Document, undefined, true);
+    }
+    @computed get nativeHeight() {
+        return Doc.NativeHeight(this.Document, undefined, true);
+    }
 
     @computed get contentScaling() {
         const nw = this.nativeWidth;
@@ -347,68 +373,73 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
     remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
     moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
-        this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false
+        this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false;
 
     contentFunc = () => {
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
-        const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined;
-        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : this.titleBar;
+        const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined);
+        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
         return [
-            <div className="collectionTreeView-contents" key="tree" style={{
-                ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
-                overflow: "auto",
-                height: "100%"//this.layoutDoc._autoHeight ? "max-content" : "100%"
-            }} >
+            <div
+                className="collectionTreeView-contents"
+                key="tree"
+                style={{
+                    ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
+                    overflow: 'auto',
+                    height: '100%', //this.layoutDoc._autoHeight ? "max-content" : "100%"
+                }}>
                 {titleBar}
-                <div className="collectionTreeView-container"
+                <div
+                    className="collectionTreeView-container"
                     style={{
-                        transform: this.outlineMode ? `scale(${this.contentScaling})` : "",
+                        transform: this.outlineMode ? `scale(${this.contentScaling})` : '',
                         paddingLeft: `${this.marginX()}px`,
-                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : ""
+                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : '',
                     }}
                     onContextMenu={this.onContextMenu}>
-                    {!this.buttonMenu && !this.noviceExplainer ? (null) :
+                    {!this.buttonMenu && !this.noviceExplainer ? null : (
                         <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
                             {this.buttonMenu}
                             {this.noviceExplainer}
                         </div>
-                    }
-                    <div className="collectionTreeView-dropTarget"
+                    )}
+                    <div
+                        className="collectionTreeView-dropTarget"
                         style={{
                             background: background(),
                             height: `calc(100% - ${this._explainerHeight}px)`,
-                            pointerEvents: pointerEvents()
+                            pointerEvents: pointerEvents(),
                         }}
                         onWheel={e => e.stopPropagation()}
                         onDrop={this.onTreeDrop}
                         ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>
-                        <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
-                            {this.treeViewElements}
-                        </ul>
-                    </div >
+                        <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
+                    </div>
                 </div>
-            </div>
+            </div>,
         ];
-    }
+    };
     render() {
         TraceMobx();
 
-        return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
-            this.doc.treeViewHasOverlay ?
-                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                    isAnnotationOverlay={true}
-                    isAnnotationOverlayScrollable={true}
-                    childDocumentsActive={this.props.isDocumentActive}
-                    fieldKey={this.props.fieldKey + "-annotations"}
-                    dropAction={"move"}
-                    select={emptyFunction}
-                    addDocument={this.addAnnotationDocument}
-                    removeDocument={this.remAnnotationDocument}
-                    moveDocument={this.moveAnnotationDocument}
-                    bringToFront={emptyFunction}
-                    renderDepth={this.props.renderDepth + 1} >
-                    {this.contentFunc}
-                </CollectionFreeFormView> :
-                this.contentFunc();
+        return !(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
+            <CollectionFreeFormView
+                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                isAnnotationOverlay={true}
+                isAnnotationOverlayScrollable={true}
+                childDocumentsActive={this.props.isDocumentActive}
+                fieldKey={this.props.fieldKey + '-annotations'}
+                dropAction={'move'}
+                select={emptyFunction}
+                addDocument={this.addAnnotationDocument}
+                removeDocument={this.remAnnotationDocument}
+                moveDocument={this.moveAnnotationDocument}
+                bringToFront={emptyFunction}
+                renderDepth={this.props.renderDepth + 1}>
+                {this.contentFunc}
+            </CollectionFreeFormView>
+        ) : (
+            this.contentFunc()
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2ae0c01ef..d788f9a77 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,5 +1,5 @@
 import { computed, observable, runInAction } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import * as React from 'react';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
 import { Doc, DocListCast } from '../../../fields/Doc';
@@ -14,66 +14,65 @@ import { BranchCreate, BranchTask } from '../../documents/Gitlike';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { InteractionUtils } from '../../util/InteractionUtils';
-import { ContextMenu } from "../ContextMenu";
+import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { CollectionCarousel3DView } from './CollectionCarousel3DView';
 import { CollectionCarouselView } from './CollectionCarouselView';
-import { CollectionDockingView } from "./CollectionDockingView";
+import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionGridView } from './collectionGrid/CollectionGridView';
 import { CollectionLinearView } from './collectionLinear';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
 import { CollectionPileView } from './CollectionPileView';
-import { CollectionSchemaView } from "./collectionSchema/CollectionSchemaView";
+import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
 import { CollectionStackingView } from './CollectionStackingView';
 import { SubCollectionViewProps } from './CollectionSubView';
 import { CollectionTimeView } from './CollectionTimeView';
-import { CollectionTreeView } from "./CollectionTreeView";
+import { CollectionTreeView } from './CollectionTreeView';
 import './CollectionView.scss';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
 export enum CollectionViewType {
-    Invalid = "invalid",
-    Freeform = "freeform",
-    Schema = "schema",
-    Docking = "docking",
+    Invalid = 'invalid',
+    Freeform = 'freeform',
+    Schema = 'schema',
+    Docking = 'docking',
     Tree = 'tree',
-    Stacking = "stacking",
-    Masonry = "masonry",
-    Multicolumn = "multicolumn",
-    Multirow = "multirow",
-    Time = "time",
-    Carousel = "carousel",
-    Carousel3D = "3D Carousel",
-    Linear = "linear",
+    Stacking = 'stacking',
+    Masonry = 'masonry',
+    Multicolumn = 'multicolumn',
+    Multirow = 'multirow',
+    Time = 'time',
+    Carousel = 'carousel',
+    Carousel3D = '3D Carousel',
+    Linear = 'linear',
     //Staff = "staff",
-    Map = "map",
-    Grid = "grid",
-    Pile = "pileup",
-    StackedTimeline = "stacked timeline"
+    Map = 'map',
+    Grid = 'grid',
+    Pile = 'pileup',
+    StackedTimeline = 'stacked timeline',
 }
-export interface CollectionViewProps extends FieldViewProps {
-    isAnnotationOverlay?: boolean;  // is the collection an annotation overlay (eg an overlay on an image/video/etc)
+interface CollectionViewProps_ extends FieldViewProps {
+    isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
     layoutEngine?: () => string;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
 
     // property overrides for child documents
-    children?: never | (() => JSX.Element[]) | React.ReactNode;
     childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
-    childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
+    childDocumentsActive?: () => boolean; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
     childFitWidth?: (child: Doc) => boolean;
     childShowTitle?: () => string;
     childOpacity?: () => number;
-    childContextMenuItems?: () => { script: ScriptField, label: string }[];
+    childContextMenuItems?: () => { script: ScriptField; label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
     childHideDecorationTitle?: () => boolean;
     childHideResizeHandles?: () => boolean;
-    childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
+    childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
     childLayoutString?: string;
     childIgnoreNativeSize?: boolean;
     childClickScript?: ScriptField;
@@ -81,20 +80,25 @@ export interface CollectionViewProps extends FieldViewProps {
     //TODO: [AL] add these fields
     AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[];
     RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[];
-    hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views) 
+    hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views)
 }
+export interface CollectionViewProps extends React.PropsWithChildren<CollectionViewProps_> {}
 @observer
 export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps>() {
-    public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
+    public static LayoutString(fieldStr: string) {
+        return FieldView.LayoutString(CollectionView, fieldStr);
+    }
 
     @observable private static _safeMode = false;
-    public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
+    public static SetSafeMode(safeMode: boolean) {
+        this._safeMode = safeMode;
+    }
 
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
     constructor(props: any) {
         super(props);
-        runInAction(() => this._annotationKeySuffix = returnEmptyString);
+        runInAction(() => (this._annotationKeySuffix = returnEmptyString));
     }
 
     get collectionViewType(): CollectionViewType | undefined {
@@ -102,15 +106,17 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         if (CollectionView._safeMode) {
             switch (viewField) {
                 case CollectionViewType.Freeform:
-                case CollectionViewType.Schema: return CollectionViewType.Tree;
-                case CollectionViewType.Invalid: return CollectionViewType.Freeform;
+                case CollectionViewType.Schema:
+                    return CollectionViewType.Tree;
+                case CollectionViewType.Invalid:
+                    return CollectionViewType.Freeform;
             }
         }
         return viewField as any as CollectionViewType;
     }
 
     showIsTagged = () => {
-        return (null);
+        return null;
         // this section would display an icon in the bototm right of a collection to indicate that all
         // photos had been processed through Google's content analysis API and Google's tags had been
         // assigned to the documents googlePhotosTags field.
@@ -119,136 +125,171 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         // const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
         // return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
         //this.isContentActive();
-    }
+    };
 
-    screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
+    screenToLocalTransform = () => (this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()));
     private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
         TraceMobx();
         if (type === undefined) return null;
         switch (type) {
             default:
-            case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
-            case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
-            case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
-            case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
-            case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
-            case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
-            case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
-            case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
-            case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
-            case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
-            case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
-            case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
-            case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
-            case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
+            case CollectionViewType.Freeform:
+                return <CollectionFreeFormView key="collview" {...props} />;
+            case CollectionViewType.Schema:
+                return <CollectionSchemaView key="collview" {...props} />;
+            case CollectionViewType.Docking:
+                return <CollectionDockingView key="collview" {...props} />;
+            case CollectionViewType.Tree:
+                return <CollectionTreeView key="collview" {...props} />;
+            case CollectionViewType.Multicolumn:
+                return <CollectionMulticolumnView key="collview" {...props} />;
+            case CollectionViewType.Multirow:
+                return <CollectionMultirowView key="collview" {...props} />;
+            case CollectionViewType.Linear:
+                return <CollectionLinearView key="collview" {...props} />;
+            case CollectionViewType.Pile:
+                return <CollectionPileView key="collview" {...props} />;
+            case CollectionViewType.Carousel:
+                return <CollectionCarouselView key="collview" {...props} />;
+            case CollectionViewType.Carousel3D:
+                return <CollectionCarousel3DView key="collview" {...props} />;
+            case CollectionViewType.Stacking:
+                return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Masonry:
+                return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Time:
+                return <CollectionTimeView key="collview" {...props} />;
+            case CollectionViewType.Grid:
+                return <CollectionGridView key="collview" {...props} />;
             //case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />;
         }
-    }
+    };
 
     setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
         const subItems: ContextMenuProps[] = [];
-        subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" });
+        subItems.push({ description: 'Freeform', event: () => func(CollectionViewType.Freeform), icon: 'signature' });
         if (addExtras && CollectionView._safeMode) {
-            ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" });
+            ContextMenu.Instance.addItem({ description: 'Test Freeform', event: () => func(CollectionViewType.Invalid), icon: 'project-diagram' });
         }
-        subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" });
-        subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" });
-        subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" });
-        subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" });
-        subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" });
-        subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
-        subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
-        subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" });
-        !Doc.noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
-        !Doc.noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
-        subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
+        subItems.push({ description: 'Schema', event: () => func(CollectionViewType.Schema), icon: 'th-list' });
+        subItems.push({ description: 'Tree', event: () => func(CollectionViewType.Tree), icon: 'tree' });
+        subItems.push({ description: 'Stacking', event: () => (func(CollectionViewType.Stacking)._autoHeight = true), icon: 'ellipsis-v' });
+        subItems.push({ description: 'Multicolumn', event: () => func(CollectionViewType.Multicolumn), icon: 'columns' });
+        subItems.push({ description: 'Multirow', event: () => func(CollectionViewType.Multirow), icon: 'columns' });
+        subItems.push({ description: 'Masonry', event: () => func(CollectionViewType.Masonry), icon: 'columns' });
+        subItems.push({ description: 'Carousel', event: () => func(CollectionViewType.Carousel), icon: 'columns' });
+        subItems.push({ description: '3D Carousel', event: () => func(CollectionViewType.Carousel3D), icon: 'columns' });
+        !Doc.noviceMode && subItems.push({ description: 'Pivot/Time', event: () => func(CollectionViewType.Time), icon: 'columns' });
+        !Doc.noviceMode && subItems.push({ description: 'Map', event: () => func(CollectionViewType.Map), icon: 'globe-americas' });
+        subItems.push({ description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' });
 
         if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
             const existingVm = ContextMenu.Instance.findByDescription(category);
-            const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
-            catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" });
-            !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: "eye" });
+            const catItems = existingVm && 'subitems' in existingVm ? existingVm.subitems : [];
+            catItems.push({ description: 'Add a Perspective...', addDivider: true, noexpand: true, subitems: subItems, icon: 'eye' });
+            !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: 'eye' });
         }
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
         if (e.nativeEvent.cancelBubble) return; // nested calls to React to render can cause the same event to trigger in the outer view even if the inner view has handled it.  This avoid CollectionDockingView menu options from being added when the event has been handled by a sub-document.
-        if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            this.setupViewTypes("UI Controls...", vtype => {
-                const newRendition = Doc.MakeAlias(this.rootDoc);
-                newRendition._viewType = vtype;
-                this.props.addDocTab(newRendition, "add:right");
-                return newRendition;
-            }, false);
+        if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== CurrentUserUtils.MainDocId) {
+            // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+            this.setupViewTypes(
+                'UI Controls...',
+                vtype => {
+                    const newRendition = Doc.MakeAlias(this.rootDoc);
+                    newRendition._viewType = vtype;
+                    this.props.addDocTab(newRendition, 'add:right');
+                    return newRendition;
+                },
+                false
+            );
 
-            const options = cm.findByDescription("Options...");
-            const optionItems = options && "subitems" in options ? options.subitems : [];
-            !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null;
+            const options = cm.findByDescription('Options...');
+            const optionItems = options && 'subitems' in options ? options.subitems : [];
+            !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => (this.rootDoc.forceActive = !this.rootDoc.forceActive), icon: 'project-diagram' }) : null;
             if (this.rootDoc.childLayout instanceof Doc) {
-                optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" });
+                optionItems.push({ description: 'View Child Layout', event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, 'add:right'), icon: 'project-diagram' });
             }
             if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) {
-                optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" });
+                optionItems.push({ description: 'View Child Detailed Layout', event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, 'add:right'), icon: 'project-diagram' });
             }
-            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
+            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? 'Unset' : 'Set'} inPlace Container`, event: () => (this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer), icon: 'project-diagram' });
 
             if (!Doc.noviceMode) {
                 optionItems.push({
-                    description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
+                    description: 'Create Branch',
+                    event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), 'add:right'),
+                    icon: 'project-diagram',
                 });
                 optionItems.push({
-                    description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram"
+                    description: 'Pull Master',
+                    event: () => BranchTask(this.rootDoc, 'pull'),
+                    icon: 'project-diagram',
                 });
                 optionItems.push({
-                    description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram"
+                    description: 'Merge Branches',
+                    event: () => BranchTask(this.rootDoc, 'merge'),
+                    icon: 'project-diagram',
                 });
             }
             if (this.Document._viewType === CollectionViewType.Docking) {
-                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(), icon: "project-diagram" });
+                optionItems.push({ description: 'Create Dashboard', event: () => CurrentUserUtils.createNewDashboard(), icon: 'project-diagram' });
             }
 
-            !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
+            !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
 
             if (!Doc.noviceMode && !this.rootDoc.annotationOn) {
-                const existingOnClick = cm.findByDescription("OnClick...");
-                const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-                const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }];
-                funcs.map(func => onClicks.push({
-                    description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => {
-                        const alias = Doc.MakeAlias(this.rootDoc);
-                        DocUtils.makeCustomViewClicked(alias, undefined, func.key);
-                        this.props.addDocTab(alias, "add:right");
-                    }
-                }));
-                DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick =>
+                const existingOnClick = cm.findByDescription('OnClick...');
+                const onClicks = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
+                const funcs = [
+                    { key: 'onChildClick', name: 'On Child Clicked' },
+                    { key: 'onChildDoubleClick', name: 'On Child Double Clicked' },
+                ];
+                funcs.map(func =>
+                    onClicks.push({
+                        description: `Edit ${func.name} script`,
+                        icon: 'edit',
+                        event: (obj: any) => {
+                            const alias = Doc.MakeAlias(this.rootDoc);
+                            DocUtils.makeCustomViewClicked(alias, undefined, func.key);
+                            this.props.addDocTab(alias, 'add:right');
+                        },
+                    })
+                );
+                DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null).data).forEach(childClick =>
                     onClicks.push({
                         description: `Set child ${childClick.title}`,
-                        icon: "edit",
-                        event: () => Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)),
-                    }));
-                !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+                        icon: 'edit',
+                        event: () => (Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))),
+                    })
+                );
+                !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
             }
 
             if (!Doc.noviceMode) {
-                const more = cm.findByDescription("More...");
-                const moreItems = more && "subitems" in more ? more.subitems : [];
-                moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) });
-                !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+                const more = cm.findByDescription('More...');
+                const moreItems = more && 'subitems' in more ? more.subitems : [];
+                moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) });
+                !more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' });
             }
         }
-    }
+    };
 
     bodyPanelWidth = () => this.props.PanelWidth();
 
     childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
     childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
-    @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); }
+    @computed get childLayoutString() {
+        return StrCast(this.rootDoc.childLayoutString);
+    }
 
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive();
-    }
+    };
     render() {
         TraceMobx();
         const props: SubCollectionViewProps = {
@@ -268,10 +309,11 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             childHideDecorationTitle: this.childHideDecorationTitle,
             CollectionView: this,
         };
-        return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? "none" : undefined }}>
-            {this.showIsTagged()}
-            {this.renderSubView(this.collectionViewType, props)}
-        </div>);
+        return (
+            <div className={'collectionView'} onContextMenu={this.onContextMenu} style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? 'none' : undefined }}>
+                {this.showIsTagged()}
+                {this.renderSubView(this.collectionViewType, props)}
+            </div>
+        );
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index dacbb3508..8720c9097 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,25 +1,23 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { Id } from "../../../../fields/FieldSymbols";
-import { DocumentManager } from "../../../util/DocumentManager";
-import "./CollectionFreeFormLinksView.scss";
-import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
-import React = require("react");
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { Id } from '../../../../fields/FieldSymbols';
+import { DocumentManager } from '../../../util/DocumentManager';
+import './CollectionFreeFormLinksView.scss';
+import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
+import React = require('react');
 
 @observer
-export class CollectionFreeFormLinksView extends React.Component {
+export class CollectionFreeFormLinksView extends React.Component<React.PropsWithChildren<{}>> {
     @computed get uniqueConnections() {
-        return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c =>
-            <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />
-        );
+        return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c => <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />);
     }
 
     render() {
-        return <div className="collectionfreeformlinksview-container">
-            <svg className="collectionfreeformlinksview-svgCanvas">
-                {this.uniqueConnections}
-            </svg>
-            {this.props.children}
-        </div>;
+        return (
+            <div className="collectionfreeformlinksview-container">
+                <svg className="collectionfreeformlinksview-svgCanvas">{this.uniqueConnections}</svg>
+                {this.props.children}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index 3d2ed0cf9..3d1d87aa0 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -1,14 +1,13 @@
 import * as React from 'react';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 
-import "../../../../../node_modules/react-grid-layout/css/styles.css";
-import "../../../../../node_modules/react-resizable/css/styles.css";
+import '../../../../../node_modules/react-grid-layout/css/styles.css';
+import '../../../../../node_modules/react-resizable/css/styles.css';
 
 import * as GridLayout from 'react-grid-layout';
 import { Layout } from 'react-grid-layout';
 export { Layout } from 'react-grid-layout';
 
-
 interface GridProps {
     width: number;
     nodeList: JSX.Element[] | null;
@@ -29,9 +28,10 @@ interface GridProps {
 @observer
 export default class Grid extends React.Component<GridProps> {
     render() {
-        const compactType = this.props.compactType === "vertical" || this.props.compactType === "horizontal" ? this.props.compactType : null;
+        const compactType = this.props.compactType === 'vertical' || this.props.compactType === 'horizontal' ? this.props.compactType : null;
         return (
-            <GridLayout className="layout"
+            <GridLayout
+                className="layout"
                 layout={this.props.layout}
                 cols={this.props.numCols}
                 rowHeight={this.props.rowHeight}
@@ -43,9 +43,8 @@ export default class Grid extends React.Component<GridProps> {
                 useCSSTransforms={true}
                 onLayoutChange={this.props.setLayout}
                 preventCollision={this.props.preventCollision}
-                transformScale={1 / this.props.transformScale} // still doesn't work :(
-                margin={[this.props.margin, this.props.margin]}
-            >
+                transformScale={1 / this.props.transformScale} // still doesn't work :(  ??
+                margin={[this.props.margin, this.props.margin]}>
                 {this.props.nodeList}
             </GridLayout>
         );
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c31267e87..8adfdc70b 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -9,7 +9,7 @@ import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types
 import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
 import { DocUtils } from '../../../documents/Documents';
 import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
-import { DocumentManager } from "../../../util/DocumentManager";
+import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { Colors, Shadows } from '../../global/globalEnums';
@@ -21,13 +21,12 @@ import { StyleProp } from '../../StyleProvider';
 import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
 import { CollectionSubView } from '../CollectionSubView';
 import { CollectionViewType } from '../CollectionView';
-import "./CollectionLinearView.scss";
-
+import './CollectionLinearView.scss';
 
 /**
- * CollectionLinearView is the class for rendering the horizontal collection 
+ * CollectionLinearView is the class for rendering the horizontal collection
  * of documents, it useful for horizontal menus. It can either be expandable
- * or not using the linearViewExpandable field. 
+ * or not using the linearViewExpandable field.
  * It is used in the following locations:
  * - It is used in the popup menu on the bottom left (see docButtons() in MainView.tsx)
  * - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
@@ -48,45 +47,47 @@ export class CollectionLinearView extends CollectionSubView() {
     }
 
     componentDidMount() {
-        this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10),
+        this._widthDisposer = reaction(
+            () => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * this.rootDoc[HeightSym]() : 10),
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
 
         this._selectedDisposer = reaction(
             () => NumCast(this.layoutDoc.selectedIndex),
-            (i) => runInAction(() => {
-                this._selectedIndex = i;
-                let selected: any = undefined;
-                this.childLayoutPairs.map(async (pair, ind) => {
-                    const isSelected = this._selectedIndex === ind;
-                    if (isSelected) {
-                        selected = pair;
-                    }
-                    else {
-                        ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+            i =>
+                runInAction(() => {
+                    this._selectedIndex = i;
+                    let selected: any = undefined;
+                    this.childLayoutPairs.map(async (pair, ind) => {
+                        const isSelected = this._selectedIndex === ind;
+                        if (isSelected) {
+                            selected = pair;
+                        } else {
+                            ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+                        }
+                    });
+                    if (selected && selected.layout) {
+                        ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
                     }
-                });
-                if (selected && selected.layout) {
-                    ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
-                }
-            }),
+                }),
             { fireImmediately: true }
         );
     }
-    protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+        //used for stacking and masonry view
         this._dropDisposer && this._dropDisposer();
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
         }
-    }
+    };
 
     dimension = () => NumCast(this.rootDoc._height); // 2 * the padding
     getTransform = (ele: Opt<HTMLDivElement>) => {
         if (!ele) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
         return new Transform(-translateX, -translateY, 1);
-    }
+    };
 
     @action
     exitLongLinks = () => {
@@ -99,28 +100,27 @@ export class CollectionLinearView extends CollectionSubView() {
         }
         DocumentLinksButton.StartLink = undefined;
         DocumentLinksButton.StartLinkView = undefined;
-    }
+    };
 
     @action
     changeDescriptionSetting = () => {
         if (LinkDescriptionPopup.showDescriptions) {
-            if (LinkDescriptionPopup.showDescriptions === "ON") {
-                LinkDescriptionPopup.showDescriptions = "OFF";
+            if (LinkDescriptionPopup.showDescriptions === 'ON') {
+                LinkDescriptionPopup.showDescriptions = 'OFF';
                 LinkDescriptionPopup.descriptionPopup = false;
             } else {
-                LinkDescriptionPopup.showDescriptions = "ON";
+                LinkDescriptionPopup.showDescriptions = 'ON';
             }
         } else {
-            LinkDescriptionPopup.showDescriptions = "OFF";
+            LinkDescriptionPopup.showDescriptions = 'OFF';
             LinkDescriptionPopup.descriptionPopup = false;
         }
-    }
+    };
 
     myContextMenu = (e: React.MouseEvent) => {
         e.stopPropagation();
         e.preventDefault();
-    }
-
+    };
 
     getDisplayDoc = (doc: Doc, preview: boolean = false) => {
         const nested = doc._viewType === CollectionViewType.Linear;
@@ -129,44 +129,50 @@ export class CollectionLinearView extends CollectionSubView() {
         let dref: Opt<HTMLDivElement>;
         const docXf = () => this.getTransform(dref);
         // const scalable = pair.layout.onClick || pair.layout.onDragStart;
-        return hidden ? (null) : <div className={preview ? "preview" : `collectionLinearView-docBtn`} key={doc[Id]} ref={r => dref = r || undefined}
-            style={{
-                pointerEvents: "all",
-                width: nested ? undefined : NumCast(doc._width),
-                height: nested ? undefined : NumCast(doc._height),
-                marginLeft: !nested ? 2.5 : 0,
-                marginRight: !nested ? 2.5 : 0,
-                // width: NumCast(pair.layout._width),
-                // height: NumCast(pair.layout._height),
-            }}  >
-            <DocumentView
-                Document={doc}
-                isContentActive={this.props.isContentActive}
-                isDocumentActive={returnTrue}
-                addDocument={this.props.addDocument}
-                moveDocument={this.props.moveDocument}
-                addDocTab={this.props.addDocTab}
-                pinToPres={emptyFunction}
-                rootSelected={this.props.isSelected}
-                removeDocument={this.props.removeDocument}
-                ScreenToLocalTransform={docXf}
-                PanelWidth={nested ? doc[WidthSym] : this.dimension}
-                PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
-                renderDepth={this.props.renderDepth + 1}
-                dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
-                focus={emptyFunction}
-                styleProvider={this.props.styleProvider}
-                docViewPath={returnEmptyDoclist}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={this.props.docFilters}
-                docRangeFilters={this.props.docRangeFilters}
-                searchFilterDocs={this.props.searchFilterDocs}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                hideResizeHandles={true} />
-        </div>;
-    }
+        return hidden ? null : (
+            <div
+                className={preview ? 'preview' : `collectionLinearView-docBtn`}
+                key={doc[Id]}
+                ref={r => (dref = r || undefined)}
+                style={{
+                    pointerEvents: 'all',
+                    width: nested ? undefined : NumCast(doc._width),
+                    height: nested ? undefined : NumCast(doc._height),
+                    marginLeft: !nested ? 2.5 : 0,
+                    marginRight: !nested ? 2.5 : 0,
+                    // width: NumCast(pair.layout._width),
+                    // height: NumCast(pair.layout._height),
+                }}>
+                <DocumentView
+                    Document={doc}
+                    isContentActive={this.props.isContentActive}
+                    isDocumentActive={returnTrue}
+                    addDocument={this.props.addDocument}
+                    moveDocument={this.props.moveDocument}
+                    addDocTab={this.props.addDocTab}
+                    pinToPres={emptyFunction}
+                    rootSelected={this.props.isSelected}
+                    removeDocument={this.props.removeDocument}
+                    ScreenToLocalTransform={docXf}
+                    PanelWidth={nested ? doc[WidthSym] : this.dimension}
+                    PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
+                    renderDepth={this.props.renderDepth + 1}
+                    dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
+                    focus={emptyFunction}
+                    styleProvider={this.props.styleProvider}
+                    docViewPath={returnEmptyDoclist}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={this.props.docFilters}
+                    docRangeFilters={this.props.docRangeFilters}
+                    searchFilterDocs={this.props.searchFilterDocs}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    hideResizeHandles={true}
+                />
+            </div>
+        );
+    };
 
     render() {
         const guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label
@@ -178,66 +184,99 @@ export class CollectionLinearView extends CollectionSubView() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle
-        const menuOpener = <label htmlFor={`${guid}`}
-            style={{
-                boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
-                color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
-                backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY
-            }}
-            onPointerDown={e => e.stopPropagation()} >
-            <div className="collectionLinearView-menuOpener">
-                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"} /> : icon ? icon : <FontAwesomeIcon icon={"plus"} />}
-            </div>
-        </label>;
-
-        return <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : "transparent" }}>
-            <div className="collectionLinearView" ref={this.createDashEventsTarget}
-                onContextMenu={this.myContextMenu} >
-                {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close" : "Open"}</div></>} placement="top">
-                    {menuOpener}
-                </Tooltip>}
-                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
-                    onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
-
-                <div className="collectionLinearView-content"
-                    style={{
-                        height: this.dimension(),
-                        flexDirection: flexDir,
-                        gap: flexGap
-                    }}>
-                    {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
-                </div>
-                {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null :
-                    <span className="bottomPopup-background" style={{ pointerEvents: "all" }}
-                        onPointerDown={e => e.stopPropagation()} >
-                        <span className="bottomPopup-text" >
-                            Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
-                        </span>
+        const menuOpener = (
+            <label
+                htmlFor={`${guid}`}
+                style={{
+                    boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
+                    color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
+                    backgroundColor: backgroundColor === color ? 'black' : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY,
+                }}
+                onPointerDown={e => e.stopPropagation()}>
+                <div className="collectionLinearView-menuOpener">{BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={'minus'} /> : icon ? icon : <FontAwesomeIcon icon={'plus'} />}</div>
+            </label>
+        );
 
-                        <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
-                            <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
-                                Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
-                            </span>
+        return (
+            <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : 'transparent' }}>
+                <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu}>
+                    {!expandable ? null : (
+                        <Tooltip
+                            title={
+                                <>
+                                    <div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? 'Close' : 'Open'}</div>
+                                </>
+                            }
+                            placement="top">
+                            {menuOpener}
                         </Tooltip>
+                    )}
+                    <input
+                        id={`${guid}`}
+                        type="checkbox"
+                        checked={BoolCast(this.props.Document.linearViewIsExpanded)}
+                        ref={this.addMenuToggle}
+                        onChange={action(() => (this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked))}
+                    />
 
-                        <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
-                            <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
-                                Stop
+                    <div
+                        className="collectionLinearView-content"
+                        style={{
+                            height: this.dimension(),
+                            flexDirection: flexDir,
+                            gap: flexGap,
+                        }}>
+                        {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
+                    </div>
+                    {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : (
+                        <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
+                            <span className="bottomPopup-text">
+                                Creating link from:{' '}
+                                <b>
+                                    {(DocumentLinksButton.AnnotationId ? 'Annotation in ' : ' ') +
+                                        (StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...')}
+                                </b>
                             </span>
-                        </Tooltip>
-                    </span>}
-                {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? (null) :
-                    <span className="bottomPopup-background">
-                        <span className="bottomPopup-text">
-                            Currently playing:
-                            {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
-                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
-                                    {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")}
-                                </span>)}
-                        </span>
-                    </span>}
 
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{'Toggle description pop-up'} </div>
+                                    </>
+                                }
+                                placement="top">
+                                <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
+                                    Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : 'ON'}
+                                </span>
+                            </Tooltip>
+
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">Exit linking mode</div>
+                                    </>
+                                }
+                                placement="top">
+                                <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
+                                    Stop
+                                </span>
+                            </Tooltip>
+                        </span>
+                    )}
+                    {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : (
+                        <span className="bottomPopup-background">
+                            <span className="bottomPopup-text">
+                                Currently playing:
+                                {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => (
+                                    <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
+                                        {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? '' : ',')}
+                                    </span>
+                                ))}
+                            </span>
+                        </span>
+                    )}
+                </div>
             </div>
-        </div>;
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 1414bfdf7..ba301962b 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -1,15 +1,14 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, NumListCast, StrListCast, Field } from "../../../fields/Doc";
-import { DateCast, StrCast, Cast } from "../../../fields/Types";
-import { LinkManager } from "../../util/LinkManager";
-import { undoBatch } from "../../util/UndoManager";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, NumListCast, StrListCast, Field } from '../../../fields/Doc';
+import { DateCast, StrCast, Cast } from '../../../fields/Types';
+import { LinkManager } from '../../util/LinkManager';
+import { undoBatch } from '../../util/UndoManager';
 import './LinkEditor.scss';
-import { LinkRelationshipSearch } from "./LinkRelationshipSearch";
-import React = require("react");
-
+import { LinkRelationshipSearch } from './LinkRelationshipSearch';
+import React = require('react');
 
 interface LinkEditorProps {
     sourceDoc: Doc;
@@ -19,16 +18,20 @@ interface LinkEditorProps {
 }
 @observer
 export class LinkEditor extends React.Component<LinkEditorProps> {
-
     @observable description = Field.toString(LinkManager.currentLink?.description as any as Field);
     @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship);
     @observable zoomFollow = StrCast(this.props.sourceDoc.followLinkZoom);
     @observable openDropdown: boolean = false;
     @observable showInfo: boolean = false;
-    @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; }
-    @observable private buttonColor: string = "";
-    @observable private relationshipButtonColor: string = "";
-    @observable private relationshipSearchVisibility: string = "none";
+    @computed get infoIcon() {
+        if (this.showInfo) {
+            return 'chevron-up';
+        }
+        return 'chevron-down';
+    }
+    @observable private buttonColor: string = '';
+    @observable private relationshipButtonColor: string = '';
+    @observable private relationshipSearchVisibility: string = 'none';
     @observable private searchIsActive: boolean = false;
 
     //@observable description = this.props.linkDoc.description ? StrCast(this.props.linkDoc.description) : "DESCRIPTION";
@@ -37,7 +40,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     deleteLink = (): void => {
         LinkManager.Instance.deleteLink(this.props.linkDoc);
         this.props.showLinks();
-    }
+    };
 
     @undoBatch
     setRelationshipValue = action((value: string) => {
@@ -49,11 +52,11 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
             const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes);
             const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
 
-            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color 
+            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
             if (!linkRelationshipList?.includes(value)) {
                 linkRelationshipList.push(value);
                 linkRelationshipSizes.push(1);
-                const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")";
+                const randColor = 'rgb(' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')';
                 linkColorList.push(randColor);
                 // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes
             } else if (linkRelationshipList && value !== prevRelationship) {
@@ -61,20 +64,22 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
                 //increment size of new relationship size
                 if (index !== -1 && index < linkRelationshipSizes.length) {
                     const pvalue = linkRelationshipSizes[index];
-                    linkRelationshipSizes[index] = (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1);
+                    linkRelationshipSizes[index] = pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1;
                 }
                 //decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation)
                 if (linkRelationshipList.includes(prevRelationship)) {
                     const pindex = linkRelationshipList.indexOf(prevRelationship);
                     if (pindex !== -1 && pindex < linkRelationshipSizes.length) {
                         const pvalue = linkRelationshipSizes[pindex];
-                        linkRelationshipSizes[pindex] = Math.max(0, (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1));
+                        linkRelationshipSizes[pindex] = Math.max(0, pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1);
                     }
                 }
-
             }
-            this.relationshipButtonColor = "rgb(62, 133, 55)";
-            setTimeout(action(() => this.relationshipButtonColor = ""), 750);
+            this.relationshipButtonColor = 'rgb(62, 133, 55)';
+            setTimeout(
+                action(() => (this.relationshipButtonColor = '')),
+                750
+            );
             return true;
         }
     });
@@ -89,144 +94,143 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
         if (linkRelationshipList) {
             return linkRelationshipList.filter(rel => rel.includes(query));
         }
-    }
+    };
 
     /**
      * toggles visibility of the relationship search results when the input field is focused on
      */
     @action
     toggleRelationshipResults = () => {
-        this.relationshipSearchVisibility = this.relationshipSearchVisibility === "none" ? "block" : "none";
-    }
+        this.relationshipSearchVisibility = this.relationshipSearchVisibility === 'none' ? 'block' : 'none';
+    };
 
     @undoBatch
     setDescripValue = action((value: string) => {
         if (LinkManager.currentLink) {
             Doc.GetProto(LinkManager.currentLink).description = value;
-            this.buttonColor = "rgb(62, 133, 55)";
-            setTimeout(action(() => this.buttonColor = ""), 750);
+            this.buttonColor = 'rgb(62, 133, 55)';
+            setTimeout(
+                action(() => (this.buttonColor = '')),
+                750
+            );
             return true;
         }
     });
 
     onDescriptionKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             this.setDescripValue(this.description);
             document.getElementById('input')?.blur();
         }
         e.stopPropagation();
-    }
+    };
 
     onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             this.setRelationshipValue(this.relationship);
             document.getElementById('input')?.blur();
         }
         e.stopPropagation();
-    }
+    };
 
     onDescriptionDown = () => this.setDescripValue(this.description);
     onRelationshipDown = () => this.setRelationshipValue(this.relationship);
 
     onBlur = () => {
-        //only hide the search results if the user clicks out of the input AND not on any of the search results 
+        //only hide the search results if the user clicks out of the input AND not on any of the search results
         // i.e. if search is not active
         if (!this.searchIsActive) {
             this.toggleRelationshipResults();
         }
-    }
+    };
     onFocus = () => {
         this.toggleRelationshipResults();
-    }
+    };
     toggleSearchIsActive = () => {
         this.searchIsActive = !this.searchIsActive;
-    }
+    };
 
     @action
-    handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; }
+    handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.description = e.target.value;
+    };
     @action
-    handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.relationship = e.target.value; }
+    handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.relationship = e.target.value;
+    };
     @action
-    handleZoomFollowChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.props.sourceDoc.followLinkZoom = e.target.checked; }
+    handleZoomFollowChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.props.sourceDoc.followLinkZoom = e.target.checked;
+    };
     @action
     handleRelationshipSearchChange = (result: string) => {
         this.setRelationshipValue(result);
         this.toggleRelationshipResults();
         this.relationship = result;
-    }
+    };
     @computed
     get editRelationship() {
         //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS
-        return <div className="linkEditor-description">
-            <div className="linkEditor-description-label">Link Relationship:</div>
-            <div className="linkEditor-description-input">
-                <div className="linkEditor-description-editing">
-                    <input
-                        style={{ width: "100%" }}
-                        id="input"
-                        value={this.relationship}
-                        autoComplete={"off"}
-                        placeholder={"Enter link relationship"}
-                        onKeyDown={this.onRelationshipKey}
-                        onChange={this.handleRelationshipChange}
-                        onFocus={this.onFocus}
-                        onBlur={this.onBlur}
-                    ></input>
-                    <LinkRelationshipSearch
-                        results={this.getRelationshipResults()}
-                        display={this.relationshipSearchVisibility}
-                        handleRelationshipSearchChange={this.handleRelationshipSearchChange}
-                        toggleSearch={this.toggleSearchIsActive}
-                    />
+        return (
+            <div className="linkEditor-description">
+                <div className="linkEditor-description-label">Link Relationship:</div>
+                <div className="linkEditor-description-input">
+                    <div className="linkEditor-description-editing">
+                        <input
+                            style={{ width: '100%' }}
+                            id="input"
+                            value={this.relationship}
+                            autoComplete={'off'}
+                            placeholder={'Enter link relationship'}
+                            onKeyDown={this.onRelationshipKey}
+                            onChange={this.handleRelationshipChange}
+                            onFocus={this.onFocus}
+                            onBlur={this.onBlur}></input>
+                        <LinkRelationshipSearch results={this.getRelationshipResults()} display={this.relationshipSearchVisibility} handleRelationshipSearchChange={this.handleRelationshipSearchChange} toggleSearch={this.toggleSearchIsActive} />
+                    </div>
+                    <div className="linkEditor-description-add-button" style={{ background: this.relationshipButtonColor }} onPointerDown={this.onRelationshipDown}>
+                        Set
+                    </div>
                 </div>
-                <div className="linkEditor-description-add-button"
-                    style={{ background: this.relationshipButtonColor }}
-                    onPointerDown={this.onRelationshipDown}>Set</div>
             </div>
-        </div>;
+        );
     }
     @computed
     get editZoomFollow() {
         //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS
-        return <div className="linkEditor-zoomFollow">
-            <div className="linkEditor-zoomFollow-label">Zoom To Link Target:</div>
-            <div className="linkEditor-zoomFollow-input">
-                <div className="linkEditor-zoomFollow-editing">
-                    <input
-                        style={{ width: "100%" }}
-                        type="checkbox"
-                        value={this.zoomFollow}
-                        onChange={this.handleZoomFollowChange} />
+        return (
+            <div className="linkEditor-zoomFollow">
+                <div className="linkEditor-zoomFollow-label">Zoom To Link Target:</div>
+                <div className="linkEditor-zoomFollow-input">
+                    <div className="linkEditor-zoomFollow-editing">
+                        <input style={{ width: '100%' }} type="checkbox" value={this.zoomFollow} onChange={this.handleZoomFollowChange} />
+                    </div>
                 </div>
             </div>
-        </div >;
+        );
     }
 
     @computed
     get editDescription() {
-        return <div className="linkEditor-description">
-            <div className="linkEditor-description-label">Link Description:</div>
-            <div className="linkEditor-description-input">
-                <div className="linkEditor-description-editing">
-                    <input
-                        style={{ width: "100%" }}
-                        autoComplete={"off"}
-                        id="input"
-                        value={this.description}
-                        placeholder={"Enter link description"}
-                        onKeyDown={this.onDescriptionKey}
-                        onChange={this.handleDescriptionChange}
-                    ></input>
+        return (
+            <div className="linkEditor-description">
+                <div className="linkEditor-description-label">Link Description:</div>
+                <div className="linkEditor-description-input">
+                    <div className="linkEditor-description-editing">
+                        <input style={{ width: '100%' }} autoComplete={'off'} id="input" value={this.description} placeholder={'Enter link description'} onKeyDown={this.onDescriptionKey} onChange={this.handleDescriptionChange}></input>
+                    </div>
+                    <div className="linkEditor-description-add-button" style={{ background: this.buttonColor }} onPointerDown={this.onDescriptionDown}>
+                        Set
+                    </div>
                 </div>
-                <div className="linkEditor-description-add-button"
-                    style={{ background: this.buttonColor }}
-                    onPointerDown={this.onDescriptionDown}>Set</div>
             </div>
-        </div>;
+        );
     }
 
     @action
-    changeDropdown = () => { this.openDropdown = !this.openDropdown; }
+    changeDropdown = () => {
+        this.openDropdown = !this.openDropdown;
+    };
 
     @undoBatch
     changeFollowBehavior = action((follow: string) => {
@@ -236,94 +240,114 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
 
     @computed
     get followingDropdown() {
-        return <div className="linkEditor-followingDropdown">
-            <div className="linkEditor-followingDropdown-label">Follow Behavior:</div>
-            <div className="linkEditor-followingDropdown-dropdown">
-                <div className="linkEditor-followingDropdown-header"
-                    onPointerDown={this.changeDropdown}>
-                    {StrCast(this.props.linkDoc.followLinkLocation, "default")}
-                    <FontAwesomeIcon className="linkEditor-followingDropdown-icon"
-                        icon={this.openDropdown ? "chevron-up" : "chevron-down"}
-                        size={"lg"} />
-                </div>
-                <div className="linkEditor-followingDropdown-optionsList"
-                    style={{ display: this.openDropdown ? "" : "none" }}>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("default")}>
-                        Default
+        return (
+            <div className="linkEditor-followingDropdown">
+                <div className="linkEditor-followingDropdown-label">Follow Behavior:</div>
+                <div className="linkEditor-followingDropdown-dropdown">
+                    <div className="linkEditor-followingDropdown-header" onPointerDown={this.changeDropdown}>
+                        {StrCast(this.props.linkDoc.followLinkLocation, 'default')}
+                        <FontAwesomeIcon className="linkEditor-followingDropdown-icon" icon={this.openDropdown ? 'chevron-up' : 'chevron-down'} size={'lg'} />
                     </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("add:left")}>
-                        Always open in new left pane
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("add:right")}>
-                        Always open in new right pane
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("replace:right")}>
-                        Always replace right tab
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("replace:left")}>
-                        Always replace left tab
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("fullScreen")}>
-                        Always open full screen
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("add")}>
-                        Always open in a new tab
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("replace")}>
-                        Replace Tab
-                    </div>
-                    {this.props.linkDoc.linksToAnnotation ?
-                        <div className="linkEditor-followingDropdown-option"
-                            onPointerDown={() => this.changeFollowBehavior("openExternal")}>
-                            Always open in external page
+                    <div className="linkEditor-followingDropdown-optionsList" style={{ display: this.openDropdown ? '' : 'none' }}>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('default')}>
+                            Default
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add:left')}>
+                            Always open in new left pane
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add:right')}>
+                            Always open in new right pane
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace:right')}>
+                            Always replace right tab
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace:left')}>
+                            Always replace left tab
                         </div>
-                        : null}
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('fullScreen')}>
+                            Always open full screen
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add')}>
+                            Always open in a new tab
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace')}>
+                            Replace Tab
+                        </div>
+                        {this.props.linkDoc.linksToAnnotation ? (
+                            <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('openExternal')}>
+                                Always open in external page
+                            </div>
+                        ) : null}
+                    </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 
     @action
-    changeInfo = () => { this.showInfo = !this.showInfo; }
+    changeInfo = () => {
+        this.showInfo = !this.showInfo;
+    };
 
     render() {
         const destination = LinkManager.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
 
-        return !destination ? (null) : (
+        return !destination ? null : (
             <div className="linkEditor" tabIndex={0} onKeyDown={e => e.stopPropagation()}>
                 <div className="linkEditor-info">
-                    <Tooltip title={<><div className="dash-tooltip">Return to link menu</div></>} placement="top">
-                        <button className="linkEditor-button-back"
-                            style={{ display: this.props.hideback ? "none" : "" }}
-                            onClick={this.props.showLinks}>
-                            <FontAwesomeIcon icon="arrow-left" size="sm" /> </button>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">Return to link menu</div>
+                            </>
+                        }
+                        placement="top">
+                        <button className="linkEditor-button-back" style={{ display: this.props.hideback ? 'none' : '' }} onClick={this.props.showLinks}>
+                            <FontAwesomeIcon icon="arrow-left" size="sm" />{' '}
+                        </button>
                     </Tooltip>
-                    <p className="linkEditor-linkedTo">Editing Link to: <b>{
-                        destination.proto?.title ?? destination.title ?? "untitled"}</b></p>
-                    <Tooltip title={<><div className="dash-tooltip">Show more link information</div></>} placement="top">
-                        <div className="linkEditor-downArrow"><FontAwesomeIcon className="button" icon={this.infoIcon} size="lg" onPointerDown={this.changeInfo} /></div>
+                    <p className="linkEditor-linkedTo">
+                        Editing Link to: <b>{StrCast(destination.proto?.title, StrCast(destination.title, 'untitled'))}</b>
+                    </p>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">Show more link information</div>
+                            </>
+                        }
+                        placement="top">
+                        <div className="linkEditor-downArrow">
+                            <FontAwesomeIcon className="button" icon={this.infoIcon} size="lg" onPointerDown={this.changeInfo} />
+                        </div>
                     </Tooltip>
                 </div>
-                {this.showInfo ? <div className="linkEditor-moreInfo">
-                    <div>{this.props.linkDoc.author ? <div> <b>Author:</b> {this.props.linkDoc.author}</div> : null}</div>
-                    <div>{this.props.linkDoc.creationDate ? <div> <b>Creation Date:</b>
-                        {DateCast(this.props.linkDoc.creationDate).toString()}</div> : null}</div>
-                </div> : null}
+                {this.showInfo ? (
+                    <div className="linkEditor-moreInfo">
+                        <div>
+                            {this.props.linkDoc.author ? (
+                                <div>
+                                    {' '}
+                                    <b>Author:</b> {StrCast(this.props.linkDoc.author)}
+                                </div>
+                            ) : null}
+                        </div>
+                        <div>
+                            {this.props.linkDoc.creationDate ? (
+                                <div>
+                                    {' '}
+                                    <b>Creation Date:</b>
+                                    {DateCast(this.props.linkDoc.creationDate).toString()}
+                                </div>
+                            ) : null}
+                        </div>
+                    </div>
+                ) : null}
 
                 {this.editDescription}
                 {this.editRelationship}
                 {this.editZoomFollow}
                 {this.followingDropdown}
             </div>
-
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index a75e7a0c4..1e7f4f10b 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, observable } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { Cast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
@@ -16,8 +16,7 @@ import { undoBatch } from '../../util/UndoManager';
 import { DocumentView } from '../nodes/DocumentView';
 import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
-import React = require("react");
-
+import React = require('react');
 
 interface LinkMenuItemProps {
     groupType: string;
@@ -50,22 +49,21 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
         };
         const containingView = docView.props.ContainingCollectionView;
         const finishDrag = (e: DragManager.DragCompleteEvent) =>
-            e.docDragData && (e.docDragData.droppedDocuments =
-                dragData.draggedDocuments.reduce((droppedDocs, d) => {
-                    const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView);
-                    if (dvs.length) {
-                        dvs.forEach(dv => droppedDocs.push(dv.props.Document));
-                    } else {
-                        droppedDocs.push(Doc.MakeAlias(d));
-                    }
-                    return droppedDocs;
-                }, [] as Doc[]));
+            e.docDragData &&
+            (e.docDragData.droppedDocuments = dragData.draggedDocuments.reduce((droppedDocs, d) => {
+                const dvs = DocumentManager.Instance.getDocumentViews(d).filter(dv => dv.props.ContainingCollectionView === containingView);
+                if (dvs.length) {
+                    dvs.forEach(dv => droppedDocs.push(dv.props.Document));
+                } else {
+                    droppedDocs.push(Doc.MakeAlias(d));
+                }
+                return droppedDocs;
+            }, [] as Doc[]));
 
         DragManager.StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag);
     }
 }
 
-
 @observer
 export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     private _drag = React.createRef<HTMLDivElement>();
@@ -74,20 +72,31 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     _buttonRef = React.createRef<HTMLDivElement>();
 
     @observable private _showMore: boolean = false;
-    @action toggleShowMore(e: React.PointerEvent) { e.stopPropagation(); this._showMore = !this._showMore; }
+    @action toggleShowMore(e: React.PointerEvent) {
+        e.stopPropagation();
+        this._showMore = !this._showMore;
+    }
 
     onEdit = (e: React.PointerEvent): void => {
         LinkManager.currentLink = this.props.linkDoc;
-        setupMoveUpEvents(this, e, e => {
-            const dragData = new DragManager.DocumentDragData([this.props.linkDoc], "alias");
-            dragData.removeDropProperties = ["hidden"];
-            DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
-            return true;
-        }, emptyFunction, () => this.props.showEditor(this.props.linkDoc));
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            e => {
+                const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'alias');
+                dragData.removeDropProperties = ['hidden'];
+                DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
+                return true;
+            },
+            emptyFunction,
+            () => this.props.showEditor(this.props.linkDoc)
+        );
+    };
 
     onLinkButtonDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e,
+        setupMoveUpEvents(
+            this,
+            e,
             e => {
                 const eleClone: any = this._drag.current!.cloneNode(true);
                 eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`;
@@ -99,109 +108,170 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             () => {
                 this.props.clearLinkEditor();
                 if (this.props.itemHandler) {
-
                     this.props.itemHandler?.(this.props.linkDoc);
                 } else {
-                    const focusDoc = Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc ? Cast(this.props.linkDoc.anchor1, Doc, null) :
-                        Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc ? Cast(this.props.linkDoc.anchor12, Doc, null) : undefined;
+                    const focusDoc =
+                        Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc
+                            ? Cast(this.props.linkDoc.anchor1, Doc, null)
+                            : Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc
+                            ? Cast(this.props.linkDoc.anchor12, Doc, null)
+                            : undefined;
 
                     if (focusDoc) this.props.docView.ComponentView?.scrollFocus?.(focusDoc, true);
                     LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
                 }
-            });
-    }
+            }
+        );
+    };
 
     deleteLink = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => {
-            this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
-            LinkManager.Instance.deleteLink(this.props.linkDoc);
-        })));
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            emptyFunction,
+            undoBatch(
+                action(() => {
+                    this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
+                    LinkManager.Instance.deleteLink(this.props.linkDoc);
+                })
+            )
+        );
+    };
 
     autoMove = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove)));
-    }
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove))));
+    };
 
     showLink = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay)));
-    }
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay))));
+    };
 
     showAnchor = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.hidden = !this.props.linkDoc.hidden)));
-    }
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.hidden = !this.props.linkDoc.hidden))));
+    };
 
     render() {
         const destinationIcon = Doc.toIcon(this.props.destinationDoc) as any as IconProp;
 
-        const title = StrCast(this.props.destinationDoc.title).length > 18 ?
-            StrCast(this.props.destinationDoc.title).substr(0, 14) + "..." : this.props.destinationDoc.title;
+        const title = StrCast(this.props.destinationDoc.title).length > 18 ? StrCast(this.props.destinationDoc.title).substr(0, 14) + '...' : this.props.destinationDoc.title;
 
         //  ...
         // from anika to bob: here's where the text that is specifically linked would show up (linkDoc.storedText)
         // ...
-        const source = this.props.sourceDoc.type === DocumentType.RTF ? this.props.linkDoc.storedText ?
-            StrCast(this.props.linkDoc.storedText).length > 17 ?
-                StrCast(this.props.linkDoc.storedText).substr(0, 18)
-                : this.props.linkDoc.storedText : undefined : undefined;
+        const source =
+            this.props.sourceDoc.type === DocumentType.RTF
+                ? this.props.linkDoc.storedText
+                    ? StrCast(this.props.linkDoc.storedText).length > 17
+                        ? StrCast(this.props.linkDoc.storedText).substr(0, 18)
+                        : this.props.linkDoc.storedText
+                    : undefined
+                : undefined;
 
         return (
             <div className="linkMenu-item">
-                <div className={"linkMenu-item-content expand-two"}>
-
-                    <div ref={this._drag} className="linkMenu-name" //title="drag to view target. click to customize."
+                <div className={'linkMenu-item-content expand-two'}>
+                    <div
+                        ref={this._drag}
+                        className="linkMenu-name" //title="drag to view target. click to customize."
                         onPointerLeave={LinkDocPreview.Clear}
-                        onPointerEnter={e => this.props.linkDoc && LinkDocPreview.SetLinkInfo({
-                            docProps: this.props.docView.props,
-                            linkSrc: this.props.sourceDoc,
-                            linkDoc: this.props.linkDoc,
-                            showHeader: false,
-                            location: [e.clientX, e.clientY + 20]
-                        })}
+                        onPointerEnter={e =>
+                            this.props.linkDoc &&
+                            LinkDocPreview.SetLinkInfo({
+                                docProps: this.props.docView.props,
+                                linkSrc: this.props.sourceDoc,
+                                linkDoc: this.props.linkDoc,
+                                showHeader: false,
+                                location: [e.clientX, e.clientY + 20],
+                            })
+                        }
                         onPointerDown={this.onLinkButtonDown}>
-
                         <div className="linkMenu-text">
-                            {source ? <p className="linkMenu-source-title"> <b>Source: {source}</b></p> : null}
+                            {source ? (
+                                <p className="linkMenu-source-title">
+                                    {' '}
+                                    <b>Source: {StrCast(source)}</b>
+                                </p>
+                            ) : null}
                             <div className="linkMenu-title-wrapper">
-                                <div className="destination-icon-wrapper" >
+                                <div className="destination-icon-wrapper">
                                     <FontAwesomeIcon className="destination-icon" icon={destinationIcon} size="sm" />
                                 </div>
                                 <p className="linkMenu-destination-title">
-                                    {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? "Annotation in" : ""} {title}
+                                    {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
                                 </p>
                             </div>
-                            {!this.props.linkDoc.description ? (null) : <p className="linkMenu-description">{StrCast(this.props.linkDoc.description)}</p>}
+                            {!this.props.linkDoc.description ? null : <p className="linkMenu-description">{StrCast(this.props.linkDoc.description)}</p>}
                         </div>
 
-                        <div className="linkMenu-item-buttons" ref={this._buttonRef} >
-
-                            <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.hidden ? "Show Link Anchor" : "Hide Link Anchor"}</div></>}>
-                                <div className="button" ref={this._editRef} style={{ background: this.props.linkDoc.hidden ? "" : "#4476f7" /* $medium-blue */ }} onPointerDown={this.showAnchor} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon={"eye"} size="sm" /></div>
+                        <div className="linkMenu-item-buttons" ref={this._buttonRef}>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{this.props.linkDoc.hidden ? 'Show Link Anchor' : 'Hide Link Anchor'}</div>
+                                    </>
+                                }>
+                                <div className="button" ref={this._editRef} style={{ background: this.props.linkDoc.hidden ? '' : '#4476f7' /* $medium-blue */ }} onPointerDown={this.showAnchor} onClick={e => e.stopPropagation()}>
+                                    <FontAwesomeIcon className="fa-icon" icon={'eye'} size="sm" />
+                                </div>
                             </Tooltip>
 
-                            <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.linkDisplay ? "Hide Link Line" : "Show Link Line"}</div></>}>
-                                <div className="button" ref={this._editRef} style={{ background: this.props.linkDoc.hidden ? "gray" : this.props.linkDoc.linkDisplay ? "#4476f7"/* $medium-blue */ : "" }} onPointerDown={this.showLink} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon={"project-diagram"} size="sm" /></div>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{this.props.linkDoc.linkDisplay ? 'Hide Link Line' : 'Show Link Line'}</div>
+                                    </>
+                                }>
+                                <div
+                                    className="button"
+                                    ref={this._editRef}
+                                    style={{ background: this.props.linkDoc.hidden ? 'gray' : this.props.linkDoc.linkDisplay ? '#4476f7' /* $medium-blue */ : '' }}
+                                    onPointerDown={this.showLink}
+                                    onClick={e => e.stopPropagation()}>
+                                    <FontAwesomeIcon className="fa-icon" icon={'project-diagram'} size="sm" />
+                                </div>
                             </Tooltip>
 
-                            <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.linkAutoMove ? "Click to freeze link anchor position" : "Click to auto move link anchor"}</div></>}>
-                                <div className="button" ref={this._editRef} style={{ background: this.props.linkDoc.hidden ? "gray" : !this.props.linkDoc.linkAutoMove ? "" : "#4476f7" /* $medium-blue */ }} onPointerDown={this.autoMove} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon={"play"} size="sm" /></div>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{this.props.linkDoc.linkAutoMove ? 'Click to freeze link anchor position' : 'Click to auto move link anchor'}</div>
+                                    </>
+                                }>
+                                <div
+                                    className="button"
+                                    ref={this._editRef}
+                                    style={{ background: this.props.linkDoc.hidden ? 'gray' : !this.props.linkDoc.linkAutoMove ? '' : '#4476f7' /* $medium-blue */ }}
+                                    onPointerDown={this.autoMove}
+                                    onClick={e => e.stopPropagation()}>
+                                    <FontAwesomeIcon className="fa-icon" icon={'play'} size="sm" />
+                                </div>
                             </Tooltip>
 
-                            <Tooltip title={<><div className="dash-tooltip">Edit Link</div></>}>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">Edit Link</div>
+                                    </>
+                                }>
                                 <div className="button" ref={this._editRef} onPointerDown={this.onEdit} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div>
+                                    <FontAwesomeIcon className="fa-icon" icon="edit" size="sm" />
+                                </div>
                             </Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">Delete Link</div></>}>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">Delete Link</div>
+                                    </>
+                                }>
                                 <div className="button" onPointerDown={this.deleteLink} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" /></div>
+                                    <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" />
+                                </div>
                             </Tooltip>
                         </div>
                     </div>
                 </div>
-
-            </div >
+            </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 96ac3e332..f1d8123da 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,48 +1,48 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { AclPrivate, Doc, Opt } from "../../../fields/Doc";
-import { ScriptField } from "../../../fields/ScriptField";
-import { Cast, StrCast } from "../../../fields/Types";
-import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
-import { emptyPath, OmitKeys, Without } from "../../../Utils";
-import { DirectoryImportBox } from "../../util/Import & Export/DirectoryImportBox";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { CollectionSchemaView } from "../collections/collectionSchema/CollectionSchemaView";
-import { CollectionView } from "../collections/CollectionView";
-import { InkingStroke } from "../InkingStroke";
-import { PresElementBox } from "../nodes/trails/PresElementBox";
-import { SearchBox } from "../search/SearchBox";
-import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
-import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
-import { AudioBox } from "./AudioBox";
-import { FontIconBox } from "./button/FontIconBox";
-import { ColorBox } from "./ColorBox";
-import { ComparisonBox } from "./ComparisonBox";
-import { DataVizBox } from "./DataVizBox/DataVizBox";
-import { DocumentViewProps } from "./DocumentView";
-import "./DocumentView.scss";
-import { EquationBox } from "./EquationBox";
-import { FieldView, FieldViewProps } from "./FieldView";
-import { FilterBox } from "./FilterBox";
-import { FormattedTextBox, FormattedTextBoxProps } from "./formattedText/FormattedTextBox";
-import { FunctionPlotBox } from "./FunctionPlotBox";
-import { ImageBox } from "./ImageBox";
-import { KeyValueBox } from "./KeyValueBox";
-import { LabelBox } from "./LabelBox";
-import { LinkAnchorBox } from "./LinkAnchorBox";
-import { LinkBox } from "./LinkBox";
-import { MapBox } from "./MapBox/MapBox";
-import { PDFBox } from "./PDFBox";
-import { RecordingBox } from "./RecordingBox";
-import { ScreenshotBox } from "./ScreenshotBox";
-import { ScriptingBox } from "./ScriptingBox";
-import { SliderBox } from "./SliderBox";
-import { PresBox } from "./trails/PresBox";
-import { VideoBox } from "./VideoBox";
-import { WebBox } from "./WebBox";
-import React = require("react");
-import XRegExp = require("xregexp");
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclPrivate, Doc, Opt } from '../../../fields/Doc';
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, StrCast } from '../../../fields/Types';
+import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
+import { emptyPath, OmitKeys, Without } from '../../../Utils';
+import { DirectoryImportBox } from '../../util/Import & Export/DirectoryImportBox';
+import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { CollectionSchemaView } from '../collections/collectionSchema/CollectionSchemaView';
+import { CollectionView } from '../collections/CollectionView';
+import { InkingStroke } from '../InkingStroke';
+import { PresElementBox } from '../nodes/trails/PresElementBox';
+import { SearchBox } from '../search/SearchBox';
+import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo';
+import { YoutubeBox } from './../../apis/youtube/YoutubeBox';
+import { AudioBox } from './AudioBox';
+import { FontIconBox } from './button/FontIconBox';
+import { ColorBox } from './ColorBox';
+import { ComparisonBox } from './ComparisonBox';
+import { DataVizBox } from './DataVizBox/DataVizBox';
+import { DocumentViewProps } from './DocumentView';
+import './DocumentView.scss';
+import { EquationBox } from './EquationBox';
+import { FieldView, FieldViewProps } from './FieldView';
+import { FilterBox } from './FilterBox';
+import { FormattedTextBox, FormattedTextBoxProps } from './formattedText/FormattedTextBox';
+import { FunctionPlotBox } from './FunctionPlotBox';
+import { ImageBox } from './ImageBox';
+import { KeyValueBox } from './KeyValueBox';
+import { LabelBox } from './LabelBox';
+import { LinkAnchorBox } from './LinkAnchorBox';
+import { LinkBox } from './LinkBox';
+import { MapBox } from './MapBox/MapBox';
+import { PDFBox } from './PDFBox';
+import { RecordingBox } from './RecordingBox';
+import { ScreenshotBox } from './ScreenshotBox';
+import { ScriptingBox } from './ScriptingBox';
+import { SliderBox } from './SliderBox';
+import { PresBox } from './trails/PresBox';
+import { VideoBox } from './VideoBox';
+import { WebBox } from './WebBox';
+import React = require('react');
+import XRegExp = require('xregexp');
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -60,7 +60,6 @@ class ObserverJsxParser1 extends JsxParser {
 
 const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 
-
 interface HTMLtagProps {
     Document: Doc;
     RootDoc: Doc;
@@ -68,16 +67,17 @@ interface HTMLtagProps {
     onClick?: ScriptField;
     onInput?: ScriptField;
     scaling: number;
+    children?: JSX.Element[];
 }
 
 //"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} overflow='hidden' position='absolute' width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'> <ImageBox {...props} fieldKey={'data'}/> <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px' transform='translate(-40px, 45px) rotate(-45deg)' position='absolute' color='{this.bannerColor===`green`?`light`:`dark`}blue' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}blue'> {this.title}</HTMLspan></HTMLdiv>"
-//"<HTMLdiv borderRadius='100px' overflow='hidden' position='absolute' width='100%' height='100%' 
-//          transform='rotate({2*this.x+this.y}deg)' 
+//"<HTMLdiv borderRadius='100px' overflow='hidden' position='absolute' width='100%' height='100%'
+//          transform='rotate({2*this.x+this.y}deg)'
 //          onClick = { this.bannerColor = this.bannerColor === 'red' ? 'green' : 'red' } >
 //    <ImageBox {...props} fieldKey={'data'}/>
-//    <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px' 
-//              transform='translate(-40px, 45px) rotate(-45deg)' position='absolute' 
-//              color='{this.bannerColor===`green`?`light`:`dark`}blue' 
+//    <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px'
+//              transform='translate(-40px, 45px) rotate(-45deg)' position='absolute'
+//              color='{this.bannerColor===`green`?`light`:`dark`}blue'
 //              backgroundColor='{this.bannerColor===`green`?`dark`:`light`}blue'>
 //       {this.title}
 //    </HTMLspan>
@@ -87,45 +87,51 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
     click = (e: React.MouseEvent) => {
         const clickScript = (this.props as any).onClick as Opt<ScriptField>;
         clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, scale: this.props.scaling });
-    }
+    };
     onInput = (e: React.FormEvent<HTMLDivElement>) => {
         const onInputScript = (this.props as any).onInput as Opt<ScriptField>;
         onInputScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, value: (e.target as any).textContent });
-    }
+    };
     render() {
         const style: { [key: string]: any } = {};
-        const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "scaling", "Document", "key", "onInput", "onClick", "__proto__"]).omit;
-        const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string:  { script }  into a value
-            return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string || "";
+        const divKeys = OmitKeys(this.props, ['children', 'htmltag', 'RootDoc', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit;
+        const replacer = (match: any, expr: string, offset: any, string: any) => {
+            // bcz: this executes a script to convert a propery expression string:  { script }  into a value
+            return (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string) || '';
         };
         Object.keys(divKeys).map((prop: string) => {
             const p = (this.props as any)[prop] as string;
             style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer);
         });
         const Tag = this.props.htmltag as keyof JSX.IntrinsicElements;
-        return <Tag style={style} onClick={this.click} onInput={this.onInput as any}>
-            {this.props.children}
-        </Tag>;
+        return (
+            <Tag style={style} onClick={this.click} onInput={this.onInput as any}>
+                {this.props.children}
+            </Tag>
+        );
     }
 }
 
 @observer
-export class DocumentContentsView extends React.Component<DocumentViewProps & FormattedTextBoxProps & {
-    isSelected: (outsideReaction: boolean) => boolean,
-    select: (ctrl: boolean) => void,
-    scaling?: () => number,
-    setHeight?: (height: number) => void,
-    layoutKey: string,
-}> {
+export class DocumentContentsView extends React.Component<
+    DocumentViewProps &
+        FormattedTextBoxProps & {
+            isSelected: (outsideReaction: boolean) => boolean;
+            select: (ctrl: boolean) => void;
+            scaling?: () => number;
+            setHeight?: (height: number) => void;
+            layoutKey: string;
+        }
+> {
     @computed get layout(): string {
         TraceMobx();
         if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
-        if (!this.layoutDoc) return "<p>awaiting layout</p>";
-        if (this.props.layoutKey === "layout_keyValue") return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
-        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string");
-        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : "");
-        if (typeof layout === "string") return layout;
-        return "<p>Loading layout</p>";
+        if (!this.layoutDoc) return '<p>awaiting layout</p>';
+        if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString('data'));
+        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, 'layout')], 'string');
+        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? 'proto' : '');
+        if (typeof layout === 'string') return layout;
+        return '<p>Loading layout</p>';
     }
 
     get dataDoc() {
@@ -136,36 +142,39 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
         const params = StrCast(this.props.Document.PARAMS);
         // bcz: replaced this with below : is it correct?  change was made to accommodate passing fieldKey's from a layout script
         // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
-        const template: Doc = this.props.LayoutTemplate?.() || (this.props.LayoutTemplateString && this.props.Document) ||
+        const template: Doc =
+            this.props.LayoutTemplate?.() ||
+            (this.props.LayoutTemplateString && this.props.Document) ||
             (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
             Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
-        return Doc.expandTemplateLayout(template, this.props.Document, params ? "(" + params + ")" : this.props.layoutKey);
+        return Doc.expandTemplateLayout(template, this.props.Document, params ? '(' + params + ')' : this.props.layoutKey);
     }
 
     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
-            "hideResizeHandles",
-            "hideTitle",
-            "treeViewDoc",
-            "contentPointerEvents",
-            "radialMenu",
-            "LayoutTemplateString",
-            "LayoutTemplate",
-            "dontCenter",
-            "ContentScaling",
-            "contextMenuItems",
-            "onClick",
-            "onDoubleClick",
-            "onPointerDown",
-            "onPointerUp",
+        const docOnlyProps = [
+            // these are the properties in  DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
+            'hideResizeHandles',
+            'hideTitle',
+            'treeViewDoc',
+            'contentPointerEvents',
+            'radialMenu',
+            'LayoutTemplateString',
+            'LayoutTemplate',
+            'dontCenter',
+            'ContentScaling',
+            'contextMenuItems',
+            'onClick',
+            'onDoubleClick',
+            'onPointerDown',
+            'onPointerUp',
         ];
         const list = {
-            ...OmitKeys(this.props, [...docOnlyProps], "").omit,
+            ...OmitKeys(this.props, [...docOnlyProps], '').omit,
             RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc,
             Document: this.layoutDoc,
             DataDoc: this.dataDoc,
             onClick: onClick,
-            onInput: onInput
+            onInput: onInput,
         };
         return { props: list };
     }
@@ -180,17 +189,17 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
 
         // replace code content with a script  >{content}<   as in  <HTMLdiv>{this.title}</HTMLdiv>
         const replacer = (match: any, prefix: string, expr: string, postfix: string, offset: any, string: any) => {
-            return prefix + (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "") + postfix;
+            return prefix + ((ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string) || '') + postfix;
         };
         layoutFrame = layoutFrame.replace(/(>[^{]*)[^=]\{([^.'][^<}]+)\}([^}]*<)/g, replacer);
 
-        // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'> 
+        // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'>
         const replacer2 = (match: any, p1: string, offset: any, string: any) => {
             return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.scaling?.() || 1}' htmltag='${p1}'`;
         };
         layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
 
-        // replace /HTML<tag> with </HTMLdiv>  as in:  </HTMLdiv> becomes  </HTMLtag> 
+        // replace /HTML<tag> with </HTMLdiv>  as in:  </HTMLdiv> becomes  </HTMLtag>
         const replacer3 = (match: any, p1: string, offset: any, string: any) => {
             return `</HTMLtag`;
         };
@@ -200,16 +209,16 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
         const makeFuncProp = (func: string) => {
             const splits = layoutFrame.split(`${func}=`);
             if (splits.length > 1) {
-                const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
+                const code = XRegExp.matchRecursive(splits[1], '{', '}', '', { valueNames: ['between', 'left', 'match', 'right', 'between'] });
                 layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1);
-                const script = code[1].value.replace(/^‘/, "").replace(/’$/, "");  // ‘’ are not valid quotes in javascript so get rid of them -- they may be present to make it easier to write complex scripts - see headerTemplate in currentUserUtils.ts
-                return ScriptField.MakeScript(script, { this: Doc.name, self: Doc.name, scale: "number", value: "string" });
+                const script = code[1].value.replace(/^‘/, '').replace(/’$/, ''); // ‘’ are not valid quotes in javascript so get rid of them -- they may be present to make it easier to write complex scripts - see headerTemplate in currentUserUtils.ts
+                return ScriptField.MakeScript(script, { this: Doc.name, self: Doc.name, scale: 'number', value: 'string' });
             }
             return undefined;
             // add input function to props
         };
-        const onClick = makeFuncProp("onClick");
-        const onInput = makeFuncProp("onInput");
+        const onClick = makeFuncProp('onClick');
+        const onInput = makeFuncProp('onInput');
         const bindings = this.CreateBindings(onClick, onInput);
         return { bindings, layoutFrame };
     }
@@ -218,23 +227,55 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
         TraceMobx();
         const { bindings, layoutFrame } = this.renderData;
 
-        return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate) ? (null) :
+        return this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate ? null : (
             <ObserverJsxParser
                 key={42}
                 blacklistedAttrs={emptyPath}
                 renderInWrapper={false}
                 components={{
-                    FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, EquationBox, SliderBox, FieldView,
-                    CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
-                    PDFBox, VideoBox, AudioBox, RecordingBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, FunctionPlotBox,
-                    ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, LinkBox, ScriptingBox, MapBox,
-                    ScreenshotBox, DataVizBox, HTMLtag, ComparisonBox
+                    FormattedTextBox,
+                    ImageBox,
+                    DirectoryImportBox,
+                    FontIconBox,
+                    LabelBox,
+                    EquationBox,
+                    SliderBox,
+                    FieldView,
+                    CollectionFreeFormView,
+                    CollectionDockingView,
+                    CollectionSchemaView,
+                    CollectionView,
+                    WebBox,
+                    KeyValueBox,
+                    PDFBox,
+                    VideoBox,
+                    AudioBox,
+                    RecordingBox,
+                    PresBox,
+                    YoutubeBox,
+                    PresElementBox,
+                    SearchBox,
+                    FilterBox,
+                    FunctionPlotBox,
+                    ColorBox,
+                    DashWebRTCVideo,
+                    LinkAnchorBox,
+                    InkingStroke,
+                    LinkBox,
+                    ScriptingBox,
+                    MapBox,
+                    ScreenshotBox,
+                    DataVizBox,
+                    HTMLtag,
+                    ComparisonBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
                 showWarnings={true}
-
-                onError={(test: any) => { console.log("DocumentContentsView:" + test); }}
-            />;
+                onError={(test: any) => {
+                    console.log('DocumentContentsView:' + test);
+                }}
+            />
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 6c7e174f7..47d5c662e 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -1,19 +1,19 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
-import { observer } from "mobx-react";
-import wiki from "wikijs";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from "../../../fields/Doc";
-import { NumCast, StrCast, Cast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents, Utils } from "../../../Utils";
+import { observer } from 'mobx-react';
+import wiki from 'wikijs';
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from '../../../fields/Doc';
+import { NumCast, StrCast, Cast } from '../../../fields/Types';
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
-import { Docs, DocUtils } from "../../documents/Documents";
+import { Docs, DocUtils } from '../../documents/Documents';
 import { LinkManager } from '../../util/LinkManager';
-import { Transform } from "../../util/Transform";
+import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
-import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
+import { DocumentView, DocumentViewSharedProps } from './DocumentView';
 import './LinkDocPreview.scss';
-import React = require("react");
+import React = require('react');
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 
@@ -27,15 +27,19 @@ interface LinkDocPreviewProps {
 }
 @observer
 export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
-    @action public static Clear() { LinkDocPreview.LinkInfo = undefined; }
-    @action public static SetLinkInfo(info?: LinkDocPreviewProps) { LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info); }
+    @action public static Clear() {
+        LinkDocPreview.LinkInfo = undefined;
+    }
+    @action public static SetLinkInfo(info?: LinkDocPreviewProps) {
+        LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info);
+    }
 
     _infoRef = React.createRef<HTMLDivElement>();
     @observable public static LinkInfo: Opt<LinkDocPreviewProps>;
     @observable _targetDoc: Opt<Doc>;
     @observable _linkDoc: Opt<Doc>;
     @observable _linkSrc: Opt<Doc>;
-    @observable _toolTipText = "";
+    @observable _toolTipText = '';
     @observable _hrefInd = 0;
 
     @action init() {
@@ -47,113 +51,136 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         if (anchor1 && anchor2) {
             linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1;
         }
-        if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) { // want to show annotation context document if annotation is not text
-            linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => this._targetDoc = anno));
+        if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) {
+            // want to show annotation context document if annotation is not text
+            linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._targetDoc = anno)));
         } else {
             this._targetDoc = linkTarget;
         }
-        this._toolTipText = "";
+        this._toolTipText = '';
     }
     componentDidUpdate(props: any) {
         if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init();
     }
     componentDidMount() {
         this.init();
-        document.addEventListener("pointerdown", this.onPointerDown);
+        document.addEventListener('pointerdown', this.onPointerDown);
     }
 
     componentWillUnmount() {
         LinkDocPreview.SetLinkInfo(undefined);
-        document.removeEventListener("pointerdown", this.onPointerDown);
+        document.removeEventListener('pointerdown', this.onPointerDown);
     }
 
     onPointerDown = (e: PointerEvent) => {
         !this._infoRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
-    }
+    };
 
     @computed get href() {
         if (this.props.hrefs?.length) {
             const href = this.props.hrefs[this._hrefInd];
-            if (href.indexOf(Doc.localServerPath()) !== 0) {  // link to a web page URL -- try to show a preview
-                if (href.startsWith("https://en.wikipedia.org/wiki/")) {
-                    wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
+            if (href.indexOf(Doc.localServerPath()) !== 0) {
+                // link to a web page URL -- try to show a preview
+                if (href.startsWith('https://en.wikipedia.org/wiki/')) {
+                    wiki()
+                        .page(href.replace('https://en.wikipedia.org/wiki/', ''))
+                        .then(page => page.summary().then(action(summary => (this._toolTipText = summary.substring(0, 500)))));
                 } else {
-                    setTimeout(action(() => this._toolTipText = "url => " + href));
+                    setTimeout(action(() => (this._toolTipText = 'url => ' + href)));
                 }
-            } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated
-                const anchorDoc = href.replace(Doc.localServerPath(), "").split("?")[0];
-                anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => {
-                    if (anchor instanceof Doc && DocListCast(anchor.links).length) {
-                        this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0];
-                        const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
-                        if (automaticLink) { // automatic links specify the target in the link info, not the source
-                            const linkTarget = anchor;
-                            this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
-                            this._targetDoc = linkTarget;
-                        } else {
-                            this._linkSrc = anchor;
-                            const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
-                            this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
-                        }
-                        this._toolTipText = "";
-                    }
-                }));
+            } else {
+                // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated
+                const anchorDoc = href.replace(Doc.localServerPath(), '').split('?')[0];
+                anchorDoc &&
+                    DocServer.GetRefField(anchorDoc).then(
+                        action(anchor => {
+                            if (anchor instanceof Doc && DocListCast(anchor.links).length) {
+                                this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0];
+                                const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
+                                if (automaticLink) {
+                                    // automatic links specify the target in the link info, not the source
+                                    const linkTarget = anchor;
+                                    this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
+                                    this._targetDoc = linkTarget;
+                                } else {
+                                    this._linkSrc = anchor;
+                                    const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
+                                    this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
+                                }
+                                this._toolTipText = '';
+                            }
+                        })
+                    );
             }
             return href;
         }
         return undefined;
     }
     deleteLink = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(() => this._linkDoc && LinkManager.Instance.deleteLink(this._linkDoc)));
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            emptyFunction,
+            undoBatch(() => this._linkDoc && LinkManager.Instance.deleteLink(this._linkDoc))
+        );
+    };
     nextHref = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, action(() => {
-            const nextHrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1);
-            if (nextHrefInd !== this._hrefInd) {
-                this._linkDoc = undefined;
-                this._hrefInd = nextHrefInd;
-            }
-        }), true);
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            emptyFunction,
+            action(() => {
+                const nextHrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1);
+                if (nextHrefInd !== this._hrefInd) {
+                    this._linkDoc = undefined;
+                    this._hrefInd = nextHrefInd;
+                }
+            }),
+            true
+        );
+    };
 
     followLink = (e: React.PointerEvent) => {
         if (this._linkDoc && this._linkSrc) {
             LinkDocPreview.Clear();
             LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
         } else if (this.props.hrefs?.length) {
-            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), "add:right");
+            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), 'add:right');
         }
-    }
+    };
     width = () => {
         if (!this._targetDoc) return 225;
         if (this._targetDoc[WidthSym]() < this._targetDoc?.[HeightSym]()) {
-            return Math.min(225, this._targetDoc[HeightSym]()) * this._targetDoc[WidthSym]() / this._targetDoc[HeightSym]();
+            return (Math.min(225, this._targetDoc[HeightSym]()) * this._targetDoc[WidthSym]()) / this._targetDoc[HeightSym]();
         }
         return Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
-    }
+    };
     height = () => {
         if (!this._targetDoc) return 225;
         if (this._targetDoc[WidthSym]() > this._targetDoc?.[HeightSym]()) {
-            return Math.min(225, this._targetDoc[WidthSym]()) * this._targetDoc[HeightSym]() / this._targetDoc[WidthSym]();
+            return (Math.min(225, this._targetDoc[WidthSym]()) * this._targetDoc[HeightSym]()) / this._targetDoc[WidthSym]();
         }
         return Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
-    }
+    };
     @computed get previewHeader() {
-        return !this._linkDoc || !this._targetDoc || !this._linkSrc ? (null) :
+        return !this._linkDoc || !this._targetDoc || !this._linkSrc ? null : (
             <div className="linkDocPreview-info" ref={this._infoRef}>
-                <div className="linkDocPreview-title" style={{ pointerEvents: "all" }}
+                <div
+                    className="linkDocPreview-title"
+                    style={{ pointerEvents: 'all' }}
                     onPointerDown={e => {
-                        DragManager.StartDocumentDrag([this._infoRef.current!],
-                            new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
+                        DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
                         e.stopPropagation();
                         LinkDocPreview.Clear();
                     }}>
-                    {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + "..." : this._targetDoc.title}
+                    {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + '...' : StrCast(this._targetDoc.title)}
                     <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
                 </div>
-                <div className="linkDocPreview-buttonBar" >
+                <div className="linkDocPreview-buttonBar">
                     <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
-                        <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? "gray" : "green" }} onPointerDown={this.nextHref}>
+                        <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
                             <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" />
                         </div>
                     </Tooltip>
@@ -164,20 +191,24 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                         </div>
                     </Tooltip>
                 </div>
-            </div>;
+            </div>
+        );
     }
 
     @computed get docPreview() {
         const href = this.href; // needs to be here to trigger lookup of web pages and docs on server
-        return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? (null) :
+        return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
             <div className="linkDocPreview-inner">
-                {!this.props.showHeader ? (null) : this.previewHeader}
-                <div className="linkDocPreview-preview-wrapper" style={{ maxHeight: this._toolTipText ? "100%" : undefined, overflow: "auto" }}>
-                    {this._toolTipText ? this._toolTipText :
-                        <DocumentView ref={(r) => {
-                            const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
-                            targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor);
-                        }}
+                {!this.props.showHeader ? null : this.previewHeader}
+                <div className="linkDocPreview-preview-wrapper" style={{ maxHeight: this._toolTipText ? '100%' : undefined, overflow: 'auto' }}>
+                    {this._toolTipText ? (
+                        this._toolTipText
+                    ) : (
+                        <DocumentView
+                            ref={r => {
+                                const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
+                                targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor);
+                            }}
                             Document={this._targetDoc!}
                             moveDocument={returnFalse}
                             rootSelected={returnFalse}
@@ -206,16 +237,19 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             bringToFront={returnFalse}
                             NativeWidth={Doc.NativeWidth(this._targetDoc) ? () => Doc.NativeWidth(this._targetDoc) : undefined}
                             NativeHeight={Doc.NativeHeight(this._targetDoc) ? () => Doc.NativeHeight(this._targetDoc) : undefined}
-                        />}
+                        />
+                    )}
                 </div>
-            </div>;
+            </div>
+        );
     }
 
     render() {
         const borders = 16; // 8px border on each side
-        return <div className="linkDocPreview" onPointerDown={this.followLink}
-            style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
-            {this.docPreview}
-        </div>;
+        return (
+            <div className="linkDocPreview" onPointerDown={this.followLink} style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+                {this.docPreview}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 0b7264d79..18a6b5453 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,8 +1,8 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
 import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
-import { observer } from "mobx-react";
-import * as React from "react";
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
@@ -22,17 +22,17 @@ import { Annotation } from '../../pdf/Annotation';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '../FieldView';
-import "./MapBox.scss";
+import './MapBox.scss';
 import { MapBoxInfoWindow } from './MapBoxInfoWindow';
 
 /**
  * MapBox architecture:
  * Main component: MapBox.tsx
  * Supporting Components: SidebarAnnos, CollectionStackingView
- * 
- * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content. 
- * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view. 
- * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available, 
+ *
+ * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
+ * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
+ * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
  * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
  * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
  * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
@@ -77,26 +77,30 @@ document.head.appendChild(script);
 //     },
 // });
 
-
 // options for searchbox in Google Maps Places Autocomplete API
 const options = {
-    fields: ["formatted_address", "geometry", "name"], // note: level of details is charged by item per retrieval, not recommended to return all fields
+    fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields
     strictBounds: false,
-    types: ["establishment"], // type pf places, subject of change according to user need
+    types: ['establishment'], // type pf places, subject of change according to user need
 } as google.maps.places.AutocompleteOptions;
 
 @observer
 export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
-
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     @observable private _overlayAnnoInfo: Opt<Doc>;
-    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapBox, fieldKey); }
-    public get SidebarKey() { return this.fieldKey + "-sidebar"; }
+    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(MapBox, fieldKey);
+    }
+    public get SidebarKey() {
+        return this.fieldKey + '-sidebar';
+    }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
-    @computed get inlineTextAnnotations() { return this.allMapMarkers.filter(a => a.textInlineAnnotations); }
+    @computed get inlineTextAnnotations() {
+        return this.allMapMarkers.filter(a => a.textInlineAnnotations);
+    }
 
     @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
     @observable private selectedPlace: Doc | undefined;
@@ -108,14 +112,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private searchMarkers: google.maps.Marker[] = [];
     @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); }
-    @computed get allMapMarkers() { return DocListCast(this.dataDoc[this.annotationKey]); }
+    @computed get allSidebarDocs() {
+        return DocListCast(this.dataDoc[this.SidebarKey]);
+    }
+    @computed get allMapMarkers() {
+        return DocListCast(this.dataDoc[this.annotationKey]);
+    }
     @observable private toggleAddMarker = false;
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
 
-
     @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;
+    }
 
     static _canAnnotate = true;
     static _hadSelection: boolean = false;
@@ -129,109 +138,106 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     private setSearchBox = (searchBox: any) => {
         this.searchBox = searchBox;
-    }
+    };
 
     // iterate allMarkers to size, center, and zoom map to contain all markers
     private fitBounds = (map: google.maps.Map) => {
         const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
-        const isFitting = this.allMapMarkers.reduce((fits, place) =>
-            fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
-        !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) =>
-            bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }),
-            new window.google.maps.LatLngBounds()));
-    }
+        const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
+        !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
+    };
 
     /**
      * Custom control for add marker button
-     * @param controlDiv 
-     * @param map 
+     * @param controlDiv
+     * @param map
      */
     private CenterControl = () => {
-        const controlDiv = document.createElement("div");
-        controlDiv.className = "mapBox-addMarker";
+        const controlDiv = document.createElement('div');
+        controlDiv.className = 'mapBox-addMarker';
         // Set CSS for the control border.
-        const controlUI = document.createElement("div");
-        controlUI.style.backgroundColor = "#fff";
-        controlUI.style.borderRadius = "3px";
-        controlUI.style.cursor = "pointer";
-        controlUI.style.marginTop = "10px";
-        controlUI.style.borderRadius = "4px";
-        controlUI.style.marginBottom = "22px";
-        controlUI.style.textAlign = "center";
-        controlUI.style.position = "absolute";
-        controlUI.style.width = "32px";
-        controlUI.style.height = "32px";
-        controlUI.title = "Click to toggle marker mode.  In marker mode, click on map to place a marker.";
-
-        const plIcon = document.createElement("img");
-        plIcon.src = "https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png";
-        plIcon.style.color = "rgb(25,25,25)";
-        plIcon.style.fontFamily = "Roboto,Arial,sans-serif";
-        plIcon.style.fontSize = "16px";
-        plIcon.style.lineHeight = "32px";
-        plIcon.style.left = "18";
-        plIcon.style.top = "15";
-        plIcon.style.position = "absolute";
+        const controlUI = document.createElement('div');
+        controlUI.style.backgroundColor = '#fff';
+        controlUI.style.borderRadius = '3px';
+        controlUI.style.cursor = 'pointer';
+        controlUI.style.marginTop = '10px';
+        controlUI.style.borderRadius = '4px';
+        controlUI.style.marginBottom = '22px';
+        controlUI.style.textAlign = 'center';
+        controlUI.style.position = 'absolute';
+        controlUI.style.width = '32px';
+        controlUI.style.height = '32px';
+        controlUI.title = 'Click to toggle marker mode.  In marker mode, click on map to place a marker.';
+
+        const plIcon = document.createElement('img');
+        plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
+        plIcon.style.color = 'rgb(25,25,25)';
+        plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+        plIcon.style.fontSize = '16px';
+        plIcon.style.lineHeight = '32px';
+        plIcon.style.left = '18';
+        plIcon.style.top = '15';
+        plIcon.style.position = 'absolute';
         plIcon.width = 14;
         plIcon.height = 14;
-        plIcon.innerHTML = "Add";
+        plIcon.innerHTML = 'Add';
         controlUI.appendChild(plIcon);
 
         // Set CSS for the control interior.
-        const markerIcon = document.createElement("img");
-        markerIcon.src = "https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png";
-        markerIcon.style.color = "rgb(25,25,25)";
-        markerIcon.style.fontFamily = "Roboto,Arial,sans-serif";
-        markerIcon.style.fontSize = "16px";
-        markerIcon.style.lineHeight = "32px";
-        markerIcon.style.left = "-2";
-        markerIcon.style.top = "1";
+        const markerIcon = document.createElement('img');
+        markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
+        markerIcon.style.color = 'rgb(25,25,25)';
+        markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+        markerIcon.style.fontSize = '16px';
+        markerIcon.style.lineHeight = '32px';
+        markerIcon.style.left = '-2';
+        markerIcon.style.top = '1';
         markerIcon.width = 30;
         markerIcon.height = 30;
-        markerIcon.style.position = "absolute";
-        markerIcon.innerHTML = "Add";
+        markerIcon.style.position = 'absolute';
+        markerIcon.innerHTML = 'Add';
         controlUI.appendChild(markerIcon);
 
         // Setup the click event listeners
-        controlUI.addEventListener("click", () => {
+        controlUI.addEventListener('click', () => {
             if (this.toggleAddMarker === true) {
                 this.toggleAddMarker = false;
-                console.log("add marker button status:" + this.toggleAddMarker);
-                controlUI.style.backgroundColor = "#fff";
-                markerIcon.style.color = "rgb(25,25,25)";
+                console.log('add marker button status:' + this.toggleAddMarker);
+                controlUI.style.backgroundColor = '#fff';
+                markerIcon.style.color = 'rgb(25,25,25)';
             } else {
                 this.toggleAddMarker = true;
-                console.log("add marker button status:" + this.toggleAddMarker);
-                controlUI.style.backgroundColor = "#4476f7";
-                markerIcon.style.color = "rgb(255,255,255)";
+                console.log('add marker button status:' + this.toggleAddMarker);
+                controlUI.style.backgroundColor = '#4476f7';
+                markerIcon.style.color = 'rgb(255,255,255)';
             }
         });
         controlDiv.appendChild(controlUI);
         return controlDiv;
-    }
+    };
 
     /**
      * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
      * @param position - the LatLng position where the marker is placed
-     * @param map 
+     * @param map
      */
     @action
     private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
         const marker = new google.maps.Marker({
             position: position,
-            map: map
+            map: map,
         });
         map.panTo(position);
         const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
-    }
+    };
 
     _loadPending = true;
     /**
      * store a reference to google map instance
      * setup the drawing manager on the top right corner of map
-     * fit map bounds to contain all markers 
-     * @param map 
+     * fit map bounds to contain all markers
+     * @param map
      */
     @action
     private loadHandler = (map: google.maps.Map) => {
@@ -256,7 +262,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5));
         map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
         setTimeout(() => {
-
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
                 this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
@@ -268,7 +273,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.placeMarker((e as any).latLng, map);
             }
         });
-    }
+    };
 
     @action
     centered = () => {
@@ -278,7 +283,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
-    }
+    };
 
     @action
     zoomChanged = () => {
@@ -287,64 +292,64 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
-    }
+    };
 
     /**
      * Load and render all map markers
-     * @param marker 
-     * @param place 
+     * @param marker
+     * @param place
      */
     @action
     private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
-        place[Id] ? this.markerMap[place[Id]] = marker : null;
-    }
+        place[Id] ? (this.markerMap[place[Id]] = marker) : null;
+    };
 
     /**
      * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
-     * @param e 
-     * @param place 
+     * @param e
+     * @param place
      */
     @action
     private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
         // set which place was clicked
         this.selectedPlace = place;
         place.infoWindowOpen = true;
-    }
+    };
 
     /**
      * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
-     * @param doc 
-     * @param sidebarKey 
-     * @returns 
+     * @param doc
+     * @param sidebarKey
+     * @returns
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        console.log("print all sidebar Docs");
+        console.log('print all sidebar Docs');
         console.log(this.allSidebarDocs);
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
             if (doc.lat !== undefined && doc.lng !== undefined) {
                 const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
-                doc.onClickBehavior = "enterPortal";
+                doc.onClickBehavior = 'enterPortal';
                 if (existingMarker) {
-                    Doc.AddDocToList(existingMarker, "data", doc);
+                    Doc.AddDocToList(existingMarker, 'data', doc);
                 } else {
                     const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
                     this.addDocument(marker, this.annotationKey);
                 }
             }
         }); //add to annotation list
-        console.log("sidebaraddDocument");
+        console.log('sidebaraddDocument');
         console.log(doc);
 
         return this.addDocument(doc, sidebarKey); // add to sidebar list
-    }
+    };
 
     /**
      * Removing documents from the sidebar
-     * @param doc 
-     * @param sidebarKey 
-     * @returns 
+     * @param doc
+     * @param sidebarKey
+     * @returns
      */
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (this.layoutDoc._showSidebar) this.toggleSidebar();
@@ -354,31 +359,43 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             console.log(this.allSidebarDocs);
         });
         return this.removeDocument(doc, sidebarKey);
-    }
+    };
 
     /**
      * Toggle sidebar onclick the tiny comment button on the top right corner
-     * @param e 
+     * @param e
      */
     sidebarBtnDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
-            const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-            const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-            const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
-            if (ratio >= 1) {
-                this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
-                this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
-            }
-            return false;
-        }, emptyFunction, this.toggleSidebar);
-    }
-
-    sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
-    @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
-    @computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                const localDelta = this.props
+                    .ScreenToLocalTransform()
+                    .scale(this.props.scaling?.() || 1)
+                    .transformDirection(delta[0], delta[1]);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+                const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
+                const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
+                if (ratio >= 1) {
+                    this.layoutDoc.nativeWidth = nativeWidth * ratio;
+                    this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
+                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                }
+                return false;
+            },
+            emptyFunction,
+            this.toggleSidebar
+        );
+    };
 
+    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    @computed get sidebarWidthPercent() {
+        return StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    }
+    @computed get sidebarColor() {
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
+    }
 
     /**
      * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
@@ -414,7 +431,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         };
 
         // put temporary cutomized marker on searched location
-        this.searchMarkers.forEach((marker) => {
+        this.searchMarkers.forEach(marker => {
             marker.setMap(null);
         });
         this.searchMarkers = [];
@@ -426,7 +443,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 position: place.geometry.location,
             })
         );
-    }
+    };
 
     /**
      * Handles toggle of sidebar on click the little comment button
@@ -435,95 +452,99 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         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 as any, StyleProp.WidgetColor + (annotated ? ":annotated" : ""));
-        return (!annotated) ? (null) :
-            <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
+        const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this.props.styleProvider?.(this.rootDoc, this.props as any, StyleProp.WidgetColor + (annotated ? ':annotated' : ''));
+        return !annotated ? 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>
+        );
     }
 
     // TODO: Adding highlight box layer to Maps
     @action
     toggleSidebar = () => {
         const prevWidth = this.sidebarWidth();
-        this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%";
+        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
         this.layoutDoc._width = this.layoutDoc._showSidebar ? 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;
-    }
+    };
 
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
         if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
-            setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                this._marqueeing = [e.clientX, e.clientY];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
         }
-    }
+    };
     @action finishMarquee = (x?: number, y?: number) => {
         this._marqueeing = undefined;
         this._isAnnotating = false;
         x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
-    }
+    };
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
         return this.addDocument(doc, annotationKey);
-    }
+    };
 
     pointerEvents = () => {
-        return this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ?
-            "all" :
-            SnappingManager.GetIsDragging() ?
-                undefined : "none";
-    }
+        return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
+    };
     @computed get annotationLayer() {
-        return <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
-            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation key={`${anno[Id]}-annotation`} {...this.props}
-                    fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />)}
-        </div>;
+        return (
+            <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+                {this.inlineTextAnnotations
+                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+                    .map(anno => (
+                        <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
+                    ))}
+            </div>
+        );
     }
 
     getAnchor = () => {
-        const anchor =
-            AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ??
-            this.rootDoc;
+        const anchor = AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ?? this.rootDoc;
         return anchor;
-    }
+    };
 
     /**
      * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
-     * @returns 
+     * @returns
      */
     private renderMarkers = () => {
         return this.allMapMarkers.map(place => (
-            <Marker
-                key={place[Id]}
-                position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }}
-                onLoad={marker => this.markerLoadHandler(marker, place)}
-                onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)}
-            />
+            <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
         ));
-    }
+    };
 
     // TODO: auto center on select a document in the sidebar
     private handleMapCenter = (map: google.maps.Map) => {
@@ -531,7 +552,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // if (SelectionManager.Views().lastElement()) {
         //     console.log(SelectionManager.Views().lastElement());
         // }
-    }
+    };
 
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
@@ -543,7 +564,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     savedAnnotations = () => this._savedAnnotations;
     render() {
-        const renderAnnotations = (docFilters?: () => string[]) => (null);
+        const renderAnnotations = (docFilters?: () => string[]) => null;
         // bcz: commmented this out.  Otherwise, any documents that are rendered with an InfoWindow of a marker
         //      will also be rendered as freeform annotations on the map. However, it doesn't seem that rendering
         //      freeform documents on the map does anything anyway, so getting rid of it for now.  Also, since documents
@@ -571,88 +592,88 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     addDocument={this.sidebarAddDocument}
         //     childPointerEvents={true}
         //     pointerEvents={CurrentUserUtils.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
-        return <div className="mapBox" ref={this._ref}>
-            {/*console.log(apiKey)*/}
-            {/* <LoadScript
+        return (
+            <div className="mapBox" ref={this._ref}>
+                {/*console.log(apiKey)*/}
+                {/* <LoadScript
                 googleMapsApiKey={apiKey!}
                 libraries={['places', 'drawing']}
             > */}
-            <div className="mapBox-wrapper"
-                onWheel={e => e.stopPropagation()}
-                onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()}
-                style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
-
-                <div style={{ mixBlendMode: "multiply" }}>
-                    {renderAnnotations(this.transparentFilter)}
+                <div className="mapBox-wrapper" onWheel={e => e.stopPropagation()} onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+                    <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
+                    {renderAnnotations(this.opaqueFilter)}
+                    {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
+                    {this.annotationLayer}
+                    <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+                        <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+                            <input className="mapBox-input" ref={this.inputRef} type="text" placeholder="Enter location" />
+                        </Autocomplete>
+
+                        {this.renderMarkers()}
+                        {this.allMapMarkers
+                            .filter(marker => marker.infoWindowOpen)
+                            .map(marker => (
+                                <MapBoxInfoWindow
+                                    key={marker[Id]}
+                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                    place={marker}
+                                    markerMap={this.markerMap}
+                                    PanelWidth={this.infoWidth}
+                                    PanelHeight={this.infoHeight}
+                                    moveDocument={this.moveDocument}
+                                    isAnyChildContentActive={this.isAnyChildContentActive}
+                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                />
+                            ))}
+                        {/* {this.handleMapCenter(this._map)} */}
+                    </GoogleMap>
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <MarqueeAnnotator
+                            rootDoc={this.rootDoc}
+                            anchorMenuClick={this.anchorMenuClick}
+                            scrollTop={0}
+                            down={this._marqueeing}
+                            scaling={returnOne}
+                            addDocument={this.addDocumentWrapper}
+                            docView={this.props.docViewPath().lastElement()}
+                            finishMarquee={this.finishMarquee}
+                            savedAnnotations={this.savedAnnotations}
+                            annotationLayer={this._annotationLayer.current}
+                            mainCont={this._mainCont.current}
+                        />
+                    )}
                 </div>
-                {renderAnnotations(this.opaqueFilter)}
-                {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
-                {this.annotationLayer}
-                <GoogleMap
-                    mapContainerStyle={mapContainerStyle}
-                    onZoomChanged={this.zoomChanged}
-                    onCenterChanged={this.centered}
-                    onLoad={this.loadHandler}
-                    options={mapOptions}
-                >
-                    <Autocomplete
-                        onLoad={this.setSearchBox}
-                        onPlaceChanged={this.handlePlaceChanged}>
-                        <input className="mapBox-input" ref={this.inputRef} type="text" placeholder="Enter location" />
-                    </Autocomplete>
-
-                    {this.renderMarkers()}
-                    {this.allMapMarkers.filter(marker => marker.infoWindowOpen).map(marker => <MapBoxInfoWindow key={marker[Id]}
-                        {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                        place={marker}
-                        markerMap={this.markerMap}
-                        PanelWidth={this.infoWidth}
-                        PanelHeight={this.infoHeight}
-                        moveDocument={this.moveDocument}
-                        isAnyChildContentActive={this.isAnyChildContentActive}
+                {/* </LoadScript > */}
+                <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                    <SidebarAnnos
+                        ref={this._sidebarRef}
+                        {...this.props}
+                        fieldKey={this.fieldKey}
+                        rootDoc={this.rootDoc}
+                        layoutDoc={this.layoutDoc}
+                        dataDoc={this.dataDoc}
+                        showSidebar={this.SidebarShown}
+                        nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    />)}
-                    {this.handleMapCenter(this._map)}
-                </GoogleMap>
-                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                    <MarqueeAnnotator rootDoc={this.rootDoc}
-                        anchorMenuClick={this.anchorMenuClick}
-                        scrollTop={0}
-                        down={this._marqueeing} scaling={returnOne}
-                        addDocument={this.addDocumentWrapper}
-                        docView={this.props.docViewPath().lastElement()}
-                        finishMarquee={this.finishMarquee}
-                        savedAnnotations={this.savedAnnotations}
-                        annotationLayer={this._annotationLayer.current}
-                        mainCont={this._mainCont.current} />}
-            </div>
-            {/* </LoadScript > */}
-            <div className="mapBox-sidebar"
-                style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
-                <SidebarAnnos ref={this._sidebarRef}
-                    {...this.props}
-                    fieldKey={this.fieldKey}
-                    rootDoc={this.rootDoc}
-                    layoutDoc={this.layoutDoc}
-                    dataDoc={this.dataDoc}
-                    showSidebar={this.SidebarShown}
-                    nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
-                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    PanelWidth={this.sidebarWidth}
-                    sidebarAddDocument={this.sidebarAddDocument}
-                    moveDocument={this.moveDocument}
-                    removeDocument={this.sidebarRemoveDocument}
-                />
-            </div>
-            <div className="mapBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
-                style={{
-                    display: !this.props.isContentActive() ? "none" : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
-                    backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
-                }}
-                onPointerDown={this.sidebarBtnDown} >
-                <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                        PanelWidth={this.sidebarWidth}
+                        sidebarAddDocument={this.sidebarAddDocument}
+                        moveDocument={this.moveDocument}
+                        removeDocument={this.sidebarRemoveDocument}
+                    />
+                </div>
+                <div
+                    className="mapBox-overlayButton-sidebar"
+                    key="sidebar"
+                    title="Toggle Sidebar"
+                    style={{
+                        display: !this.props.isContentActive() ? 'none' : undefined,
+                        top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+                    }}
+                    onPointerDown={this.sidebarBtnDown}>
+                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+                </div>
             </div>
-        </div>;
+        );
     }
 }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 1fb3cef2a..5b9d90780 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,12 +1,12 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
-import { observer } from "mobx-react";
-import * as Pdfjs from "pdfjs-dist";
-import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { observer } from 'mobx-react';
+import * as Pdfjs from 'pdfjs-dist';
+import 'pdfjs-dist/web/pdf_viewer.css';
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
-import { ImageField, PdfField } from "../../../fields/URLField";
+import { ImageField, PdfField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -15,25 +15,27 @@ import { KeyCodes } from '../../util/KeyCodes';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { Colors } from '../global/globalEnums';
-import { CreateImage } from "../nodes/WebBoxRenderer";
+import { CreateImage } from '../nodes/WebBoxRenderer';
 import { AnchorMenu } from '../pdf/AnchorMenu';
-import { PDFViewer } from "../pdf/PDFViewer";
+import { PDFViewer } from '../pdf/PDFViewer';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { FieldView, FieldViewProps } from './FieldView';
 import { ImageBox } from './ImageBox';
-import "./PDFBox.scss";
+import './PDFBox.scss';
 import { VideoBox } from './VideoBox';
-import React = require("react");
+import React = require('react');
 import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(PDFBox, fieldKey);
+    }
     public static openSidebarWidth = 250;
     public static sidebarResizerWidth = 5;
-    private _searchString: string = "";
+    private _searchString: string = '';
     private _initialScrollTarget: Opt<Doc>;
     private _pdfViewer: PDFViewer | undefined;
     private _searchRef = React.createRef<HTMLInputElement>();
@@ -44,8 +46,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
     @observable private _pageControls = false;
 
-    @computed get pdfUrl() { return Cast(this.dataDoc[this.props.fieldKey], PdfField); }
-    @computed get pdfThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; }
+    @computed get pdfUrl() {
+        return Cast(this.dataDoc[this.props.fieldKey], PdfField);
+    }
+    @computed get pdfThumb() {
+        return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url;
+    }
 
     constructor(props: any) {
         super(props);
@@ -53,8 +59,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200;
         !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
         if (this.pdfUrl) {
-            if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href));
-            else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => this._pdf = pdf));
+            if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => (this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)));
+            else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => (this._pdf = pdf)));
         }
     }
 
@@ -65,15 +71,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
 
-        if (oldDiv.className === "pdfBox-ui" ||
-            oldDiv.className === "pdfViewerDash-overlay-inking") {
-            newDiv.style.display = "none";
+        if (oldDiv.className === 'pdfBox-ui' || oldDiv.className === 'pdfViewerDash-overlay-inking') {
+            newDiv.style.display = 'none';
         }
-        if (newDiv && newDiv.style) newDiv.style.overflow = "hidden";
+        if (newDiv && newDiv.style) newDiv.style.overflow = 'hidden';
         if (oldDiv instanceof HTMLCanvasElement) {
             const canvas = oldDiv;
             const img = document.createElement('img'); // create a Image Element
-            img.src = canvas.toDataURL();   //image sourcez
+            img.src = canvas.toDataURL(); //image sourcez
             img.style.width = canvas.style.width;
             img.style.height = canvas.style.height;
             const newCan = newDiv as HTMLCanvasElement;
@@ -81,20 +86,20 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             parEle.removeChild(newCan);
             parEle.appendChild(img);
         }
-    }
+    };
 
     crop = (region: Doc | undefined, addCrop?: boolean) => {
         if (!region) return;
         const cropping = Doc.MakeCopy(region, true);
         Doc.GetProto(region).lockedPosition = true;
-        Doc.GetProto(region).title = "region:" + this.rootDoc.title;
+        Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
         Doc.GetProto(region).isPushpin = true;
         this.addDocument(region);
 
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
-        newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
-        newDiv.style.height = (this.layoutDoc[HeightSym]()).toString();
+        newDiv.style.width = this.layoutDoc[WidthSym]().toString();
+        newDiv.style.height = this.layoutDoc[HeightSym]().toString();
         this.replaceCanvases(docViewContent, newDiv);
         const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv);
 
@@ -103,7 +108,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchw = cropping[WidthSym]() * (this.props.scaling?.() || 1);
         const anchh = cropping[HeightSym]() * (this.props.scaling?.() || 1);
         const viewScale = 1;
-        cropping.title = "crop: " + this.rootDoc.title;
+        cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
         cropping._width = anchw;
@@ -114,100 +119,113 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         croppingProto.isPrototype = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
-        croppingProto.layout = ImageBox.LayoutString("data");
-        croppingProto.data = new ImageField(Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png"));
-        croppingProto["data-nativeWidth"] = anchw;
-        croppingProto["data-nativeHeight"] = anchh;
+        croppingProto.layout = ImageBox.LayoutString('data');
+        croppingProto.data = new ImageField(Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png'));
+        croppingProto['data-nativeWidth'] = anchw;
+        croppingProto['data-nativeHeight'] = anchh;
         if (addCrop) {
-            DocUtils.MakeLink({ doc: region }, { doc: cropping }, "cropped image", "");
+            DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
         }
         this.props.bringToFront(cropping);
 
         CreateImage(
-            "",
+            '',
             document.styleSheets,
             htmlString,
             anchw,
             anchh,
-            NumCast(region.y) * this.props.PanelWidth() / NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]),
-            NumCast(region.x) * this.props.PanelWidth() / NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]),
+            (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']),
+            (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']),
             4
-        ).then
-            ((data_url: any) => {
-                VideoBox.convertDataUri(data_url, region[Id]).then(
-                    returnedfilename => setTimeout(action(() => {
-                        croppingProto.data = new ImageField(returnedfilename);
-                    }), 500));
+        )
+            .then((data_url: any) => {
+                VideoBox.convertDataUri(data_url, region[Id]).then(returnedfilename =>
+                    setTimeout(
+                        action(() => {
+                            croppingProto.data = new ImageField(returnedfilename);
+                        }),
+                        500
+                    )
+                );
             })
             .catch(function (error: any) {
                 console.error('oops, something went wrong!', error);
             });
 
-
         return cropping;
-    }
+    };
 
     updateIcon = () => {
         // currently we render pdf icons as text labels
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
-        const filename = this.layoutDoc[Id] + "-icon" + (new Date()).getTime();
-        this._pdfViewer?._mainCont.current && CollectionFreeFormView.UpdateIcon(
-            filename, docViewContent, 
-            this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), 
-            this.props.PanelWidth(), this.props.PanelHeight(),  
-            NumCast(this.layoutDoc._scrollTop),
-            NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"], 1), 
-            true,  
-            this.layoutDoc[Id] + "-icon",
-            (iconFile:string, nativeWidth:number, nativeHeight:number) => {
-                setTimeout(() => {
-                    this.dataDoc.icon = new ImageField(iconFile);
-                    this.dataDoc["icon-nativeWidth"] = nativeWidth;
-                    this.dataDoc["icon-nativeHeight"] = nativeHeight;
-                }, 500);
-            });
-    }
+        const filename = this.layoutDoc[Id] + '-icon' + new Date().getTime();
+        this._pdfViewer?._mainCont.current &&
+            CollectionFreeFormView.UpdateIcon(
+                filename,
+                docViewContent,
+                this.layoutDoc[WidthSym](),
+                this.layoutDoc[HeightSym](),
+                this.props.PanelWidth(),
+                this.props.PanelHeight(),
+                NumCast(this.layoutDoc._scrollTop),
+                NumCast(this.rootDoc[this.fieldKey + '-nativeHeight'], 1),
+                true,
+                this.layoutDoc[Id] + '-icon',
+                (iconFile: string, nativeWidth: number, nativeHeight: number) => {
+                    setTimeout(() => {
+                        this.dataDoc.icon = new ImageField(iconFile);
+                        this.dataDoc['icon-nativeWidth'] = nativeWidth;
+                        this.dataDoc['icon-nativeHeight'] = nativeHeight;
+                    }, 500);
+                }
+            );
+    };
 
-    componentWillUnmount() { this._selectReactionDisposer?.(); }
+    componentWillUnmount() {
+        this._selectReactionDisposer?.();
+    }
     componentDidMount() {
         this.props.setContentView?.(this);
-        this._selectReactionDisposer = reaction(() => this.props.isSelected(),
+        this._selectReactionDisposer = reaction(
+            () => this.props.isSelected(),
             () => {
-                document.removeEventListener("keydown", this.onKeyDown);
-                this.props.isSelected(true) && document.addEventListener("keydown", this.onKeyDown);
-            }, { fireImmediately: true });
+                document.removeEventListener('keydown', this.onKeyDown);
+                this.props.isSelected(true) && document.addEventListener('keydown', this.onKeyDown);
+            },
+            { fireImmediately: true }
+        );
     }
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
         let didToggle = false;
-        if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
             didToggle = true;
         }
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         this._initialScrollTarget = doc;
         return this._pdfViewer?.scrollFocus(doc, smooth) ?? (didToggle ? 1 : undefined);
-    }
+    };
     getAnchor = () => {
         const anchor =
             this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ??
             Docs.Create.TextanchorDocument({
-                title: StrCast(this.rootDoc.title + "@" + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
+                title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
                 y: NumCast(this.layoutDoc._scrollTop),
-                unrendered: true
+                unrendered: true,
             });
         this.addDocument(anchor);
         return anchor;
-    }
+    };
 
     @action
     loaded = (nw: number, nh: number, np: number) => {
-        this.dataDoc[this.props.fieldKey + "-numPages"] = np;
-        Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), nw * 96 / 72));
-        Doc.SetNativeHeight(this.dataDoc, nh * 96 / 72);
+        this.dataDoc[this.props.fieldKey + '-numPages'] = np;
+        Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), (nw * 96) / 72));
+        Doc.SetNativeHeight(this.dataDoc, (nh * 96) / 72);
         this.layoutDoc._height = this.layoutDoc[WidthSym]() / (Doc.NativeAspect(this.dataDoc) || 1);
         !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
-    }
+    };
 
     public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => {
         if (!this._searching && !clear) {
@@ -225,19 +243,23 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public backPage = () => {
         this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1);
         return true;
-    }
+    };
     public forwardPage = () => {
-        this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + "-numPages"]), (NumCast(this.Document._curPage) || 1) + 1);
+        this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '-numPages']), (NumCast(this.Document._curPage) || 1) + 1);
         return true;
-    }
-    public gotoPage = (p: number) => this.Document._curPage = p;
+    };
+    public gotoPage = (p: number) => (this.Document._curPage = p);
 
     @undoBatch
     onKeyDown = action((e: KeyboardEvent) => {
         let processed = false;
         switch (e.key) {
-            case "PageDown": processed = this.forwardPage(); break;
-            case "PageUp": processed = this.backPage(); break;
+            case 'PageDown':
+                processed = this.forwardPage();
+                break;
+            case 'PageUp':
+                processed = this.backPage();
+                break;
         }
         if (processed) {
             e.stopImmediatePropagation();
@@ -251,131 +273,163 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.scrollFocus(this._initialScrollTarget, false);
             this._initialScrollTarget = undefined;
         }
-    }
-    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
+    };
+    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
 
     // adding external documents; to sidebar key
     // if (doc.Geolocation) this.addDocument(doc, this.fieldkey+"-annotation")
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         return this.addDocument(doc, sidebarKey);
-    }
-    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
-        const batch = UndoManager.StartBatch("sidebar");
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
-            const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-            const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-            const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
-            if (ratio >= 1) {
-                this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
-                this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
-            }
-            return false;
-        }, () => batch.end(), () => this.toggleSidebar());
-    }
+    };
+    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => {
+        // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
+        const batch = UndoManager.StartBatch('sidebar');
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                const localDelta = this.props
+                    .ScreenToLocalTransform()
+                    .scale(this.props.scaling?.() || 1)
+                    .transformDirection(delta[0], delta[1]);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+                const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
+                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / nativeWidth;
+                if (ratio >= 1) {
+                    this.layoutDoc.nativeWidth = nativeWidth * ratio;
+                    onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
+                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                }
+                return false;
+            },
+            () => batch.end(),
+            () => this.toggleSidebar()
+        );
+    };
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
-        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
+        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
         const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
         if (preview) {
             this._previewNativeWidth = nativeWidth * sideratio;
-            this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
+            this._previewWidth = (this.layoutDoc[WidthSym]() * nativeWidth * sideratio) / curNativeWidth;
             this._showSidebar = true;
-        }
-        else {
+        } else {
             this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
-            this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
+            this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
             this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
         }
     });
     settingsPanel() {
-        const pageBtns = <>
-            <button className="pdfBox-backBtn" key="back" title="Page Back"
-                onPointerDown={e => e.stopPropagation()} onClick={this.backPage} >
-                <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-left"} size="sm" />
-            </button>
-            <button className="pdfBox-fwdBtn" key="fwd" title="Page Forward"
-                onPointerDown={e => e.stopPropagation()} onClick={this.forwardPage} >
-                <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-right"} size="sm" />
-            </button>
-        </>;
-        const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`;
+        const pageBtns = (
+            <>
+                <button className="pdfBox-backBtn" key="back" title="Page Back" onPointerDown={e => e.stopPropagation()} onClick={this.backPage}>
+                    <FontAwesomeIcon style={{ color: 'white' }} icon={'arrow-left'} size="sm" />
+                </button>
+                <button className="pdfBox-fwdBtn" key="fwd" title="Page Forward" onPointerDown={e => e.stopPropagation()} onClick={this.forwardPage}>
+                    <FontAwesomeIcon style={{ color: 'white' }} icon={'arrow-right'} size="sm" />
+                </button>
+            </>
+        );
+        const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`;
         const curPage = NumCast(this.Document._curPage) || 1;
-        return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? (null) :
-            <div className="pdfBox-ui" onKeyDown={e => [KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true}
-                onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
-                <div className="pdfBox-overlayCont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
+        return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? null : (
+            <div
+                className="pdfBox-ui"
+                onKeyDown={e => ([KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true)}
+                onPointerDown={e => e.stopPropagation()}
+                style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
+                <div className="pdfBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
                     <button className="pdfBox-overlayButton" title={searchTitle} />
-                    <input className="pdfBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged}
+                    <input
+                        className="pdfBox-searchBar"
+                        placeholder="Search"
+                        ref={this._searchRef}
+                        onChange={this.searchStringChanged}
                         onKeyDown={e => {
                             e.stopPropagation();
                             e.keyCode === KeyCodes.ENTER && this.search(this._searchString, e.shiftKey);
-                        }} />
+                        }}
+                    />
                     <button className="pdfBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}>
                         <FontAwesomeIcon icon="search" size="sm" />
                     </button>
-                    <button className="pdfBox-prevIcon" title="Previous Annotation" onClick={this.prevAnnotation} >
-                        <FontAwesomeIcon icon={"arrow-up"} size="lg" />
+                    <button className="pdfBox-prevIcon" title="Previous Annotation" onClick={this.prevAnnotation}>
+                        <FontAwesomeIcon icon={'arrow-up'} size="lg" />
                     </button>
-                    <button className="pdfBox-nextIcon" title="Next Annotation" onClick={this.nextAnnotation} >
-                        <FontAwesomeIcon icon={"arrow-down"} size="lg" />
+                    <button className="pdfBox-nextIcon" title="Next Annotation" onClick={this.nextAnnotation}>
+                        <FontAwesomeIcon icon={'arrow-down'} size="lg" />
                     </button>
                 </div>
-                <button className="pdfBox-overlayButton" title={searchTitle}
-                    onClick={action(() => { this._searching = !this._searching; this.search("", true, true); })} >
-                    <div className="pdfBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()} />
-                    <div className="pdfBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
-                        <FontAwesomeIcon icon={this._searching ? "times" : "search"} size="lg" />
+                <button
+                    className="pdfBox-overlayButton"
+                    title={searchTitle}
+                    onClick={action(() => {
+                        this._searching = !this._searching;
+                        this.search('', true, true);
+                    })}>
+                    <div className="pdfBox-overlayButton-arrow" onPointerDown={e => e.stopPropagation()} />
+                    <div className="pdfBox-overlayButton-iconCont" onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon icon={this._searching ? 'times' : 'search'} size="lg" />
                     </div>
                 </button>
 
                 <div className="pdfBox-pageNums">
-                    <input value={curPage} style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: "all" }}
-                        onChange={e => this.Document._curPage = Number(e.currentTarget.value)}
+                    <input
+                        value={curPage}
+                        style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: 'all' }}
+                        onChange={e => (this.Document._curPage = Number(e.currentTarget.value))}
                         onKeyDown={e => e.stopPropagation()}
-                        onClick={action(() => this._pageControls = !this._pageControls)} />
-                    {this._pageControls ? pageBtns : (null)}
+                        onClick={action(() => (this._pageControls = !this._pageControls))}
+                    />
+                    {this._pageControls ? pageBtns : null}
                 </div>
-                <div className="pdfBox-sidebarBtn" key="sidebar" title="Toggle Sidebar"
+                <div
+                    className="pdfBox-sidebarBtn"
+                    key="sidebar"
+                    title="Toggle Sidebar"
                     style={{
-                        display: !this.props.isContentActive() ? "none" : undefined,
-                        top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
-                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
+                        display: !this.props.isContentActive() ? 'none' : undefined,
+                        top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                     }}
-                    onPointerDown={e => this.sidebarBtnDown(e, true)} >
-                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                    onPointerDown={e => this.sidebarBtnDown(e, true)}>
+                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
                 </div>
-            </div>;
+            </div>
+        );
     }
-    sidebarWidth = () => !this.SidebarShown ? 0 :
-        PDFBox.sidebarResizerWidth + (this._previewWidth ? PDFBox.openSidebarWidth :
-            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
+    sidebarWidth = () =>
+        !this.SidebarShown ? 0 : PDFBox.sidebarResizerWidth + (this._previewWidth ? PDFBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: "Copy path", event: () => this.pdfUrl && Utils.CopyText(Utils.prepend("") + this.pdfUrl.url.pathname), icon: "expand-arrows-alt" });
-        funcs.push({ description: "update icon", event: () => this.pdfUrl && this.updateIcon(), icon: "expand-arrows-alt" });
+        funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' });
+        funcs.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' });
         //funcs.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" });
-        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
-    }
+        ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
+    };
 
     @computed get renderTitleBox() {
-        const classname = "pdfBox" + (this.props.isContentActive() ? "-interactive" : "");
-        return <div className={classname} >
-            <div className="pdfBox-title-outer">
-                <strong className="pdfBox-title" >{this.props.Document.title}</strong>
+        const classname = 'pdfBox' + (this.props.isContentActive() ? '-interactive' : '');
+        return (
+            <div className={classname}>
+                <div className="pdfBox-title-outer">
+                    <strong className="pdfBox-title">{StrCast(this.props.Document.title)}</strong>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     @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;
+    }
 
     contentScaling = () => 1;
     isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected();
@@ -383,54 +437,60 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         TraceMobx();
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
-        return <div className={"pdfBox"} onContextMenu={this.specificContextMenu}
-            style={{
-                display: this.props.thumbShown?.() ? "none" : undefined,
-                height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ?
-                    NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined
-            }}>
-            <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
-            <div style={{
-                width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
-                height: `${100 / scale}%`,
-                transform: `scale(${scale})`,
-                position: "absolute",
-                transformOrigin: "top left",
-                top: 0,
-            }}>
-                <PDFViewer {...this.props}
+        return (
+            <div
+                className={'pdfBox'}
+                onContextMenu={this.specificContextMenu}
+                style={{
+                    display: this.props.thumbShown?.() ? 'none' : undefined,
+                    height: this.props.Document._scrollTop && !this.Document._fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined,
+                }}>
+                <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
+                <div
+                    style={{
+                        width: `calc(${100 / scale}% - ${(this.sidebarWidth() / scale) * (this._previewWidth ? scale : 1)}px)`,
+                        height: `${100 / scale}%`,
+                        transform: `scale(${scale})`,
+                        position: 'absolute',
+                        transformOrigin: 'top left',
+                        top: 0,
+                    }}>
+                    <PDFViewer
+                        {...this.props}
+                        rootDoc={this.rootDoc}
+                        layoutDoc={this.layoutDoc}
+                        dataDoc={this.dataDoc}
+                        pdf={this._pdf!}
+                        url={this.pdfUrl!.url.pathname}
+                        isContentActive={this.isPdfContentActive}
+                        anchorMenuClick={this.anchorMenuClick}
+                        loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
+                        setPdfViewer={this.setPdfViewer}
+                        addDocument={this.addDocument}
+                        moveDocument={this.moveDocument}
+                        removeDocument={this.removeDocument}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                        crop={this.crop}
+                        ContentScaling={returnOne}
+                    />
+                </div>
+                <SidebarAnnos
+                    ref={this._sidebarRef}
+                    {...this.props}
                     rootDoc={this.rootDoc}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
-                    pdf={this._pdf!}
-                    url={this.pdfUrl!.url.pathname}
-                    isContentActive={this.isPdfContentActive}
-                    anchorMenuClick={this.anchorMenuClick}
-                    loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
-                    setPdfViewer={this.setPdfViewer}
-                    addDocument={this.addDocument}
+                    setHeight={emptyFunction}
+                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
+                    showSidebar={this.SidebarShown}
+                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                    sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
-                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    crop={this.crop}
-                    ContentScaling={returnOne}
                 />
+                {this.settingsPanel()}
             </div>
-            <SidebarAnnos ref={this._sidebarRef}
-                {...this.props}
-                rootDoc={this.rootDoc}
-                layoutDoc={this.layoutDoc}
-                dataDoc={this.dataDoc}
-                setHeight={emptyFunction}
-                nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
-                showSidebar={this.SidebarShown}
-                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                sidebarAddDocument={this.sidebarAddDocument}
-                moveDocument={this.moveDocument}
-                removeDocument={this.removeDocument}
-            />
-            {this.settingsPanel()}
-        </div>;
+        );
     }
 
     static pdfcache = new Map<string, Pdfjs.PDFDocumentProxy>();
@@ -446,13 +506,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         const href = this.pdfUrl?.url.href;
         if (href) {
-            if (PDFBox.pdfcache.get(href)) setTimeout(action(() => this._pdf = PDFBox.pdfcache.get(href)));
+            if (PDFBox.pdfcache.get(href)) setTimeout(action(() => (this._pdf = PDFBox.pdfcache.get(href))));
             else {
                 if (!PDFBox.pdfpromise.get(href)) PDFBox.pdfpromise.set(href, Pdfjs.getDocument(href).promise);
-                PDFBox.pdfpromise.get(href)?.then(action((pdf: any) => PDFBox.pdfcache.set(href, this._pdf = pdf)));
+                PDFBox.pdfpromise.get(href)?.then(action((pdf: any) => PDFBox.pdfcache.set(href, (this._pdf = pdf))));
             }
         }
         return this.renderTitleBox;
     }
 }
-
diff --git a/src/client/views/nodes/formattedText/schema_rts.ts b/src/client/views/nodes/formattedText/schema_rts.ts
index 83561073c..d6e0e6002 100644
--- a/src/client/views/nodes/formattedText/schema_rts.ts
+++ b/src/client/views/nodes/formattedText/schema_rts.ts
@@ -1,8 +1,7 @@
-import { Schema, Slice } from "prosemirror-model";
-
-import { nodes } from "./nodes_rts";
-import { marks } from "./marks_rts";
+import { Schema, Slice } from 'prosemirror-model';
 
+import { nodes } from './nodes_rts';
+import { marks } from './marks_rts';
 
 // :: Schema
 // This schema rougly corresponds to the document schema used by
@@ -20,7 +19,9 @@ const fromJson = schema.nodeFromJSON;
 schema.nodeFromJSON = (json: any) => {
     const node = fromJson(json);
     if (json.type === schema.nodes.summary.name) {
-        node.attrs.text = Slice.fromJSON(schema, node.attrs.textslice);
+        // bcz: this is a hacky way to convert the JSON that's serialized for a summary node into the Slice that the summary node wants at run-time.
+        //      since attrs are readonly, assigning the text field like this violates the way prosemirror works, but I think we can get away with it.
+        (node.attrs.text as any) = Slice.fromJSON(schema, node.attrs.textslice);
     }
     return node;
-};
\ No newline at end of file
+};
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 7045d6d5d..91f8d1efc 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,55 +1,57 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { ColorState, SketchPicker } from "react-color";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { ColorState, SketchPicker } from 'react-color';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
-import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../../fields/Doc";
-import { InkTool } from "../../../../fields/InkField";
-import { List } from "../../../../fields/List";
-import { PrefetchProxy } from "../../../../fields/Proxy";
-import { listSpec } from "../../../../fields/Schema";
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from "../../../../fields/Types";
+import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc';
+import { InkTool } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
+import { PrefetchProxy } from '../../../../fields/Proxy';
+import { listSpec } from '../../../../fields/Schema';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-import { Docs } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { CollectionDockingView } from "../../collections/CollectionDockingView";
-import { MarqueeViewBounds } from "../../collections/collectionFreeForm";
-import { CollectionView, CollectionViewType } from "../../collections/CollectionView";
-import { TabDocView } from "../../collections/TabDocView";
-import { ViewBoxBaseComponent } from "../../DocComponent";
-import { Colors } from "../../global/globalEnums";
-import { LightboxView } from "../../LightboxView";
-import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentView";
+import { Docs } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { CollectionDockingView } from '../../collections/CollectionDockingView';
+import { MarqueeViewBounds } from '../../collections/collectionFreeForm';
+import { CollectionView, CollectionViewType } from '../../collections/CollectionView';
+import { TabDocView } from '../../collections/TabDocView';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { Colors } from '../../global/globalEnums';
+import { LightboxView } from '../../LightboxView';
+import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
-import "./PresBox.scss";
-import { PresEffect, PresMovement, PresStatus } from "./PresEnums";
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
-import { PresElementBox } from ".";
+import './PresBox.scss';
+import { PresEffect, PresMovement, PresStatus } from './PresEnums';
+import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { PresElementBox } from '.';
 
 export interface PinProps {
     audioRange?: boolean;
     setPosition?: boolean;
     hidePresBox?: boolean;
     pinWithView?: PinViewProps;
-    pinDocView?: boolean;  // whether the current view specs of the document should be saved the pinned document
-    panelWidth?: number;  // panel width and height of the document (used to compute the bounds of the pinned view area)
-    panelHeight?: number
+    pinDocView?: boolean; // whether the current view specs of the document should be saved the pinned document
+    panelWidth?: number; // panel width and height of the document (used to compute the bounds of the pinned view area)
+    panelHeight?: number;
 }
 
 export interface PinViewProps {
-  bounds: MarqueeViewBounds;
-  scale: number;
+    bounds: MarqueeViewBounds;
+    scale: number;
 }
 
 @observer
 export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(PresBox, fieldKey);
+    }
 
     /**
      * transitions & effects for documents
@@ -67,22 +69,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // when: this.layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc,
         };
         switch (presDoc.presEffect) {
-            case PresEffect.Zoom: return (<Zoom {...effectProps}>{renderDoc}</Zoom>);
-            case PresEffect.Fade: return (<Fade {...effectProps}>{renderDoc}</Fade>);
-            case PresEffect.Flip: return (<Flip {...effectProps}>{renderDoc}</Flip>);
-            case PresEffect.Rotate: return (<Rotate {...effectProps}>{renderDoc}</Rotate>);
-            case PresEffect.Bounce: return (<Bounce {...effectProps}>{renderDoc}</Bounce>);
-            case PresEffect.Roll: return (<Roll {...effectProps}>{renderDoc}</Roll>);
-            case PresEffect.Lightspeed: return (<LightSpeed {...effectProps}>{renderDoc}</LightSpeed>);
+            case PresEffect.Zoom:
+                return <Zoom {...effectProps}>{renderDoc}</Zoom>;
+            case PresEffect.Fade:
+                return <Fade {...effectProps}>{renderDoc}</Fade>;
+            case PresEffect.Flip:
+                return <Flip {...effectProps}>{renderDoc}</Flip>;
+            case PresEffect.Rotate:
+                return <Rotate {...effectProps}>{renderDoc}</Rotate>;
+            case PresEffect.Bounce:
+                return <Bounce {...effectProps}>{renderDoc}</Bounce>;
+            case PresEffect.Roll:
+                return <Roll {...effectProps}>{renderDoc}</Roll>;
+            case PresEffect.Lightspeed:
+                return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
             case PresEffect.None:
-            default: return renderDoc;
+            default:
+                return renderDoc;
         }
     }
     public static EffectsProvider(layoutDoc: Doc, renderDoc: any) {
-        return PresBox.Instance && layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc ?
-            PresBox.renderEffectsDoc(renderDoc, layoutDoc, PresBox.Instance.childDocs[PresBox.Instance.itemIndex])
-            :
-            renderDoc;
+        return PresBox.Instance && layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc ? PresBox.renderEffectsDoc(renderDoc, layoutDoc, PresBox.Instance.childDocs[PresBox.Instance.itemIndex]) : renderDoc;
     }
 
     @observable public static Instance: PresBox;
@@ -107,10 +114,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable private openMovementDropdown: boolean = false;
     @observable private openEffectDropdown: boolean = false;
     @observable private presentTools: boolean = false;
-    @computed get isTreeOrStack() {return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any) }
-    @computed get isTree() { return this.layoutDoc._viewType === CollectionViewType.Tree;}
-    @computed get presFieldKey() { return StrCast(this.layoutDoc.presFieldKey, "data"); }
-    @computed get childDocs() { return DocListCast(this.rootDoc[this.presFieldKey]); }
+    @computed get isTreeOrStack() {
+        return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any);
+    }
+    @computed get isTree() {
+        return this.layoutDoc._viewType === CollectionViewType.Tree;
+    }
+    @computed get presFieldKey() {
+        return StrCast(this.layoutDoc.presFieldKey, 'data');
+    }
+    @computed get childDocs() {
+        return DocListCast(this.rootDoc[this.presFieldKey]);
+    }
     @observable _treeViewMap: Map<Doc, number> = new Map();
 
     @computed get tagDocs() {
@@ -121,9 +136,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         return tagDocs;
     }
-    @computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); }
-    @computed get activeItem() { return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null); }
-    @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
+    @computed get itemIndex() {
+        return NumCast(this.rootDoc._itemIndex);
+    }
+    @computed get activeItem() {
+        return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null);
+    }
+    @computed get targetDoc() {
+        return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
+    }
     @computed get scrollable(): boolean {
         if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
         else return false;
@@ -134,7 +155,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     constructor(props: any) {
         super(props);
-        if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
+        if ((CurrentUserUtils.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
         this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
     }
     @computed get selectedDocumentView() {
@@ -142,21 +163,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
     }
     @computed get isPres(): boolean {
-        document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+        document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
         if (this.selectedDoc?.type === DocumentType.PRES) {
-            document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
-            document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
+            document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
+            document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
             return true;
         }
         return false;
     }
-    @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+    @computed get selectedDoc() {
+        return this.selectedDocumentView?.rootDoc;
+    }
 
     _unmounting = false;
     @action
     componentWillUnmount() {
         this._unmounting = true;
-        document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+        document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = false;
         this.resetPresentation();
         // Turn of progressivize editors
@@ -167,37 +190,38 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     componentDidMount() {
         this._unmounting = false;
-        this.rootDoc._forceRenderEngine = "timeline";
+        this.rootDoc._forceRenderEngine = 'timeline';
         this.layoutDoc.presStatus = PresStatus.Edit;
         this.layoutDoc._gridGap = 0;
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
-        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres =>
-            !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", this.rootDoc));
-        this._disposers.selection = reaction(() => SelectionManager.Views(),
-            views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation());
+        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, 'data', this.rootDoc));
+        this._disposers.selection = reaction(
+            () => SelectionManager.Views(),
+            views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation()
+        );
     }
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
         if (pres) CurrentUserUtils.ActivePresentation = pres;
         else CurrentUserUtils.ActivePresentation = this.rootDoc;
-        document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
-        document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
+        document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
+        document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = true;
         PresBox.Instance = this;
-    }
+    };
 
     // There are still other internal frames and should go through all frames before going to next slide
     nextInternalFrame = (targetDoc: Doc, activeItem: Doc) => {
-        const currentFrame = Cast(targetDoc?._currentFrame, "number", null);
+        const currentFrame = Cast(targetDoc?._currentFrame, 'number', null);
         const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
-        targetDoc._viewTransition = "all 1s";
-        setTimeout(() => targetDoc._viewTransition = undefined, 1010);
+        targetDoc._viewTransition = 'all 1s';
+        setTimeout(() => (targetDoc._viewTransition = undefined), 1010);
         this.nextKeyframe(targetDoc, activeItem);
         if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
         else targetDoc.keyFrameEditing = true;
-    }
+    };
 
     _mediaTimer!: [NodeJS.Timeout, Doc];
     // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
@@ -207,7 +231,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const targMedia = DocumentManager.Instance.getDocumentView(targetDoc);
             targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.presStartTime), NumCast(activeItem.presStartTime) + duration);
         }
-    }
+    };
 
     stopTempMedia = (targetDocField: FieldResult) => {
         const targetDoc = Cast(targetDocField, Doc, null);
@@ -215,11 +239,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const targMedia = DocumentManager.Instance.getDocumentView(targetDoc);
             targMedia?.ComponentView?.Pause?.();
         }
-    }
+    };
 
-    //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time 
+    //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time
     // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions
-    // No more frames in current doc and next slide is defined, therefore move to next slide 
+    // No more frames in current doc and next slide is defined, therefore move to next slide
     nextSlide = (activeNext: Doc) => {
         const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
         console.info('nextSlide', activeNext.title, targetNext?.title);
@@ -229,11 +253,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (!this.childDocs[nextSelected].groupWithUp) {
                 break;
             } else {
-                console.log("Title: " + this.childDocs[nextSelected].title);
+                console.log('Title: ' + this.childDocs[nextSelected].title);
                 this.gotoDocument(nextSelected, this.activeItem, true);
             }
         }
-    }
+    };
 
     // Called when the user activates 'next' - to move to the next part of the pres. trail
     @action
@@ -241,7 +265,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null);
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const lastFrame = Cast(targetDoc?.lastFrame, "number", null);
+        const lastFrame = Cast(targetDoc?.lastFrame, 'number', null);
         const curFrame = NumCast(targetDoc?._currentFrame);
         let internalFrames: boolean = false;
         if (activeItem.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
@@ -249,13 +273,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // Case 1: There are still other frames and should go through all frames before going to next slide
             this.nextInternalFrame(targetDoc, activeItem);
         } else if (this.childDocs[this.itemIndex + 1] !== undefined) {
-            // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide 
+            // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide
             this.nextSlide(activeNext);
         } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
             // Case 3: Last slide and presLoop is toggled ON or it is in Edit mode
             this.gotoDocument(0, this.activeItem);
         }
-    }
+    };
 
     // Called when the user activates 'back' - to move to the previous part of the pres. trail
     @action
@@ -264,7 +288,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null);
         const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null);
-        const lastFrame = Cast(targetDoc.lastFrame, "number", null);
+        const lastFrame = Cast(targetDoc.lastFrame, 'number', null);
         const curFrame = NumCast(targetDoc._currentFrame);
         let prevSelected = this.itemIndex;
         // Functionality for group with up
@@ -284,7 +308,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // Case 3: Pres loop is on so it should go to the last slide
             this.gotoDocument(this.childDocs.length - 1, activeItem);
         }
-    }
+    };
 
     //The function that is called when a document is clicked or reached through next or back.
     //it'll also execute the necessary actions if presentation is playing.
@@ -297,20 +321,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
                 DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
             }
-            if (from?.mediaStop === "auto" && this.layoutDoc.presStatus !== PresStatus.Edit) {
+            if (from?.mediaStop === 'auto' && this.layoutDoc.presStatus !== PresStatus.Edit) {
                 this.stopTempMedia(from.presentationTargetDoc);
             }
             // If next slide is audio / video 'Play automatically' then the next slide should be played
-            if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && (activeItem.mediaStart === "auto")) {
+            if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && activeItem.mediaStart === 'auto') {
                 this.startTempMedia(targetDoc, activeItem);
             }
             if (targetDoc) {
-                Doc.linkFollowHighlight((targetDoc.annotationOn instanceof Doc) ? [targetDoc, targetDoc.annotationOn] : targetDoc);
-                targetDoc && runInAction(() => {
-                    if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
-                    else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
-                });
-                setTimeout(() => targetDoc.focusSpeed = 500, this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+                Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc);
+                targetDoc &&
+                    runInAction(() => {
+                        if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
+                        else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
+                    });
+                setTimeout(() => (targetDoc.focusSpeed = 500), this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
             }
             if (targetDoc?.lastFrame !== undefined) {
                 targetDoc._currentFrame = 0;
@@ -327,14 +352,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         clearTimeout(this._navTimer);
         const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
         if (bestTarget) console.log(bestTarget.title, bestTarget.type);
-        else console.log("no best target");
-        if (bestTarget)  this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
-    }
+        else console.log('no best target');
+        if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
+    };
 
     // navigates to the bestTarget document by making sure it is on screen,
-    // then it applies the view specs stored in activeItem to 
+    // then it applies the view specs stored in activeItem to
     @action
-    static navigateToDoc(bestTarget:Doc, activeItem:Doc, jumpToDoc:boolean)  {
+    static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
         if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
             bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
             bestTarget._scrollTop = activeItem.presPinViewScroll;
@@ -343,32 +368,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
             bestTarget._currentTimecode = activeItem.presStartTime;
         } else {
-            const contentBounds= Cast(activeItem.contentBounds, listSpec("number"));
+            const contentBounds = Cast(activeItem.contentBounds, listSpec('number'));
             bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
             if (contentBounds) {
-                bestTarget._panX = (contentBounds[0] + contentBounds[2])/2;
-                bestTarget._panY = (contentBounds[1] + contentBounds[3])/2;
+                bestTarget._panX = (contentBounds[0] + contentBounds[2]) / 2;
+                bestTarget._panY = (contentBounds[1] + contentBounds[3]) / 2;
                 const dv = DocumentManager.Instance.getDocumentView(bestTarget);
                 if (dv) {
-                    bestTarget._viewScale =  Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]),
-                            dv.props.PanelWidth() / (contentBounds[2]- contentBounds[0]));
-                };
+                    bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0]));
+                }
             } else {
                 bestTarget._panX = activeItem.presPinViewX;
                 bestTarget._panY = activeItem.presPinViewY;
                 bestTarget._viewScale = activeItem.presPinViewScale;
             }
         }
-        return setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
+        return setTimeout(() => (bestTarget._viewTransition = undefined), activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
     }
 
     /**
      * This method makes sure that cursor navigates to the element that
-     * has the option open and last in the group. 
-     * Design choice: If the next document is not in presCollection or 
+     * has the option open and last in the group.
+     * Design choice: If the next document is not in presCollection or
      * presCollection itself then if there is a presCollection it will add
      * a new tab. If presCollection is undefined it will open the document
-     * on the right. 
+     * on the right.
      */
     navigateToElement = async (curDoc: Doc) => {
         const activeItem: Doc = this.activeItem;
@@ -401,7 +425,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             self._eleArray.splice(0, self._eleArray.length, ...eleViewCache);
         });
         const openInTab = (doc: Doc, finished?: () => void) => {
-            collectionDocView ? collectionDocView.props.addDocTab(doc, "") : this.props.addDocTab(doc, "");
+            collectionDocView ? collectionDocView.props.addDocTab(doc, '') : this.props.addDocTab(doc, '');
             this.layoutDoc.presCollection = targetDoc;
             // this still needs some fixing
             setTimeout(resetSelection, 500);
@@ -417,21 +441,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // openInTab(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
+            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
         } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            //awaiting jump so that new scale can be found, since jumping is async     
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
+            //awaiting jump so that new scale can be found, since jumping is async
+            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right
         }
         // After navigating to the document, if it is added as a presPinView then it will
         // adjust the pan and scale to that of the pinView when it was added.
         if (activeItem.presPinView) {
             console.log(targetDoc.title);
-            console.log("presPinView in PresBox.tsx:420");
+            console.log('presPinView in PresBox.tsx:420');
             // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
             this.navigateToView(targetDoc, activeItem);
         }
-    }
+    };
 
     /**
      * Uses the viewfinder to progressivize through the different views of a single collection.
@@ -441,10 +465,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         const srcContext = Cast(targetDoc?.context, Doc, null);
         const docView = DocumentManager.Instance.getDocumentView(targetDoc);
-        const vfLeft = this.checkList(targetDoc, activeItem["viewfinder-left-indexed"]);
-        const vfWidth = this.checkList(targetDoc, activeItem["viewfinder-width-indexed"]);
-        const vfTop = this.checkList(targetDoc, activeItem["viewfinder-top-indexed"]);
-        const vfHeight = this.checkList(targetDoc, activeItem["viewfinder-height-indexed"]);
+        const vfLeft = this.checkList(targetDoc, activeItem['viewfinder-left-indexed']);
+        const vfWidth = this.checkList(targetDoc, activeItem['viewfinder-width-indexed']);
+        const vfTop = this.checkList(targetDoc, activeItem['viewfinder-top-indexed']);
+        const vfHeight = this.checkList(targetDoc, activeItem['viewfinder-height-indexed']);
         // Case 1: document that is not a Golden Layout tab
         if (srcContext) {
             const srcDocView = DocumentManager.Instance.getDocumentView(srcContext);
@@ -455,8 +479,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 const newPanX = NumCast(targetDoc.x) + NumCast(layoutdoc._width) / 2;
                 const newPanY = NumCast(targetDoc.y) + NumCast(layoutdoc._height) / 2;
                 const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
-                srcContext._panX = newPanX + (vfLeft + (vfWidth / 2));
-                srcContext._panY = newPanY + (vfTop + (vfHeight / 2));
+                srcContext._panX = newPanX + (vfLeft + vfWidth / 2);
+                srcContext._panY = newPanY + (vfTop + vfHeight / 2);
                 srcContext._viewScale = newScale;
             }
         }
@@ -465,8 +489,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const panelWidth: number = docView.props.PanelWidth();
             const panelHeight: number = docView.props.PanelHeight();
             const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
-            targetDoc._panX = vfLeft + (vfWidth / 2);
-            targetDoc._panY = vfTop + (vfWidth / 2);
+            targetDoc._panX = vfLeft + vfWidth / 2;
+            targetDoc._panY = vfTop + vfWidth / 2;
             targetDoc._viewScale = newScale;
         }
         const resize = document.getElementById('resizable');
@@ -476,7 +500,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             resize.style.top = vfTop + 'px';
             resize.style.left = vfLeft + 'px';
         }
-    }
+    };
 
     /**
      * For 'Hide Before' and 'Hide After' buttons making sure that
@@ -490,18 +514,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (tagDoc) tagDoc.opacity = 1;
             const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
             const curInd: number = itemIndexes.indexOf(index);
-            if (tagDoc === this.layoutDoc.presCollection) { tagDoc.opacity = 1; }
-            else {
-                if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) { }
-                else if (curDoc.presHideBefore) {
+            if (tagDoc === this.layoutDoc.presCollection) {
+                tagDoc.opacity = 1;
+            } else {
+                if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) {
+                } else if (curDoc.presHideBefore) {
                     if (index > this.itemIndex) {
                         tagDoc.opacity = 0;
                     } else if (!curDoc.presHideAfter) {
                         tagDoc.opacity = 1;
                     }
                 }
-                if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== (itemIndexes.length - 1)) { }
-                else if (curDoc.presHideAfter) {
+                if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== itemIndexes.length - 1) {
+                } else if (curDoc.presHideAfter) {
                     if (index < this.itemIndex) {
                         tagDoc.opacity = 0;
                     } else if (!curDoc.presHideBefore) {
@@ -510,9 +535,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }
         });
-    }
-
-
+    };
 
     //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc
     @action
@@ -521,13 +544,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         let activeItem: Doc = this.activeItem;
         let targetDoc: Doc = this.targetDoc;
         let duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition);
-        const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
-        const load = async () => { // Wrap the loop into an async function for this to work
+        const timer = (ms: number) => new Promise(res => (this._presTimer = setTimeout(res, ms)));
+        const load = async () => {
+            // Wrap the loop into an async function for this to work
             for (var i = startSlide; i < this.childDocs.length; i++) {
                 activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
                 targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
                 duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition);
-                if (duration < 100) { duration = 2500; }
+                if (duration < 100) {
+                    duration = 2500;
+                }
                 if (NumCast(targetDoc.lastFrame) > 0) {
                     for (var f = 0; f < NumCast(targetDoc.lastFrame); f++) {
                         await timer(duration / NumCast(targetDoc.lastFrame));
@@ -535,7 +561,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     }
                 }
 
-                await timer(duration); this.next(); // then the created Promise can be awaited
+                await timer(duration);
+                this.next(); // then the created Promise can be awaited
                 if (i === this.childDocs.length - 1) {
                     setTimeout(() => {
                         clearTimeout(this._presTimer);
@@ -549,7 +576,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.startPresentation(startSlide);
         this.gotoDocument(startSlide, activeItem);
         load();
-    }
+    };
 
     // The function pauses the auto presentation
     @action
@@ -560,20 +587,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.layoutDoc.presLoop = false;
             this.childDocs.forEach(this.stopTempMedia);
         }
-    }
+    };
 
     //The function that resets the presentation by removing every action done by it. It also
     //stops the presentaton.
     resetPresentation = () => {
         this.rootDoc._itemIndex = 0;
-        this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null)).filter(doc => doc instanceof Doc).forEach(doc => {
-            try {
-                doc.opacity = 1;
-            } catch (e) {
-                console.log("Reset presentation error: ", e);
-            }
-        });
-    }
+        this.childDocs
+            .map(doc => Cast(doc.presentationTargetDoc, Doc, null))
+            .filter(doc => doc instanceof Doc)
+            .forEach(doc => {
+                try {
+                    doc.opacity = 1;
+                } catch (e) {
+                    console.log('Reset presentation error: ', e);
+                }
+            });
+    };
 
     // The function allows for viewing the pres path on toggle
     @action togglePath = (srcContext: Doc, off?: boolean) => {
@@ -581,19 +611,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this._pathBoolean = false;
             srcContext.presPathView = false;
         } else {
-            runInAction(() => this._pathBoolean = !this._pathBoolean);
+            runInAction(() => (this._pathBoolean = !this._pathBoolean));
             srcContext.presPathView = this._pathBoolean;
         }
-    }
+    };
 
     // The function allows for expanding the view of pres on toggle
     @action toggleExpandMode = () => {
-        runInAction(() => this._expandBoolean = !this._expandBoolean);
+        runInAction(() => (this._expandBoolean = !this._expandBoolean));
         this.rootDoc.expandBoolean = this._expandBoolean;
-        this.childDocs.forEach((doc) => {
+        this.childDocs.forEach(doc => {
             doc.presExpandInlineButton = this._expandBoolean;
         });
-    }
+    };
 
     /**
      * The function that starts the presentation at the given index, also checking if actions should be applied
@@ -611,7 +641,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 tagDoc.opacity = 0;
             }
         });
-    }
+    };
 
     /**
      * The method called to open the presentation as a minimized view
@@ -621,7 +651,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
             Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
-            CollectionDockingView.AddSplit(this.rootDoc, "right");
+            CollectionDockingView.AddSplit(this.rootDoc, 'right');
         } else {
             this.layoutDoc.presStatus = PresStatus.Edit;
             clearTimeout(this._presTimer);
@@ -633,7 +663,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
             this.props.removeDocument?.(this.layoutDoc);
         }
-    }
+    };
 
     /**
      * Called when the user changes the view type
@@ -643,7 +673,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     viewChanged = action((e: React.ChangeEvent) => {
         //@ts-ignore
         const viewType = e.target.selectedOptions[0].value as CollectionViewType;
-        this.layoutDoc.presFieldKey = this.fieldKey+(viewType === CollectionViewType.Tree ?"-linearized":"");
+        this.layoutDoc.presFieldKey = this.fieldKey + (viewType === CollectionViewType.Tree ? '-linearized' : '');
         // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
         [CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType) && (this.rootDoc._pivotField = undefined);
         this.rootDoc._viewType = viewType;
@@ -677,20 +707,30 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     });
 
-
     setMovementName = action((movement: any, activeItem: Doc): string => {
         let output: string = 'none';
         switch (movement) {
-            case PresMovement.Zoom: output = 'Pan & Zoom'; break; //Pan and zoom
-            case PresMovement.Pan: output = 'Pan'; break; //Pan
-            case PresMovement.Jump: output = 'Jump cut'; break; //Jump Cut
-            case PresMovement.None: output = 'None'; break; //None
-            default: output = 'Zoom'; activeItem.presMovement = 'zoom'; break; //default set as zoom
+            case PresMovement.Zoom:
+                output = 'Pan & Zoom';
+                break; //Pan and zoom
+            case PresMovement.Pan:
+                output = 'Pan';
+                break; //Pan
+            case PresMovement.Jump:
+                output = 'Jump cut';
+                break; //Jump Cut
+            case PresMovement.None:
+                output = 'None';
+                break; //None
+            default:
+                output = 'Zoom';
+                activeItem.presMovement = 'zoom';
+                break; //default set as zoom
         }
         return output;
     });
 
-    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isChildActive = isActive));
+    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
     // For dragging documents into the presentation trail
     addDocumentFilter = (docs: Doc[]) => {
         docs.forEach((doc, i) => {
@@ -698,8 +738,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (doc.type === DocumentType.LABEL) {
                 const audio = Cast(doc.annotationOn, Doc, null);
                 if (audio) {
-                    audio.mediaStart = "manual";
-                    audio.mediaStop = "manual";
+                    audio.mediaStart = 'manual';
+                    audio.mediaStop = 'manual';
                     audio.presStartTime = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
                     audio.presEndTime = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
                     audio.presDuration = audio.presStartTime - audio.presEndTime;
@@ -714,7 +754,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     setTimeout(() => this.removeDocument(doc), 0);
                     return false;
                 } else {
-                    if (!doc.presentationTargetDoc) doc.title = doc.title + " - Slide";
+                    if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
                     doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
                     doc.presMovement = PresMovement.Zoom;
                     if (this._expandBoolean) doc.presExpandInlineButton = true;
@@ -722,13 +762,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         });
         return true;
-    }
-    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement);
+    };
+    childLayoutTemplate = () => (!this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement));
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
-    getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
+    getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
-        (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
+    isContentActive = (outsideReaction?: boolean) =>
+        CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
 
     /**
      * For sorting the array so that the order is maintained when it is dropped.
@@ -736,7 +776,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     sortArray = (): Doc[] => {
         return this.childDocs.filter(doc => this._selectedArray.has(doc));
-    }
+    };
 
     /**
      * Method to get the list of selected items in the order in which they have been selected
@@ -745,9 +785,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const list = Array.from(this._selectedArray.keys()).map((doc: Doc, index: any) => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
-            if (curDoc && curDoc === this.activeItem) return <div key={index} className="selectedList-items"><b>{index + 1}.  {curDoc.title}</b></div>;
-            else if (tagDoc) return <div key={index} className="selectedList-items">{index + 1}.  {curDoc.title}</div>;
-            else if (curDoc) return <div key={index} className="selectedList-items">{index + 1}.  {curDoc.title}</div>;
+            if (curDoc && curDoc === this.activeItem)
+                return (
+                    <div key={index} className="selectedList-items">
+                        <b>
+                            {index + 1}. {curDoc.title}
+                        </b>
+                    </div>
+                );
+            else if (tagDoc)
+                return (
+                    <div key={index} className="selectedList-items">
+                        {index + 1}. {curDoc.title}
+                    </div>
+                );
+            else if (curDoc)
+                return (
+                    <div key={index} className="selectedList-items">
+                        {index + 1}. {curDoc.title}
+                    </div>
+                );
         });
         return list;
     }
@@ -756,7 +813,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     selectPres = () => {
         const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
         presDocView && SelectionManager.SelectView(presDocView, false);
-    }
+    };
 
     //Regular click
     @action
@@ -766,12 +823,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0);
         else this.updateCurrentPresentation(context);
 
-        if (this.activeItem.setPosition &&
-            this.activeItem.y !== undefined &&
-            this.activeItem.x !== undefined &&
-            this.targetDoc.x !== undefined &&
-            this.targetDoc.y !== undefined) {
-            const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
+        if (this.activeItem.setPosition && this.activeItem.y !== undefined && this.activeItem.x !== undefined && this.targetDoc.x !== undefined && this.targetDoc.y !== undefined) {
+            const timer = (ms: number) => new Promise(res => (this._presTimer = setTimeout(res, ms)));
             const time = 10;
             const ydiff = NumCast(this.activeItem.y) - NumCast(this.targetDoc.y);
             const xdiff = NumCast(this.activeItem.x) - NumCast(this.targetDoc.x);
@@ -782,7 +835,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 await timer(0.1);
             }
         }
-    }
+    };
 
     //Command click
     @action
@@ -797,13 +850,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.removeFromArray(this._dragArray, doc);
         }
         this.selectPres();
-    }
+    };
 
     removeFromArray = (arr: any[], val: any) => {
         const index: number = arr.indexOf(val);
         const ret: any[] = arr.splice(index, 1);
         arr = ret;
-    }
+    };
 
     //Shift click
     @action
@@ -818,7 +871,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         this.selectPres();
-    }
+    };
 
     //regular click
     @action
@@ -829,17 +882,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this._dragArray.splice(0, this._dragArray.length, drag);
         focus && this.selectElement(doc);
         selectPres && this.selectPres();
-    }
+    };
 
     modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
         if (cmdClick) this.multiSelect(doc, ref, drag);
         else if (shiftClick) this.shiftSelect(doc, ref, drag);
         else this.regularSelect(doc, ref, drag, focus);
-    }
+    };
 
     static keyEventsWrapper = (e: KeyboardEvent) => {
         PresBox.Instance.keyEvents(e);
-    }
+    };
 
     // Key for when the presentaiton is active
     @action
@@ -847,57 +900,75 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (e.target instanceof HTMLInputElement) return;
         let handled = false;
         const anchorNode = document.activeElement as HTMLDivElement;
-        if (anchorNode && anchorNode.className?.includes("lm_title")) return;
+        if (anchorNode && anchorNode.className?.includes('lm_title')) return;
         switch (e.key) {
-            case "Backspace":
-                if (this.layoutDoc.presStatus === "edit") {
-                    undoBatch(action(() => {
-                        for (const doc of Array.from(this._selectedArray.keys())) {
-                            this.removeDocument(doc);
-                        }
-                        this._selectedArray.clear();
-                        this._eleArray.length = 0;
-                        this._dragArray.length = 0;
-                    }))();
+            case 'Backspace':
+                if (this.layoutDoc.presStatus === 'edit') {
+                    undoBatch(
+                        action(() => {
+                            for (const doc of Array.from(this._selectedArray.keys())) {
+                                this.removeDocument(doc);
+                            }
+                            this._selectedArray.clear();
+                            this._eleArray.length = 0;
+                            this._dragArray.length = 0;
+                        })
+                    )();
                     handled = true;
                 }
                 break;
-            case "Escape":
-                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) { this.updateMinimize(); }
-                else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; }
-                else this.layoutDoc.presStatus = "edit";
+            case 'Escape':
+                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+                    this.updateMinimize();
+                } else if (this.layoutDoc.presStatus === 'edit') {
+                    this._selectedArray.clear();
+                    this._eleArray.length = this._dragArray.length = 0;
+                } else this.layoutDoc.presStatus = 'edit';
                 if (this._presTimer) clearTimeout(this._presTimer);
                 handled = true;
                 break;
-            case "Down": case "ArrowDown":
-            case "Right": case "ArrowRight":
-                if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) { // TODO: update to work properly
+            case 'Down':
+            case 'ArrowDown':
+            case 'Right':
+            case 'ArrowRight':
+                if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) {
+                    // TODO: update to work properly
                     this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1;
                     this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
                 } else {
                     this.next();
-                    if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; }
+                    if (this._presTimer) {
+                        clearTimeout(this._presTimer);
+                        this.layoutDoc.presStatus = PresStatus.Manual;
+                    }
                 }
                 handled = true;
                 break;
-            case "Up": case "ArrowUp":
-            case "Left": case "ArrowLeft":
-                if (e.shiftKey && this.itemIndex !== 0) { // TODO: update to work properly
+            case 'Up':
+            case 'ArrowUp':
+            case 'Left':
+            case 'ArrowLeft':
+                if (e.shiftKey && this.itemIndex !== 0) {
+                    // TODO: update to work properly
                     this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1;
                     this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
                 } else {
                     this.back();
-                    if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; }
+                    if (this._presTimer) {
+                        clearTimeout(this._presTimer);
+                        this.layoutDoc.presStatus = PresStatus.Manual;
+                    }
                 }
                 handled = true;
                 break;
-            case "Spacebar": case " ":
+            case 'Spacebar':
+            case ' ':
                 if (this.layoutDoc.presStatus === PresStatus.Manual) this.startAutoPres(this.itemIndex);
                 else if (this.layoutDoc.presStatus === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer);
                 handled = true;
                 break;
-            case "a":
-                if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === "edit") {
+            case 'a':
+                if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === 'edit') {
                     this._selectedArray.clear();
                     this.childDocs.forEach(doc => this._selectedArray.set(doc, undefined));
                     handled = true;
@@ -909,10 +980,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             e.stopPropagation();
             e.preventDefault();
         }
-    }
+    };
 
     /**
-     * 
+     *
      */
     @action
     viewPaths = () => {
@@ -920,7 +991,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (srcContext) {
             this.togglePath(srcContext);
         }
-    }
+    };
 
     getAllIndexes = (arr: Doc[], val: Doc): number[] => {
         const indexes = [];
@@ -928,7 +999,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             arr[i] === val && indexes.push(i);
         }
         return indexes;
-    }
+    };
 
     // Adds the index in the pres path graphically
     @computed get order() {
@@ -936,61 +1007,64 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const docs: Doc[] = [];
         const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
         const dv = DocumentManager.Instance.getDocumentView(presCollection);
-        this.childDocs.filter(doc => Cast(doc.presentationTargetDoc, Doc, null)).forEach((doc, index) => {
-            const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
-            const srcContext = Cast(tagDoc.context, Doc, null);
-            const width = NumCast(tagDoc._width) / 10;
-            const height = Math.max(NumCast(tagDoc._height) / 10, 15);
-            const edge = Math.max(width, height);
-            const fontSize = edge * 0.8;
-            const gap = 2;
-            if (presCollection === srcContext) {
-                // Case A: Document is contained within the collection
-                if (docs.includes(tagDoc)) {
-                    const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length;
-                    docs.push(tagDoc);
-                    order.push(
-                        <div className="pathOrder"
-                            key={tagDoc.id + 'pres' + index}
-                            style={{ top: NumCast(tagDoc.y) + (prevOccurances * (edge + gap) - (edge / 2)), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}
-                            onClick={() => this.selectElement(doc)}>
-                            <div className="pathOrder-frame">{index + 1}</div>
-                        </div>);
-                } else {
+        this.childDocs
+            .filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
+            .forEach((doc, index) => {
+                const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+                const srcContext = Cast(tagDoc.context, Doc, null);
+                const width = NumCast(tagDoc._width) / 10;
+                const height = Math.max(NumCast(tagDoc._height) / 10, 15);
+                const edge = Math.max(width, height);
+                const fontSize = edge * 0.8;
+                const gap = 2;
+                if (presCollection === srcContext) {
+                    // Case A: Document is contained within the collection
+                    if (docs.includes(tagDoc)) {
+                        const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length;
+                        docs.push(tagDoc);
+                        order.push(
+                            <div
+                                className="pathOrder"
+                                key={tagDoc.id + 'pres' + index}
+                                style={{ top: NumCast(tagDoc.y) + (prevOccurances * (edge + gap) - edge / 2), left: NumCast(tagDoc.x) - edge / 2, width: edge, height: edge, fontSize: fontSize }}
+                                onClick={() => this.selectElement(doc)}>
+                                <div className="pathOrder-frame">{index + 1}</div>
+                            </div>
+                        );
+                    } else {
+                        docs.push(tagDoc);
+                        order.push(
+                            <div
+                                className="pathOrder"
+                                key={tagDoc.id + 'pres' + index}
+                                style={{ top: NumCast(tagDoc.y) - edge / 2, left: NumCast(tagDoc.x) - edge / 2, width: edge, height: edge, fontSize: fontSize }}
+                                onClick={() => this.selectElement(doc)}>
+                                <div className="pathOrder-frame">{index + 1}</div>
+                            </div>
+                        );
+                    }
+                } else if (doc.presPinView && presCollection === tagDoc && dv) {
+                    // Case B: Document is presPinView and is presCollection
+                    const scale: number = 1 / NumCast(doc.presPinViewScale);
+                    const height: number = dv.props.PanelHeight() * scale;
+                    const width: number = dv.props.PanelWidth() * scale;
+                    const indWidth = width / 10;
+                    const indHeight = Math.max(height / 10, 15);
+                    const indEdge = Math.max(indWidth, indHeight);
+                    const indFontSize = indEdge * 0.8;
+                    const xLoc: number = NumCast(doc.presPinViewX) - width / 2;
+                    const yLoc: number = NumCast(doc.presPinViewY) - height / 2;
                     docs.push(tagDoc);
                     order.push(
-                        <div className="pathOrder"
-                            key={tagDoc.id + 'pres' + index}
-                            style={{ top: NumCast(tagDoc.y) - (edge / 2), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}
-                            onClick={() => this.selectElement(doc)}>
-                            <div className="pathOrder-frame">{index + 1}</div>
-                        </div>);
+                        <>
+                            <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top: yLoc - indEdge / 2, left: xLoc - indEdge / 2, width: indEdge, height: indEdge, fontSize: indFontSize }} onClick={() => this.selectElement(doc)}>
+                                <div className="pathOrder-frame">{index + 1}</div>
+                            </div>
+                            <div className="pathOrder-presPinView" style={{ top: yLoc, left: xLoc, width: width, height: height, borderWidth: indEdge / 10 }}></div>
+                        </>
+                    );
                 }
-            } else if (doc.presPinView && presCollection === tagDoc && dv) {
-                // Case B: Document is presPinView and is presCollection
-                const scale: number = 1 / NumCast(doc.presPinViewScale);
-                const height: number = dv.props.PanelHeight() * scale;
-                const width: number = dv.props.PanelWidth() * scale;
-                const indWidth = width / 10;
-                const indHeight = Math.max(height / 10, 15);
-                const indEdge = Math.max(indWidth, indHeight);
-                const indFontSize = indEdge * 0.8;
-                const xLoc: number = NumCast(doc.presPinViewX) - (width / 2);
-                const yLoc: number = NumCast(doc.presPinViewY) - (height / 2);
-                docs.push(tagDoc);
-                order.push(
-                    <>
-                        <div className="pathOrder"
-                            key={tagDoc.id + 'pres' + index}
-                            style={{ top: yLoc - (indEdge / 2), left: xLoc - (indEdge / 2), width: indEdge, height: indEdge, fontSize: indFontSize }}
-                            onClick={() => this.selectElement(doc)}
-                        >
-                            <div className="pathOrder-frame">{index + 1}</div>
-                        </div>
-                        <div className="pathOrder-presPinView" style={{ top: yLoc, left: xLoc, width: width, height: height, borderWidth: indEdge / 10 }}></div>
-                    </>);
-            }
-        });
+            });
         return order;
     }
 
@@ -1003,37 +1077,39 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * collection)
      */
     @computed get paths() {
-        let pathPoints = "";
+        let pathPoints = '';
         const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
         this.childDocs.forEach((doc, index) => {
             const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
             const srcContext = Cast(tagDoc?.context, Doc, null);
             if (tagDoc && presCollection === srcContext) {
-                const n1x = NumCast(tagDoc.x) + (NumCast(tagDoc._width) / 2);
-                const n1y = NumCast(tagDoc.y) + (NumCast(tagDoc._height) / 2);
-                if (index = 0) pathPoints = n1x + "," + n1y;
-                else pathPoints = pathPoints + " " + n1x + "," + n1y;
+                const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
+                const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
+                if ((index = 0)) pathPoints = n1x + ',' + n1y;
+                else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
             } else if (doc.presPinView && presCollection === tagDoc) {
                 const n1x = NumCast(doc.presPinViewX);
                 const n1y = NumCast(doc.presPinViewY);
-                if (index = 0) pathPoints = n1x + "," + n1y;
-                else pathPoints = pathPoints + " " + n1x + "," + n1y;
+                if ((index = 0)) pathPoints = n1x + ',' + n1y;
+                else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
             }
         });
-        return (<polyline
-            points={pathPoints}
-            style={{
-                opacity: 1,
-                stroke: "#69a6db",
-                strokeWidth: 5,
-                strokeDasharray: '10 5',
-                boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
-            }}
-            fill="none"
-            markerStart="url(#markerArrow)"
-            markerMid="url(#markerSquare)"
-            markerEnd="url(#markerSquareFilled)"
-        />);
+        return (
+            <polyline
+                points={pathPoints}
+                style={{
+                    opacity: 1,
+                    stroke: '#69a6db',
+                    strokeWidth: 5,
+                    strokeDasharray: '10 5',
+                    boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
+                }}
+                fill="none"
+                markerStart="url(#markerArrow)"
+                markerMid="url(#markerSquare)"
+                markerEnd="url(#markerSquareFilled)"
+            />
+        );
     }
 
     // Converts seconds to ms and updates presTransition
@@ -1042,8 +1118,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 10000) timeInMS = 10000;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presTransition = timeInMS);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presTransition = timeInMS));
+    };
 
     // Converts seconds to ms and updates presTransition
     setZoom = (number: String, change?: number) => {
@@ -1051,8 +1127,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) scale += change;
         if (scale < 0.01) scale = 0.01;
         if (scale > 1.5) scale = 1.5;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presZoom = scale);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presZoom = scale));
+    };
 
     // Converts seconds to ms and updates presDuration
     setDurationTime = (number: String, change?: number) => {
@@ -1060,8 +1136,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 20000) timeInMS = 20000;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presDuration = timeInMS);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presDuration = timeInMS));
+    };
 
     /**
      * When the movement dropdown is changes
@@ -1069,7 +1145,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     updateMovement = action((movement: any, all?: boolean) => {
         const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach((doc) => {
+        array.forEach(doc => {
             switch (movement) {
                 case PresMovement.Zoom: //Pan and zoom
                     doc.presMovement = PresMovement.Zoom;
@@ -1081,7 +1157,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     doc.presJump = true;
                     doc.presMovement = PresMovement.Jump;
                     break;
-                case PresMovement.None: default:
+                case PresMovement.None:
+                default:
                     doc.presMovement = PresMovement.None;
                     break;
             }
@@ -1092,31 +1169,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     updateHideBefore = (activeItem: Doc) => {
         activeItem.presHideBefore = !activeItem.presHideBefore;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presHideBefore = activeItem.presHideBefore);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
+    };
 
     @undoBatch
     @action
     updateHideAfter = (activeItem: Doc) => {
         activeItem.presHideAfter = !activeItem.presHideAfter;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presHideAfter = activeItem.presHideAfter);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
+    };
 
     @undoBatch
     @action
     updateOpenDoc = (activeItem: Doc) => {
         activeItem.openDocument = !activeItem.openDocument;
-        Array.from(this._selectedArray.keys()).forEach((doc) => {
+        Array.from(this._selectedArray.keys()).forEach(doc => {
             doc.openDocument = activeItem.openDocument;
         });
-    }
+    };
 
     @undoBatch
     @action
     updateEffectDirection = (effect: any, all?: boolean) => {
         const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach((doc) => {
-            const tagDoc = doc;// Cast(doc.presentationTargetDoc, Doc, null);
+        array.forEach(doc => {
+            const tagDoc = doc; // Cast(doc.presentationTargetDoc, Doc, null);
             switch (effect) {
                 case PresEffect.Left:
                     tagDoc.presEffectDirection = PresEffect.Left;
@@ -1130,19 +1207,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 case PresEffect.Bottom:
                     tagDoc.presEffectDirection = PresEffect.Bottom;
                     break;
-                case PresEffect.Center: default:
+                case PresEffect.Center:
+                default:
                     tagDoc.presEffectDirection = PresEffect.Center;
                     break;
             }
         });
-    }
+    };
 
     @undoBatch
     @action
     updateEffect = (effect: any, all?: boolean) => {
         const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach((doc) => {
-            const tagDoc = doc;//Cast(doc.presentationTargetDoc, Doc, null);
+        array.forEach(doc => {
+            const tagDoc = doc; //Cast(doc.presentationTargetDoc, Doc, null);
             switch (effect) {
                 case PresEffect.Bounce:
                     tagDoc.presEffect = PresEffect.Bounce;
@@ -1159,19 +1237,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 case PresEffect.Rotate:
                     tagDoc.presEffect = PresEffect.Rotate;
                     break;
-                case PresEffect.None: default:
+                case PresEffect.None:
+                default:
                     tagDoc.presEffect = PresEffect.None;
                     break;
             }
         });
-    }
+    };
 
     _batch: UndoManager.Batch | undefined = undefined;
 
     @computed get transitionDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const isPresCollection: boolean = (targetDoc === this.layoutDoc.presCollection);
+        const isPresCollection: boolean = targetDoc === this.layoutDoc.presCollection;
         const isPinWithView: boolean = BoolCast(activeItem.presPinView);
         if (activeItem && targetDoc) {
             const type = targetDoc.type;
@@ -1182,155 +1261,301 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const effect = this.activeItem.presEffect ? this.activeItem.presEffect : 'None';
             activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
             return (
-                <div className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = false; this.openEffectDropdown = false; })}>
+                <div
+                    className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
+                    onPointerDown={e => e.stopPropagation()}
+                    onPointerUp={e => e.stopPropagation()}
+                    onClick={action(e => {
+                        e.stopPropagation();
+                        this.openMovementDropdown = false;
+                        this.openEffectDropdown = false;
+                    })}>
                     <div className="ribbon-box">
                         Movement
-                        {isPresCollection || (isPresCollection && isPinWithView) ?
+                        {isPresCollection || (isPresCollection && isPinWithView) ? (
                             <div className="ribbon-property" style={{ marginLeft: 0, height: 25, textAlign: 'left', paddingLeft: 5, paddingRight: 5, fontSize: 10 }}>
-                                {this.scrollable ? "Scroll to pinned view" : !isPinWithView ? "No movement" : "Pan & Zoom to pinned view"}
+                                {this.scrollable ? 'Scroll to pinned view' : !isPinWithView ? 'No movement' : 'Pan & Zoom to pinned view'}
                             </div>
-                            :
-                            <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                        ) : (
+                            <div
+                                className="presBox-dropdown"
+                                onClick={action(e => {
+                                    e.stopPropagation();
+                                    this.openMovementDropdown = !this.openMovementDropdown;
+                                })}
+                                style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
                                 {this.setMovementName(activeItem.presMovement, activeItem)}
-                                <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} />
-                                <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>None</div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>Pan {"&"} Zoom</div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>Pan</div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>Jump cut</div>
+                                <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this.openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+                                <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? 'grid' : 'none' }}>
+                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>
+                                        None
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>
+                                        Pan {'&'} Zoom
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>
+                                        Pan
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>
+                                        Jump cut
+                                    </div>
                                 </div>
                             </div>
-                        }
-                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}>
+                        )}
+                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Zoom (% screen filled)</div>
                             <div className="ribbon-property">
-                                <input className="presBox-input"
-                                    type="number" value={zoom}
-                                    onChange={action((e) => this.setZoom(e.target.value))} />%
+                                <input className="presBox-input" type="number" value={zoom} onChange={action(e => this.setZoom(e.target.value))} />%
                             </div>
                             <div className="ribbon-propertyUpDown">
                                 <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
-                                    <FontAwesomeIcon icon={"caret-up"} />
+                                    <FontAwesomeIcon icon={'caret-up'} />
                                 </div>
                                 <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
-                                    <FontAwesomeIcon icon={"caret-down"} />
+                                    <FontAwesomeIcon icon={'caret-down'} />
                                 </div>
                             </div>
                         </div>
-                        <input type="range" step="1" min="0" max="150" value={zoom}
-                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}
+                        <input
+                            type="range"
+                            step="1"
+                            min="0"
+                            max="150"
+                            value={zoom}
+                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}
                             id="toolbar-slider"
-                            onPointerDown={() => this._batch = UndoManager.StartBatch("presZoom")}
+                            onPointerDown={() => (this._batch = UndoManager.StartBatch('presZoom'))}
                             onPointerUp={() => this._batch?.end()}
                             onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                 e.stopPropagation();
                                 this.setZoom(e.target.value);
-                            }} />
-                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}>
+                            }}
+                        />
+                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Movement Speed</div>
                             <div className="ribbon-property">
-                                <input className="presBox-input"
-                                    type="number" value={transitionSpeed}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e) => this.setTransitionTime(e.target.value))} /> s
+                                <input className="presBox-input" type="number" value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.setTransitionTime(e.target.value))} /> s
                             </div>
                             <div className="ribbon-propertyUpDown">
                                 <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), 1000))}>
-                                    <FontAwesomeIcon icon={"caret-up"} />
+                                    <FontAwesomeIcon icon={'caret-up'} />
                                 </div>
                                 <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), -1000))}>
-                                    <FontAwesomeIcon icon={"caret-down"} />
+                                    <FontAwesomeIcon icon={'caret-down'} />
                                 </div>
                             </div>
                         </div>
-                        <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed}
-                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}
+                        <input
+                            type="range"
+                            step="0.1"
+                            min="0.1"
+                            max="10"
+                            value={transitionSpeed}
+                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}
                             id="toolbar-slider"
-                            onPointerDown={() => this._batch = UndoManager.StartBatch("presTransition")}
+                            onPointerDown={() => (this._batch = UndoManager.StartBatch('presTransition'))}
                             onPointerUp={() => this._batch?.end()}
                             onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                 e.stopPropagation();
                                 this.setTransitionTime(e.target.value);
-                            }} />
-                        <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
+                            }}
+                        />
+                        <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}>
                             <div className="slider-text">Fast</div>
                             <div className="slider-text">Medium</div>
                             <div className="slider-text">Slow</div>
                         </div>
                     </div>
                     <div className="ribbon-box">
-                        Visibility {"&"} Duration
+                        Visibility {'&'} Duration
                         <div className="ribbon-doubleButton">
-                            {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideBefore ? "active" : ""}`} onClick={() => this.updateHideBefore(activeItem)}>Hide before</div></Tooltip>}
-                            {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip>}
-                            <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip>
+                            {isPresCollection ? null : (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Hide before presented'}</div>
+                                        </>
+                                    }>
+                                    <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
+                                        Hide before
+                                    </div>
+                                </Tooltip>
+                            )}
+                            {isPresCollection ? null : (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Hide after presented'}</div>
+                                        </>
+                                    }>
+                                    <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
+                                        Hide after
+                                    </div>
+                                </Tooltip>
+                            )}
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{'Open in lightbox view'}</div>
+                                    </>
+                                }>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
+                                    Lightbox
+                                </div>
+                            </Tooltip>
                         </div>
-                        {(type === DocumentType.AUDIO || type === DocumentType.VID) ? (null) : <>
-                            <div className="ribbon-doubleButton" >
-                                <div className="presBox-subheading">Slide Duration</div>
-                                <div className="ribbon-property">
-                                    <input className="presBox-input"
-                                        type="number" value={duration}
-                                        onKeyDown={e => e.stopPropagation()}
-                                        onChange={action((e) => this.setDurationTime(e.target.value))} /> s
+                        {type === DocumentType.AUDIO || type === DocumentType.VID ? null : (
+                            <>
+                                <div className="ribbon-doubleButton">
+                                    <div className="presBox-subheading">Slide Duration</div>
+                                    <div className="ribbon-property">
+                                        <input className="presBox-input" type="number" value={duration} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.setDurationTime(e.target.value))} /> s
+                                    </div>
+                                    <div className="ribbon-propertyUpDown">
+                                        <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}>
+                                            <FontAwesomeIcon icon={'caret-up'} />
+                                        </div>
+                                        <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}>
+                                            <FontAwesomeIcon icon={'caret-down'} />
+                                        </div>
+                                    </div>
+                                </div>
+                                <input
+                                    type="range"
+                                    step="0.1"
+                                    min="0.1"
+                                    max="20"
+                                    value={duration}
+                                    style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'block' }}
+                                    className={'toolbar-slider'}
+                                    id="duration-slider"
+                                    onPointerDown={() => {
+                                        this._batch = UndoManager.StartBatch('presDuration');
+                                    }}
+                                    onPointerUp={() => {
+                                        if (this._batch) this._batch.end();
+                                    }}
+                                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                                        e.stopPropagation();
+                                        this.setDurationTime(e.target.value);
+                                    }}
+                                />
+                                <div className={'slider-headers'} style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'grid' }}>
+                                    <div className="slider-text">Short</div>
+                                    <div className="slider-text">Medium</div>
+                                    <div className="slider-text">Long</div>
                                 </div>
-                                <div className="ribbon-propertyUpDown">
-                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}>
-                                        <FontAwesomeIcon icon={"caret-up"} />
+                            </>
+                        )}
+                    </div>
+                    {isPresCollection ? null : (
+                        <div className="ribbon-box">
+                            Effects
+                            <div
+                                className="presBox-dropdown"
+                                onClick={action(e => {
+                                    e.stopPropagation();
+                                    this.openEffectDropdown = !this.openEffectDropdown;
+                                })}
+                                style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                                {effect.toString()}
+                                <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this.openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+                                <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
+                                    <div
+                                        className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.None || !this.activeItem.presEffect ? 'active' : ''}`}
+                                        onPointerDown={e => e.stopPropagation()}
+                                        onClick={() => this.updateEffect(PresEffect.None)}>
+                                        None
                                     </div>
-                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}>
-                                        <FontAwesomeIcon icon={"caret-down"} />
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Fade ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>
+                                        Fade In
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Flip ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}>
+                                        Flip
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Rotate ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}>
+                                        Rotate
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Bounce ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}>
+                                        Bounce
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Roll ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}>
+                                        Roll
                                     </div>
                                 </div>
                             </div>
-                            <input type="range" step="0.1" min="0.1" max="20" value={duration}
-                                style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }}
-                                className={"toolbar-slider"} id="duration-slider"
-                                onPointerDown={() => { this._batch = UndoManager.StartBatch("presDuration"); }}
-                                onPointerUp={() => { if (this._batch) this._batch.end(); }}
-                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }}
-                            />
-                            <div className={"slider-headers"} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "grid" }}>
-                                <div className="slider-text">Short</div>
-                                <div className="slider-text">Medium</div>
-                                <div className="slider-text">Long</div>
+                            <div className="ribbon-doubleButton" style={{ display: effect === 'None' ? 'none' : 'inline-flex' }}>
+                                <div className="presBox-subheading">Effect direction</div>
+                                <div className="ribbon-property">{this.effectDirection}</div>
                             </div>
-                        </>}
-                    </div>
-                    {isPresCollection ? (null) : <div className="ribbon-box">
-                        Effects
-                        <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
-                            {effect}
-                            <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} />
-                            <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? "grid" : "none" }} onPointerDown={e => e.stopPropagation()}>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.None || !this.activeItem.presEffect ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.None)}>None</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Fade ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>Fade In</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Flip ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}>Flip</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Rotate ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}>Rotate</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Bounce ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}>Bounce</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Roll ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}>Roll</div>
-                            </div>
-                        </div>
-                        <div className="ribbon-doubleButton" style={{ display: effect === 'None' ? "none" : "inline-flex" }}>
-                            <div className="presBox-subheading" >Effect direction</div>
-                            <div className="ribbon-property">
-                                {this.effectDirection}
+                            <div className="effectDirection" style={{ display: effect === 'None' ? 'none' : 'grid', width: 40 }}>
+                                <Tooltip title={<div className="dash-tooltip">{'Enter from left'}</div>}>
+                                    <div
+                                        style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Left)}>
+                                        <FontAwesomeIcon icon={'angle-right'} />
+                                    </div>
+                                </Tooltip>
+                                <Tooltip title={<div className="dash-tooltip">{'Enter from right'}</div>}>
+                                    <div
+                                        style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Right)}>
+                                        <FontAwesomeIcon icon={'angle-left'} />
+                                    </div>
+                                </Tooltip>
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Enter from top'}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Top)}>
+                                        <FontAwesomeIcon icon={'angle-down'} />
+                                    </div>
+                                </Tooltip>
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Enter from bottom'}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Bottom)}>
+                                        <FontAwesomeIcon icon={'angle-up'} />
+                                    </div>
+                                </Tooltip>
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Enter from center'}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        style={{
+                                            gridColumn: 2,
+                                            gridRow: 2,
+                                            width: 10,
+                                            height: 10,
+                                            alignSelf: 'center',
+                                            justifySelf: 'center',
+                                            border: this.activeItem.presEffectDirection === PresEffect.Center || !this.activeItem.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : 'solid 2px black',
+                                            borderRadius: '100%',
+                                            cursor: 'pointer',
+                                        }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Center)}></div>
+                                </Tooltip>
                             </div>
                         </div>
-                        <div className="effectDirection" style={{ display: effect === 'None' ? "none" : "grid", width: 40 }}>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: this.activeItem.presEffectDirection === PresEffect.Center || !this.activeItem.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip>
-                        </div>
-                    </div>}
+                    )}
                     <div className="ribbon-final-box">
                         <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
                             Apply to all
                         </div>
                     </div>
-                </div >
+                </div>
             );
         }
     }
@@ -1338,11 +1563,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get effectDirection(): string {
         let effect = '';
         switch (this.activeItem.presEffectDirection) {
-            case 'left': effect = "Enter from left"; break;
-            case 'right': effect = "Enter from right"; break;
-            case 'top': effect = "Enter from top"; break;
-            case 'bottom': effect = "Enter from bottom"; break;
-            default: effect = "Enter from center"; break;
+            case 'left':
+                effect = 'Enter from left';
+                break;
+            case 'right':
+                effect = 'Enter from right';
+                break;
+            case 'top':
+                effect = 'Enter from top';
+                break;
+            case 'bottom':
+                effect = 'Enter from bottom';
+                break;
+            default:
+                effect = 'Enter from center';
+                break;
         }
         return effect;
     }
@@ -1355,7 +1590,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.updateMovement(activeItem.presMovement, true);
         this.updateEffect(activeItem.presEffect, true);
         this.updateEffectDirection(activeItem.presEffectDirection, true);
-        array.forEach((doc) => {
+        array.forEach(doc => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
             if (tagDoc && targetDoc) {
@@ -1365,63 +1600,86 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 curDoc.presHideAfter = activeItem.presHideAfter;
             }
         });
-    }
+    };
 
     @computed get presPinViewOptionsDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 16, filter: 'invert(1)' }} />;
+        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 16, filter: 'invert(1)' }} />;
         return (
             <>
-                {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)}
+                {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : null}
                 <div className="ribbon-doubleButton">
-                    <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : "" }}
-                        onClick={() => {
-                            activeItem.presPinView = !activeItem.presPinView;
-                            targetDoc.presPinView = activeItem.presPinView;
-                            if (activeItem.presPinView) {
-                                if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
-                                    const scroll = targetDoc._scrollTop;
-                                    activeItem.presPinView = true;
-                                    activeItem.presPinViewScroll = scroll;
-                                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                                    activeItem.presStartTime = targetDoc._currentTimecode;
-                                    activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                                } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
-                                    const x = targetDoc._panX;
-                                    const y = targetDoc._panY;
-                                    const scale = targetDoc._viewScale;
-                                    activeItem.presPinView = true;
-                                    activeItem.presPinViewX = x;
-                                    activeItem.presPinViewY = y;
-                                    activeItem.presPinViewScale = scale;
-                                } else if (targetDoc.type === DocumentType.COMPARISON) {
-                                    const width = targetDoc._clipWidth;
-                                    activeItem.presPinClipWidth = width;
-                                    activeItem.presPinView = true;
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{activeItem.presPinView ? 'Turn off pin with view' : 'Turn on pin with view'}</div>
+                            </>
+                        }>
+                        <div
+                            className="ribbon-toggle"
+                            style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : '' }}
+                            onClick={() => {
+                                activeItem.presPinView = !activeItem.presPinView;
+                                targetDoc.presPinView = activeItem.presPinView;
+                                if (activeItem.presPinView) {
+                                    if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
+                                        const scroll = targetDoc._scrollTop;
+                                        activeItem.presPinView = true;
+                                        activeItem.presPinViewScroll = scroll;
+                                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
+                                        activeItem.presStartTime = targetDoc._currentTimecode;
+                                        activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
+                                    } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
+                                        const x = targetDoc._panX;
+                                        const y = targetDoc._panY;
+                                        const scale = targetDoc._viewScale;
+                                        activeItem.presPinView = true;
+                                        activeItem.presPinViewX = x;
+                                        activeItem.presPinViewY = y;
+                                        activeItem.presPinViewScale = scale;
+                                    } else if (targetDoc.type === DocumentType.COMPARISON) {
+                                        const width = targetDoc._clipWidth;
+                                        activeItem.presPinClipWidth = width;
+                                        activeItem.presPinView = true;
+                                    }
                                 }
-                            }
-                        }}>{presPinWithViewIcon}</div></Tooltip>
-                    {activeItem.presPinView ? <Tooltip title={<><div className="dash-tooltip">{"Update the pinned view with the view of the selected document"}</div></>}><div className="ribbon-button"
-                        onClick={() => {
-                            if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
-                                const scroll = targetDoc._scrollTop;
-                                activeItem.presPinViewScroll = scroll;
-                            } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                                activeItem.presStartTime = targetDoc._currentTimecode;
-                                activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                            } else if (targetDoc.type === DocumentType.COMPARISON) {
-                                const clipWidth = targetDoc._clipWidth;
-                                activeItem.presPinClipWidth = clipWidth;
-                            } else {
-                                const x = targetDoc._panX;
-                                const y = targetDoc._panY;
-                                const scale = targetDoc._viewScale;
-                                activeItem.presPinViewX = x;
-                                activeItem.presPinViewY = y;
-                                activeItem.presPinViewScale = scale;
-                            }
-                        }}>Update</div></Tooltip> : (null)}
+                            }}>
+                            {presPinWithViewIcon}
+                        </div>
+                    </Tooltip>
+                    {activeItem.presPinView ? (
+                        <Tooltip
+                            title={
+                                <>
+                                    <div className="dash-tooltip">{'Update the pinned view with the view of the selected document'}</div>
+                                </>
+                            }>
+                            <div
+                                className="ribbon-button"
+                                onClick={() => {
+                                    if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
+                                        const scroll = targetDoc._scrollTop;
+                                        activeItem.presPinViewScroll = scroll;
+                                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
+                                        activeItem.presStartTime = targetDoc._currentTimecode;
+                                        activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
+                                    } else if (targetDoc.type === DocumentType.COMPARISON) {
+                                        const clipWidth = targetDoc._clipWidth;
+                                        activeItem.presPinClipWidth = clipWidth;
+                                    } else {
+                                        const x = targetDoc._panX;
+                                        const y = targetDoc._panY;
+                                        const scale = targetDoc._viewScale;
+                                        activeItem.presPinViewX = x;
+                                        activeItem.presPinViewY = y;
+                                        activeItem.presPinViewScale = scale;
+                                    }
+                                }}>
+                                Update
+                            </div>
+                        </Tooltip>
+                    ) : null}
                 </div>
             </>
         );
@@ -1432,38 +1690,58 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         return (
             <>
-                {this.panable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
-                    <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                        <div className="presBox-subheading">Pan X</div>
-                        <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                            <input className="presBox-input"
-                                style={{ textAlign: 'left', width: 50 }}
-                                type="number" value={NumCast(activeItem.presPinViewX)}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} />
+                {this.panable ? (
+                    <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
+                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+                            <div className="presBox-subheading">Pan X</div>
+                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+                                <input
+                                    className="presBox-input"
+                                    style={{ textAlign: 'left', width: 50 }}
+                                    type="number"
+                                    value={NumCast(activeItem.presPinViewX)}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                        const val = e.target.value;
+                                        activeItem.presPinViewX = Number(val);
+                                    })}
+                                />
+                            </div>
                         </div>
-                    </div>
-                    <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                        <div className="presBox-subheading">Pan Y</div>
-                        <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                            <input className="presBox-input"
-                                style={{ textAlign: 'left', width: 50 }}
-                                type="number" value={NumCast(activeItem.presPinViewY)}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} />
+                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+                            <div className="presBox-subheading">Pan Y</div>
+                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+                                <input
+                                    className="presBox-input"
+                                    style={{ textAlign: 'left', width: 50 }}
+                                    type="number"
+                                    value={NumCast(activeItem.presPinViewY)}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                        const val = e.target.value;
+                                        activeItem.presPinViewY = Number(val);
+                                    })}
+                                />
+                            </div>
                         </div>
-                    </div>
-                    <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                        <div className="presBox-subheading">Scale</div>
-                        <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                            <input className="presBox-input"
-                                style={{ textAlign: 'left', width: 50 }}
-                                type="number" value={NumCast(activeItem.presPinViewScale)}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
+                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+                            <div className="presBox-subheading">Scale</div>
+                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+                                <input
+                                    className="presBox-input"
+                                    style={{ textAlign: 'left', width: 50 }}
+                                    type="number"
+                                    value={NumCast(activeItem.presPinViewScale)}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                        const val = e.target.value;
+                                        activeItem.presPinViewScale = Number(val);
+                                    })}
+                                />
+                            </div>
                         </div>
                     </div>
-                </div> : (null)}
+                ) : null}
             </>
         );
     }
@@ -1473,18 +1751,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         return (
             <>
-                {this.scrollable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
-                    <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                        <div className="presBox-subheading">Scroll</div>
-                        <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                            <input className="presBox-input"
-                                style={{ textAlign: 'left', width: 50 }}
-                                type="number" value={NumCast(activeItem.presPinViewScroll)}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScroll = Number(val); })} />
+                {this.scrollable ? (
+                    <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
+                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+                            <div className="presBox-subheading">Scroll</div>
+                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+                                <input
+                                    className="presBox-input"
+                                    style={{ textAlign: 'left', width: 50 }}
+                                    type="number"
+                                    value={NumCast(activeItem.presPinViewScroll)}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                        const val = e.target.value;
+                                        activeItem.presPinViewScroll = Number(val);
+                                    })}
+                                />
+                            </div>
                         </div>
                     </div>
-                </div> : (null)}
+                ) : null}
             </>
         );
     }
@@ -1494,13 +1780,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const list = this.childDocs.map((doc, i) => {
             if (i > this.itemIndex) {
                 return (
-                    <option>{i + 1}. {doc.title}</option>
+                    <option>
+                        {i + 1}. {StrCast(doc.title)}
+                    </option>
                 );
             }
         });
-        return (
-            list
-        );
+        return list;
     }
 
     @computed get mediaOptionsDropdown() {
@@ -1510,25 +1796,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const clipEnd: number = NumCast(activeItem.clipEnd);
         const duration = Math.round(NumCast(activeItem[`${Doc.LayoutFieldKey(activeItem)}-duration`]) * 10);
         const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc);
-        const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + ". " + this.childDocs[mediaStopDocInd - 1].title : "";
+        const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + '. ' + this.childDocs[mediaStopDocInd - 1].title : '';
         if (activeItem && targetDoc) {
             return (
                 <div>
                     <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                         <div>
                             <div className="ribbon-box">
-                                Start {"&"} End Time
-                                <div className={"slider-headers"}>
-                                    <div className="slider-block" >
+                                Start {'&'} End Time
+                                <div className={'slider-headers'}>
+                                    <div className="slider-block">
                                         <div className="slider-text" style={{ fontWeight: 500 }}>
                                             Start time (s)
                                         </div>
-                                        <div id={"startTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
-                                            <input className="presBox-input"
+                                        <div id={'startTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
+                                            <input
+                                                className="presBox-input"
                                                 style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }}
-                                                type="number" value={NumCast(activeItem.presStartTime)}
+                                                type="number"
+                                                value={NumCast(activeItem.presStartTime)}
                                                 onKeyDown={e => e.stopPropagation()}
-                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); })}
+                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                                    activeItem.presStartTime = Number(e.target.value);
+                                                })}
                                             />
                                         </div>
                                     </div>
@@ -1544,24 +1834,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         <div className="slider-text" style={{ fontWeight: 500 }}>
                                             End time (s)
                                         </div>
-                                        <div id={"endTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
-                                            <input className="presBox-input"
+                                        <div id={'endTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
+                                            <input
+                                                className="presBox-input"
                                                 onKeyDown={e => e.stopPropagation()}
                                                 style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }}
-                                                type="number" value={NumCast(activeItem.presEndTime)}
-                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presEndTime = Number(e.target.value); })}
+                                                type="number"
+                                                value={NumCast(activeItem.presEndTime)}
+                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                                    activeItem.presEndTime = Number(e.target.value);
+                                                })}
                                             />
                                         </div>
                                     </div>
                                 </div>
                                 <div className="multiThumb-slider">
-                                    <input type="range" step="0.1" min={clipStart} max={clipEnd} value={NumCast(activeItem.presEndTime)}
+                                    <input
+                                        type="range"
+                                        step="0.1"
+                                        min={clipStart}
+                                        max={clipEnd}
+                                        value={NumCast(activeItem.presEndTime)}
                                         style={{ gridColumn: 1, gridRow: 1 }}
-                                        className={`toolbar-slider ${"end"}`}
+                                        className={`toolbar-slider ${'end'}`}
                                         id="toolbar-slider"
                                         onPointerDown={() => {
-                                            this._batch = UndoManager.StartBatch("presEndTime");
-                                            const endBlock = document.getElementById("endTime");
+                                            this._batch = UndoManager.StartBatch('presEndTime');
+                                            const endBlock = document.getElementById('endTime');
                                             if (endBlock) {
                                                 endBlock.style.color = Colors.LIGHT_GRAY;
                                                 endBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
@@ -1569,7 +1868,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         }}
                                         onPointerUp={() => {
                                             this._batch?.end();
-                                            const endBlock = document.getElementById("endTime");
+                                            const endBlock = document.getElementById('endTime');
                                             if (endBlock) {
                                                 endBlock.style.color = Colors.BLACK;
                                                 endBlock.style.backgroundColor = Colors.LIGHT_GRAY;
@@ -1578,14 +1877,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                             e.stopPropagation();
                                             activeItem.presEndTime = Number(e.target.value);
-                                        }} />
-                                    <input type="range" step="0.1" min={clipStart} max={clipEnd} value={NumCast(activeItem.presStartTime)}
+                                        }}
+                                    />
+                                    <input
+                                        type="range"
+                                        step="0.1"
+                                        min={clipStart}
+                                        max={clipEnd}
+                                        value={NumCast(activeItem.presStartTime)}
                                         style={{ gridColumn: 1, gridRow: 1 }}
-                                        className={`toolbar-slider ${"start"}`}
+                                        className={`toolbar-slider ${'start'}`}
                                         id="toolbar-slider"
                                         onPointerDown={() => {
-                                            this._batch = UndoManager.StartBatch("presStartTime");
-                                            const startBlock = document.getElementById("startTime");
+                                            this._batch = UndoManager.StartBatch('presStartTime');
+                                            const startBlock = document.getElementById('startTime');
                                             if (startBlock) {
                                                 startBlock.style.color = Colors.LIGHT_GRAY;
                                                 startBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
@@ -1593,7 +1898,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         }}
                                         onPointerUp={() => {
                                             this._batch?.end();
-                                            const startBlock = document.getElementById("startTime");
+                                            const startBlock = document.getElementById('startTime');
                                             if (startBlock) {
                                                 startBlock.style.color = Colors.BLACK;
                                                 startBlock.style.backgroundColor = Colors.LIGHT_GRAY;
@@ -1602,9 +1907,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                             e.stopPropagation();
                                             activeItem.presStartTime = Number(e.target.value);
-                                        }} />
+                                        }}
+                                    />
                                 </div>
-                                <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
+                                <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}>
                                     <div className="slider-text">{clipStart} s</div>
                                     <div className="slider-text"></div>
                                     <div className="slider-text">{clipEnd} s</div>
@@ -1615,38 +1921,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 <div className="presBox-subheading">Start playing:</div>
                                 <div className="presBox-radioButtons">
                                     <div className="checkbox-container">
-                                        <input className="presBox-checkbox"
-                                            type="checkbox"
-                                            onChange={() => activeItem.mediaStart = "manual"}
-                                            checked={activeItem.mediaStart === "manual"}
-                                        />
+                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'manual')} checked={activeItem.mediaStart === 'manual'} />
                                         <div>On click</div>
                                     </div>
                                     <div className="checkbox-container">
-                                        <input className="presBox-checkbox"
-                                            type="checkbox"
-                                            onChange={() => activeItem.mediaStart = "auto"}
-                                            checked={activeItem.mediaStart === "auto"}
-                                        />
+                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'auto')} checked={activeItem.mediaStart === 'auto'} />
                                         <div>Automatically</div>
                                     </div>
                                 </div>
                                 <div className="presBox-subheading">Stop playing:</div>
                                 <div className="presBox-radioButtons">
                                     <div className="checkbox-container">
-                                        <input className="presBox-checkbox"
-                                            type="checkbox"
-                                            onChange={() => activeItem.mediaStop = "manual"}
-                                            checked={activeItem.mediaStop === "manual"}
-                                        />
+                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'manual')} checked={activeItem.mediaStop === 'manual'} />
                                         <div>At audio end time</div>
                                     </div>
                                     <div className="checkbox-container">
-                                        <input className="presBox-checkbox"
-                                            type="checkbox"
-                                            onChange={() => activeItem.mediaStop = "auto"}
-                                            checked={activeItem.mediaStop === "auto"}
-                                        />
+                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'auto')} checked={activeItem.mediaStop === 'auto'} />
                                         <div>On slide change</div>
                                     </div>
                                     {/* <div className="checkbox-container">
@@ -1670,7 +1960,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             </div>
                         </div>
                     </div>
-                </div >
+                </div>
             );
         }
     }
@@ -1678,84 +1968,137 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get newDocumentToolbarDropdown() {
         return (
             <div>
-                <div className={'presBox-toolbar-dropdown'} style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                <div
+                    className={'presBox-toolbar-dropdown'}
+                    style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
+                    onClick={e => e.stopPropagation()}
+                    onPointerUp={e => e.stopPropagation()}
+                    onPointerDown={e => e.stopPropagation()}>
                     <div className="layout-container" style={{ height: 'max-content' }}>
-                        <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} />
-                        <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}>
+                        <div
+                            className="layout"
+                            style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                            onClick={action(() => {
+                                this.layout = 'blank';
+                                this.createNewSlide(this.layout);
+                            })}
+                        />
+                        <div
+                            className="layout"
+                            style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                            onClick={action(() => {
+                                this.layout = 'title';
+                                this.createNewSlide(this.layout);
+                            })}>
                             <div className="title">Title</div>
                             <div className="subtitle">Subtitle</div>
                         </div>
-                        <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}>
-                            <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div>
+                        <div
+                            className="layout"
+                            style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                            onClick={action(() => {
+                                this.layout = 'header';
+                                this.createNewSlide(this.layout);
+                            })}>
+                            <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
+                                Section header
+                            </div>
                         </div>
-                        <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}>
-                            <div className="title" style={{ alignSelf: 'center' }}>Title</div>
+                        <div
+                            className="layout"
+                            style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                            onClick={action(() => {
+                                this.layout = 'content';
+                                this.createNewSlide(this.layout);
+                            })}>
+                            <div className="title" style={{ alignSelf: 'center' }}>
+                                Title
+                            </div>
                             <div className="content">Text goes here</div>
                         </div>
                     </div>
                 </div>
-            </div >
+            </div>
         );
     }
 
     @observable openLayouts: boolean = false;
     @observable addFreeform: boolean = true;
-    @observable layout: string = "";
-    @observable title: string = "";
+    @observable layout: string = '';
+    @observable title: string = '';
 
     @computed get newDocumentDropdown() {
         return (
             <div>
-                <div className={"presBox-ribbon"} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                     <div className="ribbon-box">
                         Slide Title: <br></br>
-                        <input className="ribbon-textInput" placeholder="..." type="text" name="fname"
-                            onChange={(e) => {
+                        <input
+                            className="ribbon-textInput"
+                            placeholder="..."
+                            type="text"
+                            name="fname"
+                            onChange={e => {
                                 e.stopPropagation();
                                 e.preventDefault();
-                                runInAction(() => this.title = e.target.value);
-                            }}>
-                        </input>
+                                runInAction(() => (this.title = e.target.value));
+                            }}></input>
                     </div>
                     <div className="ribbon-box">
                         Choose type:
                         <div className="ribbon-doubleButton">
-                            <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : Colors.LIGHT_BLUE }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
-                            <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
+                            <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? '' : Colors.LIGHT_BLUE }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
+                                Text
+                            </div>
+                            <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : '' }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
+                                Freeform
+                            </div>
                         </div>
                     </div>
-                    <div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}>
+                    <div className="ribbon-box" style={{ display: this.addFreeform ? 'grid' : 'none' }}>
                         Preset layouts:
                         <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
-                            <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'blank')} />
-                            <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'title')}>
+                            <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'blank'))} />
+                            <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'title'))}>
                                 <div className="title">Title</div>
                                 <div className="subtitle">Subtitle</div>
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'header')}>
-                                <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div>
+                            <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'header'))}>
+                                <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
+                                    Section header
+                                </div>
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'content')}>
-                                <div className="title" style={{ alignSelf: 'center' }}>Title</div>
+                            <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'content'))}>
+                                <div className="title" style={{ alignSelf: 'center' }}>
+                                    Title
+                                </div>
                                 <div className="content">Text goes here</div>
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'twoColumns')}>
-                                <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div>
-                                <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div>
-                                <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div>
+                            <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'twoColumns'))}>
+                                <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>
+                                    Title
+                                </div>
+                                <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>
+                                    Column one text
+                                </div>
+                                <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>
+                                    Column two text
+                                </div>
                             </div>
                         </div>
-                        <div className="open-layout" onClick={action(() => this.openLayouts = !this.openLayouts)}>
-                            <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"caret-down"} size={"lg"} />
+                        <div className="open-layout" onClick={action(() => (this.openLayouts = !this.openLayouts))}>
+                            <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'caret-down'} size={'lg'} />
                         </div>
                     </div>
                     <div className="ribbon-final-box">
-                        <div className={this.title !== "" && (this.addFreeform && this.layout !== "" || !this.addFreeform) ? "ribbon-final-button-hidden" : "ribbon-final-button"} onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
+                        <div
+                            className={this.title !== '' && ((this.addFreeform && this.layout !== '') || !this.addFreeform) ? 'ribbon-final-button-hidden' : 'ribbon-final-button'}
+                            onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
                             Create New Slide
                         </div>
                     </div>
                 </div>
-            </div >
+            </div>
         );
     }
 
@@ -1763,7 +2106,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         let doc = undefined;
         if (layout) doc = this.createTemplate(layout);
         if (freeform && layout) doc = this.createTemplate(layout, title);
-        if (!freeform && !layout) doc = Docs.Create.TextDocument("", { _nativeWidth: 400, _width: 225, title: title });
+        if (!freeform && !layout) doc = Docs.Create.TextDocument('', { _nativeWidth: 400, _width: 225, title: title });
         if (doc) {
             const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
             const data = Cast(presCollection?.data, listSpec(Doc));
@@ -1773,10 +2116,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 TabDocView.PinDoc(doc);
                 this.gotoDocument(this.childDocs.length, this.activeItem);
             } else {
-                this.props.addDocTab(doc, "add:right");
+                this.props.addDocTab(doc, 'add:right');
             }
         }
-    }
+    };
 
     createTemplate = (layout: string, input?: string) => {
         const activeItem: Doc = this.activeItem;
@@ -1788,43 +2131,59 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20;
         }
         let doc = undefined;
-        const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24pt", });
-        const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16pt" });
-        const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20pt" });
-        const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24pt" });
-        const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14pt" });
-        const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14pt" });
-        const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14pt" });
+        const title = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
+        const subtitle = Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
+        const header = Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
+        const contentTitle = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
+        const content = Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
+        const content1 = Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
+        const content2 = Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
         switch (layout) {
             case 'blank':
-                doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x: x, y: y });
                 break;
             case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([header], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             default:
                 break;
         }
         return doc;
-    }
+    };
 
     // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view)
     @computed get presentDropdown() {
         return (
-            <div className={`dropdown-play ${this.presentTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
-                <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.updateMinimize(); this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); }))}>
+            <div className={`dropdown-play ${this.presentTools ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                <div
+                    className="dropdown-play-button"
+                    onClick={undoBatch(
+                        action(() => {
+                            this.updateMinimize();
+                            this.turnOffEdit(true);
+                            this.gotoDocument(this.itemIndex, this.activeItem);
+                        })
+                    )}>
                     Mini-player
                 </div>
-                <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); }))}>
+                <div
+                    className="dropdown-play-button"
+                    onClick={undoBatch(
+                        action(() => {
+                            this.layoutDoc.presStatus = 'manual';
+                            this.turnOffEdit(true);
+                            this.gotoDocument(this.itemIndex, this.activeItem);
+                        })
+                    )}>
                     Sidebar player
                 </div>
             </div>
@@ -1835,7 +2194,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     nextKeyframe = (tagDoc: Doc, curDoc: Doc): void => {
         const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
-        const currentFrame = Cast(tagDoc._currentFrame, "number", null);
+        const currentFrame = Cast(tagDoc._currentFrame, 'number', null);
         if (currentFrame === undefined) {
             tagDoc._currentFrame = 0;
             // CollectionFreeFormDocumentView.setupScroll(tagDoc, 0);
@@ -1845,19 +2204,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc);
         tagDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1);
         tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame));
-    }
+    };
 
     @action
     prevKeyframe = (tagDoc: Doc, actItem: Doc): void => {
         const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
-        const currentFrame = Cast(tagDoc._currentFrame, "number", null);
+        const currentFrame = Cast(tagDoc._currentFrame, 'number', null);
         if (currentFrame === undefined) {
             tagDoc._currentFrame = 0;
             // CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
         }
         CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice());
         tagDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1);
-    }
+    };
 
     /**
      * Returns the collection type as a string for headers
@@ -1868,15 +2227,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         let type: string = '';
         if (activeItem) {
             switch (targetDoc.type) {
-                case DocumentType.PDF: type = "PDF"; break;
-                case DocumentType.RTF: type = "Text node"; break;
-                case DocumentType.COL: type = "Collection"; break;
-                case DocumentType.AUDIO: type = "Audio"; break;
-                case DocumentType.VID: type = "Video"; break;
-                case DocumentType.IMG: type = "Image"; break;
-                case DocumentType.WEB: type = "Web page"; break;
-                case DocumentType.MAP: type = "Map"; break;
-                default: type = "Other node"; break;
+                case DocumentType.PDF:
+                    type = 'PDF';
+                    break;
+                case DocumentType.RTF:
+                    type = 'Text node';
+                    break;
+                case DocumentType.COL:
+                    type = 'Collection';
+                    break;
+                case DocumentType.AUDIO:
+                    type = 'Audio';
+                    break;
+                case DocumentType.VID:
+                    type = 'Video';
+                    break;
+                case DocumentType.IMG:
+                    type = 'Image';
+                    break;
+                case DocumentType.WEB:
+                    type = 'Web page';
+                    break;
+                case DocumentType.MAP:
+                    type = 'Map';
+                    break;
+                default:
+                    type = 'Other node';
+                    break;
             }
         }
         return type;
@@ -1885,66 +2262,122 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable private openActiveColorPicker: boolean = false;
     @observable private openViewedColorPicker: boolean = false;
 
-
-
     @computed get progressivizeDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         if (activeItem && targetDoc) {
-            const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
-            const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
+            const activeFontColor = targetDoc['pres-text-color'] ? StrCast(targetDoc['pres-text-color']) : 'Black';
+            const viewedFontColor = targetDoc['pres-text-viewed-color'] ? StrCast(targetDoc['pres-text-viewed-color']) : 'Black';
             return (
                 <div>
-                    <div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                    <div
+                        className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === 'edit' ? 'active' : ''}`}
+                        onClick={e => e.stopPropagation()}
+                        onPointerUp={e => e.stopPropagation()}
+                        onPointerDown={e => e.stopPropagation()}>
                         <div className="ribbon-box">
                             {this.stringType} selected
-                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
-                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeChild}>Contents</div>
-                                <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editProgressivize}>Edit</div>
+                            <div
+                                className="ribbon-doubleButton"
+                                style={{
+                                    borderTop: 'solid 1px darkgrey',
+                                    display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
+                                }}>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeChild}>
+                                    Contents
+                                </div>
+                                <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editProgressivize}>
+                                    Edit
+                                </div>
                             </div>
-                            <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
+                            <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
                                 <div className="presBox-subheading">Active text color</div>
-                                <div className="ribbon-colorBox" style={{ backgroundColor: activeFontColor, height: 15, width: 15 }} onClick={action(() => { this.openActiveColorPicker = !this.openActiveColorPicker; })}>
-                                </div>
+                                <div
+                                    className="ribbon-colorBox"
+                                    style={{ backgroundColor: activeFontColor, height: 15, width: 15 }}
+                                    onClick={action(() => {
+                                        this.openActiveColorPicker = !this.openActiveColorPicker;
+                                    })}></div>
                             </div>
                             {this.activeColorPicker}
-                            <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
+                            <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
                                 <div className="presBox-subheading">Viewed font color</div>
-                                <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => this.openViewedColorPicker = !this.openViewedColorPicker)}>
-                                </div>
+                                <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => (this.openViewedColorPicker = !this.openViewedColorPicker))}></div>
                             </div>
                             {this.viewedColorPicker}
-                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
-                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeZoom}>Zoom</div>
-                                <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editZoomProgressivize}>Edit</div>
+                            <div
+                                className="ribbon-doubleButton"
+                                style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? 'inline-flex' : 'none' }}>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeZoom}>
+                                    Zoom
+                                </div>
+                                <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editZoomProgressivize}>
+                                    Edit
+                                </div>
                             </div>
-                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
-                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeScroll}>Scroll</div>
-                                <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editScrollProgressivize}>Edit</div>
+                            <div
+                                className="ribbon-doubleButton"
+                                style={{
+                                    borderTop: 'solid 1px darkgrey',
+                                    display: targetDoc._viewType === 'stacking' || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
+                                }}>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeScroll}>
+                                    Scroll
+                                </div>
+                                <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editScrollProgressivize}>
+                                    Edit
+                                </div>
                             </div>
                         </div>
                         <div className="ribbon-final-box">
                             Frames
                             <div className="ribbon-doubleButton">
                                 <div className="ribbon-frameSelector">
-                                    <div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc, activeItem); }}>
-                                        <FontAwesomeIcon icon={"caret-left"} size={"lg"} />
+                                    <div
+                                        key="back"
+                                        title="back frame"
+                                        className="backKeyframe"
+                                        onClick={e => {
+                                            e.stopPropagation();
+                                            this.prevKeyframe(targetDoc, activeItem);
+                                        }}>
+                                        <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
                                     </div>
-                                    <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.keyFrameEditing ? "white" : "black", backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }}
-                                        onClick={action(() => targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing)} >
+                                    <div
+                                        key="num"
+                                        title="toggle view all"
+                                        className="numKeyframe"
+                                        style={{ color: targetDoc.keyFrameEditing ? 'white' : 'black', backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }}
+                                        onClick={action(() => (targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing))}>
                                         {NumCast(targetDoc._currentFrame)}
                                     </div>
-                                    <div key="fwd" title="forward frame" className="fwdKeyframe" onClick={e => { e.stopPropagation(); this.nextKeyframe(targetDoc, activeItem); }}>
-                                        <FontAwesomeIcon icon={"caret-right"} size={"lg"} />
+                                    <div
+                                        key="fwd"
+                                        title="forward frame"
+                                        className="fwdKeyframe"
+                                        onClick={e => {
+                                            e.stopPropagation();
+                                            this.nextKeyframe(targetDoc, activeItem);
+                                        }}>
+                                        <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
                                     </div>
                                 </div>
-                                <Tooltip title={<><div className="dash-tooltip">{"Last frame"}</div></>}><div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div></Tooltip>
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Last frame'}</div>
+                                        </>
+                                    }>
+                                    <div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div>
+                                </Tooltip>
                             </div>
                             <div className="ribbon-frameList">
                                 {this.frameListHeader}
                                 {this.frameList}
                             </div>
-                            <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(" TODO: play frames")}>Play</div>
+                            <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(' TODO: play frames')}>
+                                Play
+                            </div>
                         </div>
                     </div>
                 </div>
@@ -1958,37 +2391,41 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         const val = String(color.hex);
-        targetDoc["pres-text-color"] = val;
+        targetDoc['pres-text-color'] = val;
         return true;
-    }
+    };
     @undoBatch
     @action
     switchPresented = (color: ColorState) => {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         const val = String(color.hex);
-        targetDoc["pres-text-viewed-color"] = val;
+        targetDoc['pres-text-viewed-color'] = val;
         return true;
-    }
+    };
 
     @computed get activeColorPicker() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        return !this.openActiveColorPicker ? (null) : <SketchPicker onChange={this.switchActive}
-            presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-                '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-                '#FFFFFF', '#f1efeb', 'transparent']}
-            color={StrCast(targetDoc["pres-text-color"])} />;
+        return !this.openActiveColorPicker ? null : (
+            <SketchPicker
+                onChange={this.switchActive}
+                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+                color={StrCast(targetDoc['pres-text-color'])}
+            />
+        );
     }
 
     @computed get viewedColorPicker() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        return !this.openViewedColorPicker ? (null) : <SketchPicker onChange={this.switchPresented}
-            presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-                '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-                '#FFFFFF', '#f1efeb', 'transparent']}
-            color={StrCast(targetDoc["pres-text-viewed-color"])} />;
+        return !this.openViewedColorPicker ? null : (
+            <SketchPicker
+                onChange={this.switchPresented}
+                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+                color={StrCast(targetDoc['pres-text-viewed-color'])}
+            />
+        );
     }
 
     @action
@@ -1999,7 +2436,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (srcContext) this.togglePath(srcContext, true);
         }
         // Turn off the progressivize editors for each document
-        this.childDocs.forEach((doc) => {
+        this.childDocs.forEach(doc => {
             doc.editSnapZoomProgressivize = false;
             doc.editZoomProgressivize = false;
             const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
@@ -2008,7 +2445,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 // targetDoc.editScrollProgressivize = false;
             }
         });
-    }
+    };
 
     //Toggle whether the user edits or not
     @action
@@ -2016,14 +2453,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         if (!targetDoc.editZoomProgressivize) {
-            if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true; targetDoc.zoomProgressivize = true;
+            if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true;
+            targetDoc.zoomProgressivize = true;
             targetDoc.editZoomProgressivize = true;
             activeItem.editZoomProgressivize = true;
         } else {
             targetDoc.editZoomProgressivize = false;
             activeItem.editZoomProgressivize = false;
         }
-    }
+    };
 
     //Toggle whether the user edits or not
     @action
@@ -2031,12 +2469,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         if (!targetDoc.editScrollProgressivize) {
-            if (!targetDoc.scrollProgressivize) { targetDoc.scrollProgressivize = true; activeItem.scrollProgressivize = true; }
+            if (!targetDoc.scrollProgressivize) {
+                targetDoc.scrollProgressivize = true;
+                activeItem.scrollProgressivize = true;
+            }
             targetDoc.editScrollProgressivize = true;
         } else {
             targetDoc.editScrollProgressivize = false;
         }
-    }
+    };
 
     //Progressivize Zoom
     @action
@@ -2052,7 +2493,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             targetDoc._currentFrame = 0;
             targetDoc.lastFrame = 0;
         }
-    }
+    };
 
     //Progressivize Zoom
     @action
@@ -2068,7 +2509,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             targetDoc._currentFrame = 0;
             targetDoc.lastFrame = 0;
         }
-    }
+    };
 
     //Progressivize Child Docs
     @action
@@ -2077,12 +2518,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         targetDoc._currentFrame = targetDoc.lastFrame;
         if (!targetDoc.editProgressivize) {
-            if (!activeItem.presProgressivize) { activeItem.presProgressivize = true; targetDoc.presProgressivize = true; }
+            if (!activeItem.presProgressivize) {
+                activeItem.presProgressivize = true;
+                targetDoc.presProgressivize = true;
+            }
             targetDoc.editProgressivize = true;
         } else {
             targetDoc.editProgressivize = false;
         }
-    }
+    };
 
     @action
     progressivizeChild = (e: React.MouseEvent) => {
@@ -2104,40 +2548,48 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             targetDoc._currentFrame = 0;
             targetDoc.keyFrameEditing = true;
         }
-    }
+    };
 
     @action
     checkMovementLists = (doc: Doc, xlist: any, ylist: any) => {
         const x: List<number> = xlist;
         const y: List<number> = ylist;
         const tags: JSX.Element[] = [];
-        let pathPoints = ""; //List of all of the pathpoints that need to be added
+        let pathPoints = ''; //List of all of the pathpoints that need to be added
         for (let i = 0; i < x.length - 1; i++) {
             if (y[i] || x[i]) {
-                if (i === 0) pathPoints = (x[i] - 11) + "," + (y[i] + 33);
-                else pathPoints = pathPoints + " " + (x[i] - 11) + "," + (y[i] + 33);
-                tags.push(<div className="progressivizeMove-frame" style={{ position: 'absolute', top: y[i], left: x[i] }}>{i}</div>);
+                if (i === 0) pathPoints = x[i] - 11 + ',' + (y[i] + 33);
+                else pathPoints = pathPoints + ' ' + (x[i] - 11) + ',' + (y[i] + 33);
+                tags.push(
+                    <div className="progressivizeMove-frame" style={{ position: 'absolute', top: y[i], left: x[i] }}>
+                        {i}
+                    </div>
+                );
             }
         }
-        tags.push(<svg style={{ overflow: 'visible', position: 'absolute' }}><polyline
-            points={pathPoints}
-            style={{
-                position: 'absolute',
-                opacity: 1,
-                stroke: "#000000",
-                strokeWidth: 2,
-                strokeDasharray: '10 5',
-            }}
-            fill="none"
-        /></svg>);
+        tags.push(
+            <svg style={{ overflow: 'visible', position: 'absolute' }}>
+                <polyline
+                    points={pathPoints}
+                    style={{
+                        position: 'absolute',
+                        opacity: 1,
+                        stroke: '#000000',
+                        strokeWidth: 2,
+                        strokeDasharray: '10 5',
+                    }}
+                    fill="none"
+                />
+            </svg>
+        );
         return tags;
-    }
+    };
 
     @observable
     toggleDisplayMovement = (doc: Doc) => {
         if (doc.displayMovement) doc.displayMovement = false;
         else doc.displayMovement = true;
-    }
+    };
 
     @action
     checkList = (doc: Doc, list: any): number => {
@@ -2149,22 +2601,54 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             x[NumCast(doc._currentFrame)] = x[NumCast(doc._currentFrame) - 1];
             return x[NumCast(doc._currentFrame)];
         } else return 100;
-    }
+    };
 
     @computed get progressivizeChildDocs() {
         const targetDoc: Doc = this.targetDoc;
         const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
         const tags: JSX.Element[] = [];
         docs.forEach((doc, index) => {
-            if (doc["x-indexed"] && doc["y-indexed"]) {
-                tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>);
+            if (doc['x-indexed'] && doc['y-indexed']) {
+                tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? 'block' : 'none' }}>{this.checkMovementLists(doc, doc['x-indexed'], doc['y-indexed'])}</div>);
             }
             tags.push(
-                <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
-                    <div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div>
-                    <div className="progressivizeButton-frame">{doc.appearFrame}</div>
-                    <div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div>
-                </div>);
+                <div
+                    className="progressivizeButton"
+                    key={index}
+                    onPointerLeave={() => {
+                        if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0;
+                    }}
+                    onPointerOver={() => {
+                        if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5;
+                    }}
+                    onClick={e => {
+                        this.toggleDisplayMovement(doc);
+                        e.stopPropagation();
+                    }}
+                    style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : '#c8c8c8', top: NumCast(doc.y), left: NumCast(doc.x) }}>
+                    <div className="progressivizeButton-prev">
+                        <FontAwesomeIcon
+                            icon={'caret-left'}
+                            size={'lg'}
+                            onClick={e => {
+                                e.stopPropagation();
+                                this.prevAppearFrame(doc, index);
+                            }}
+                        />
+                    </div>
+                    <div className="progressivizeButton-frame">{NumCast(doc.appearFrame)}</div>
+                    <div className="progressivizeButton-next">
+                        <FontAwesomeIcon
+                            icon={'caret-right'}
+                            size={'lg'}
+                            onClick={e => {
+                                e.stopPropagation();
+                                this.nextAppearFrame(doc, index);
+                            }}
+                        />
+                    </div>
+                </div>
+            );
         });
         return tags;
     }
@@ -2173,25 +2657,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     nextAppearFrame = (doc: Doc, i: number): void => {
         // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
         // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
-        const appearFrame = Cast(doc.appearFrame, "number", null);
+        const appearFrame = Cast(doc.appearFrame, 'number', null);
         if (appearFrame === undefined) {
             doc.appearFrame = 0;
         }
         doc.appearFrame = appearFrame + 1;
-        this.updateOpacityList(doc["opacity-indexed"], NumCast(doc.appearFrame));
-    }
+        this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
+    };
 
     @action
     prevAppearFrame = (doc: Doc, i: number): void => {
         // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
         // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
-        const appearFrame = Cast(doc.appearFrame, "number", null);
+        const appearFrame = Cast(doc.appearFrame, 'number', null);
         if (appearFrame === undefined) {
             doc.appearFrame = 0;
         }
         doc.appearFrame = Math.max(0, appearFrame - 1);
-        this.updateOpacityList(doc["opacity-indexed"], NumCast(doc.appearFrame));
-    }
+        this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
+    };
 
     @action
     updateOpacityList = (list: any, frame: number) => {
@@ -2216,10 +2700,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             list = x;
         }
-    }
+    };
 
     @computed get moreInfoDropdown() {
-        return (<div></div>);
+        return <div></div>;
     }
 
     @computed
@@ -2235,28 +2719,36 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         } else {
             CurrentUserUtils.propertiesWidth = 250;
         }
-    }
+    };
 
     @computed get toolbar() {
-        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
-        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel";
+        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
+        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
+        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation;
         const activeColor = Colors.LIGHT_BLUE;
         const inactiveColor = Colors.WHITE;
-        return (mode === CollectionViewType.Carousel3D) ? (null) : (
+        return mode === CollectionViewType.Carousel3D ? null : (
             <div id="toolbarContainer" className={'presBox-toolbar'}>
                 {/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
                     <FontAwesomeIcon icon={"plus"} />
                     <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} />
                 </div></Tooltip> */}
-                <Tooltip title={<><div className="dash-tooltip">{"View paths"}</div></>}>
-                    <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
-                        <FontAwesomeIcon icon={"exchange-alt"} />
+                <Tooltip
+                    title={
+                        <>
+                            <div className="dash-tooltip">{'View paths'}</div>
+                        </>
+                    }>
+                    <div
+                        style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }}
+                        className={'toolbar-button'}
+                        onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
+                        <FontAwesomeIcon icon={'exchange-alt'} />
                     </div>
                 </Tooltip>
-                {isMini ? (null) :
+                {isMini ? null : (
                     <>
                         <div className="toolbar-divider" />
                         {/* <Tooltip title={<><div className="dash-tooltip">{this._expandBoolean ? "Minimize all" : "Expand all"}</div></>}>
@@ -2267,18 +2759,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             </div>
                         </Tooltip>
                         <div className="toolbar-divider" /> */}
-                        <Tooltip title={<><div className="dash-tooltip">{presKeyEvents ? "Keys are active" : "Keys are not active - click anywhere on the presentation trail to activate keys"}</div></>}>
+                        <Tooltip
+                            title={
+                                <>
+                                    <div className="dash-tooltip">{presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}</div>
+                                </>
+                            }>
                             <div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}>
-                                <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? activeColor : inactiveColor }} />
+                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={'keyboard'} style={{ color: presKeyEvents ? activeColor : inactiveColor }} />
                             </div>
                         </Tooltip>
-                        <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}>
+                        <Tooltip
+                            title={
+                                <>
+                                    <div className="dash-tooltip">{propTitle}</div>
+                                </>
+                            }>
                             <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
-                                <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} />
+                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} />
                             </div>
                         </Tooltip>
                     </>
-                }
+                )}
             </div>
         );
     }
@@ -2292,35 +2794,45 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
         return (
-            <div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? "none" : undefined }}>
-                {isMini ? (null) : <select className="presBox-viewPicker"
-                    style={{ display: this.layoutDoc.presStatus === "edit" ? "block" : "none" }}
-                    onPointerDown={e => e.stopPropagation()}
-                    onChange={this.viewChanged}
-                    value={mode}>
-                    <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
-                    <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>Tree</option>
-                    {Doc.noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
-                </select>}
+            <div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
+                {isMini ? null : (
+                    <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
+                        <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>
+                            List
+                        </option>
+                        <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>
+                            Tree
+                        </option>
+                        {Doc.noviceMode ? null : (
+                            <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>
+                                3D Carousel
+                            </option>
+                        )}
+                    </select>
+                )}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
-                    <span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}>
-                        <div className="presBox-button-left"
+                    <span className={`presBox-button ${this.layoutDoc.presStatus === 'edit' ? 'present' : ''}`}>
+                        <div
+                            className="presBox-button-left"
                             onClick={undoBatch(() => {
                                 if (this.childDocs.length) {
-                                    this.layoutDoc.presStatus = "manual";
+                                    this.layoutDoc.presStatus = 'manual';
                                     this.gotoDocument(this.itemIndex, this.activeItem);
                                 }
                             })}>
-                            <FontAwesomeIcon icon={"play-circle"} />
-                            <div style={{ display: this.props.PanelWidth() > 200 ? "inline-flex" : "none" }}>&nbsp; Present</div>
+                            <FontAwesomeIcon icon={'play-circle'} />
+                            <div style={{ display: this.props.PanelWidth() > 200 ? 'inline-flex' : 'none' }}>&nbsp; Present</div>
                         </div>
-                        {(mode === CollectionViewType.Carousel3D || isMini) ? (null) : <div className={`presBox-button-right ${this.presentTools ? "active" : ""}`}
-                            onClick={(action(() => {
-                                if (this.childDocs.length) this.presentTools = !this.presentTools;
-                            }))}>
-                            <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"angle-down"} />
-                            {this.presentDropdown}
-                        </div>}
+                        {mode === CollectionViewType.Carousel3D || isMini ? null : (
+                            <div
+                                className={`presBox-button-right ${this.presentTools ? 'active' : ''}`}
+                                onClick={action(() => {
+                                    if (this.childDocs.length) this.presentTools = !this.presentTools;
+                                })}>
+                                <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'angle-down'} />
+                                {this.presentDropdown}
+                            </div>
+                        )}
                     </span>
                     {this.playButtons}
                 </div>
@@ -2332,7 +2844,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     getList = (list: any): List<number> => {
         const x: List<number> = list;
         return x;
-    }
+    };
 
     @action
     updateList = (list: any): List<number> => {
@@ -2341,7 +2853,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         x.length + 1;
         x[x.length - 1] = NumCast(targetDoc._scrollY);
         return x;
-    }
+    };
 
     @action
     newFrame = () => {
@@ -2350,7 +2862,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const type: string = StrCast(targetDoc.type);
         if (!activeItem.frameList) activeItem.frameList = new List<number>();
         switch (type) {
-            case (DocumentType.PDF || DocumentType.RTF || DocumentType.WEB):
+            case DocumentType.PDF || DocumentType.RTF || DocumentType.WEB:
                 this.updateList(activeItem.frameList);
                 break;
             case DocumentType.COL:
@@ -2358,20 +2870,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             default:
                 break;
         }
-    }
+    };
 
     @computed get frameListHeader() {
-        return (<div className="frameList-header">
-            &nbsp; Frames {this.panable ? <i>Panable</i> : this.scrollable ? <i>Scrollable</i> : (null)}
-            <div className={"frameList-headerButtons"}>
-                <Tooltip title={<><div className="dash-tooltip">{"Add frame by example"}</div></>}><div className={"headerButton"} onClick={e => { e.stopPropagation(); this.newFrame(); }}>
-                    <FontAwesomeIcon icon={"plus"} onPointerDown={e => e.stopPropagation()} />
-                </div></Tooltip>
-                <Tooltip title={<><div className="dash-tooltip">{"Edit in collection"}</div></>}><div className={"headerButton"} onClick={e => { e.stopPropagation(); console.log('New frame'); }}>
-                    <FontAwesomeIcon icon={"edit"} onPointerDown={e => e.stopPropagation()} />
-                </div></Tooltip>
+        return (
+            <div className="frameList-header">
+                &nbsp; Frames {this.panable ? <i>Panable</i> : this.scrollable ? <i>Scrollable</i> : null}
+                <div className={'frameList-headerButtons'}>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{'Add frame by example'}</div>
+                            </>
+                        }>
+                        <div
+                            className={'headerButton'}
+                            onClick={e => {
+                                e.stopPropagation();
+                                this.newFrame();
+                            }}>
+                            <FontAwesomeIcon icon={'plus'} onPointerDown={e => e.stopPropagation()} />
+                        </div>
+                    </Tooltip>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{'Edit in collection'}</div>
+                            </>
+                        }>
+                        <div
+                            className={'headerButton'}
+                            onClick={e => {
+                                e.stopPropagation();
+                                console.log('New frame');
+                            }}>
+                            <FontAwesomeIcon icon={'edit'} onPointerDown={e => e.stopPropagation()} />
+                        </div>
+                    </Tooltip>
+                </div>
             </div>
-        </div>);
+        );
     }
 
     @computed get frameList() {
@@ -2379,66 +2917,118 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         const frameList: List<number> = this.getList(activeItem.frameList);
         if (frameList) {
-            const frameItems = frameList.map((value) =>
-                <div className="framList-item">
-
-                </div>
-            );
-            return (
-
-                <div className="frameList-container">
-                    {frameItems}
-                </div>
-            );
-        } else return (null);
-
+            const frameItems = frameList.map(value => <div className="framList-item"></div>);
+            return <div className="frameList-container">{frameItems}</div>;
+        } else return null;
     }
 
     @computed get playButtonFrames() {
         const targetDoc: Doc = this.targetDoc;
         return (
             <>
-                {this.targetDoc ? <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? "inline-flex" : "none" }}>
-                    <div>{targetDoc._currentFrame}</div>
-                    <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
-                    <div>{targetDoc.lastFrame}</div>
-                </div> : null}
+                {this.targetDoc ? (
+                    <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? 'inline-flex' : 'none' }}>
+                        <div>{NumCast(targetDoc._currentFrame)}</div>
+                        <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
+                        <div>{NumCast(targetDoc.lastFrame)}</div>
+                    </div>
+                ) : null}
             </>
         );
     }
 
     @computed get playButtons() {
-        const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
-        const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
+        const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
+        const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
         // Case 1: There are still other frames and should go through all frames before going to next slide
-        return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}>
-            <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
-            <div className="presPanel-divider"></div>
-            <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div>
-            <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? "pause" : "play"} /></div></Tooltip>
-            <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onClick={() => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-right"} /></div>
-            <div className="presPanel-divider"></div>
-            <Tooltip title={<><div className="dash-tooltip">{"Click to return to 1st slide"}</div></>}><div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0, this.activeItem)}><b>1</b></div></Tooltip>
-            <div
-                className="presPanel-button-text"
-                onClick={() => this.gotoDocument(0, this.activeItem)}
-                style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
-                Slide {this.itemIndex + 1} / {this.childDocs.length}
-                {this.playButtonFrames}
+        return (
+            <div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}>
+                <Tooltip
+                    title={
+                        <>
+                            <div className="dash-tooltip">{'Loop'}</div>
+                        </>
+                    }>
+                    <div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}>
+                        <FontAwesomeIcon icon={'redo-alt'} />
+                    </div>
+                </Tooltip>
+                <div className="presPanel-divider"></div>
+                <div
+                    className="presPanel-button"
+                    style={{ opacity: presStart ? 0.4 : 1 }}
+                    onClick={() => {
+                        this.back();
+                        if (this._presTimer) {
+                            clearTimeout(this._presTimer);
+                            this.layoutDoc.presStatus = PresStatus.Manual;
+                        }
+                    }}>
+                    <FontAwesomeIcon icon={'arrow-left'} />
+                </div>
+                <Tooltip
+                    title={
+                        <>
+                            <div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>
+                        </>
+                    }>
+                    <div className="presPanel-button" onClick={this.startOrPause}>
+                        <FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
+                    </div>
+                </Tooltip>
+                <div
+                    className="presPanel-button"
+                    style={{ opacity: presEnd ? 0.4 : 1 }}
+                    onClick={() => {
+                        this.next();
+                        if (this._presTimer) {
+                            clearTimeout(this._presTimer);
+                            this.layoutDoc.presStatus = PresStatus.Manual;
+                        }
+                    }}>
+                    <FontAwesomeIcon icon={'arrow-right'} />
+                </div>
+                <div className="presPanel-divider"></div>
+                <Tooltip
+                    title={
+                        <>
+                            <div className="dash-tooltip">{'Click to return to 1st slide'}</div>
+                        </>
+                    }>
+                    <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0, this.activeItem)}>
+                        <b>1</b>
+                    </div>
+                </Tooltip>
+                <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
+                    Slide {this.itemIndex + 1} / {this.childDocs.length}
+                    {this.playButtonFrames}
+                </div>
+                <div className="presPanel-divider"></div>
+                {this.props.PanelWidth() > 250 ? (
+                    <div
+                        className="presPanel-button-text"
+                        onClick={undoBatch(
+                            action(() => {
+                                this.layoutDoc.presStatus = 'edit';
+                                clearTimeout(this._presTimer);
+                            })
+                        )}>
+                        EXIT
+                    </div>
+                ) : (
+                    <div className="presPanel-button" onClick={undoBatch(action(() => (this.layoutDoc.presStatus = 'edit')))}>
+                        <FontAwesomeIcon icon={'times'} />
+                    </div>
+                )}
             </div>
-            <div className="presPanel-divider"></div>
-            {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }))}>EXIT</div>
-                : <div className="presPanel-button" onClick={undoBatch(action(() => this.layoutDoc.presStatus = "edit"))}>
-                    <FontAwesomeIcon icon={"times"} />
-                </div>}
-        </div>);
+        );
     }
 
     @action
     startOrPause = () => {
         if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex);
         else this.pauseAutoPres();
-    }
+    };
 
     @action
     prevClicked = (e: PointerEvent) => {
@@ -2447,7 +3037,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             clearTimeout(this._presTimer);
             this.layoutDoc.presStatus = PresStatus.Manual;
         }
-    }
+    };
 
     @action
     nextClicked = (e: PointerEvent) => {
@@ -2456,35 +3046,35 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             clearTimeout(this._presTimer);
             this.layoutDoc.presStatus = PresStatus.Manual;
         }
-    }
+    };
     @undoBatch
     @action
     exitClicked = (e: PointerEvent) => {
         this.updateMinimize();
         this.layoutDoc.presStatus = PresStatus.Edit;
         clearTimeout(this._presTimer);
-    }
+    };
 
     @action
     startMarqueeCreateSlide = () => {
         PresBox.startMarquee = true;
-    }
+    };
 
     AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
         var indexNum = 0;
         for (let i = 0; i < index.length; i++) {
-            indexNum += (index[i] * (10 ** (-i)));
+            indexNum += index[i] * 10 ** -i;
         }
         if (this._treeViewMap.get(treeViewDoc) !== indexNum) {
             this._treeViewMap.set(treeViewDoc, indexNum);
             const sorted = this.sort(this._treeViewMap);
             const curList = DocListCast(this.dataDoc[this.presFieldKey]);
-            if (sorted.length !== curList.length || sorted.some((doc,ind) => doc !== curList[ind])) {
+            if (sorted.length !== curList.length || sorted.some((doc, ind) => doc !== curList[ind])) {
                 this.dataDoc[this.presFieldKey] = new List<Doc>(sorted); // this is a flat array of Docs
             }
         }
         return this.childDocs;
-    }
+    };
 
     RemFromMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
         if (!this._unmounting && this.isTree) {
@@ -2492,10 +3082,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.dataDoc[this.presFieldKey] = new List<Doc>(this.sort(this._treeViewMap));
         }
         return this.childDocs;
-    }
+    };
 
     // TODO: [AL] implement sort function for an array of numbers (e.g. arr[1,2,4] v arr[1,2,1])
-    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0).map(kv => kv[0]);
+    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]);
 
     render() {
         // calling this method for keyEvents
@@ -2503,42 +3093,73 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
-        const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
-        const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
-        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ?
+        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation;
+        const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
+        const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
+        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ? (
             <div className="miniPres" onClick={e => e.stopPropagation()}>
-                <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
-                    <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.layoutDoc.presLoop = !this.layoutDoc.presLoop, false, false)}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+                <div className="presPanelOverlay" style={{ display: 'inline-flex', height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{'Loop'}</div>
+                            </>
+                        }>
+                        <div
+                            className="presPanel-button"
+                            style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
+                            onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop), false, false)}>
+                            <FontAwesomeIcon icon={'redo-alt'} />
+                        </div>
+                    </Tooltip>
                     <div className="presPanel-divider"></div>
-                    <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }}
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}><FontAwesomeIcon icon={"arrow-left"} /></div>
-                    <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button"
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
-                    <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }}
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.nextClicked, false, false)}><FontAwesomeIcon icon={"arrow-right"} /></div>
+                    <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}>
+                        <FontAwesomeIcon icon={'arrow-left'} />
+                    </div>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>
+                            </>
+                        }>
+                        <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}>
+                            <FontAwesomeIcon icon={this.layoutDoc.presStatus === 'auto' ? 'pause' : 'play'} />
+                        </div>
+                    </Tooltip>
+                    <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.nextClicked, false, false)}>
+                        <FontAwesomeIcon icon={'arrow-right'} />
+                    </div>
                     <div className="presPanel-divider"></div>
-                    <Tooltip title={<><div className="dash-tooltip">{"Click to return to 1st slide"}</div></>}><div className="presPanel-button" style={{ border: 'solid 1px white' }}
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.gotoDocument(0, this.activeItem), false, false)}><b>1</b></div></Tooltip>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{'Click to return to 1st slide'}</div>
+                            </>
+                        }>
+                        <div className="presPanel-button" style={{ border: 'solid 1px white' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.gotoDocument(0, this.activeItem), false, false)}>
+                            <b>1</b>
+                        </div>
+                    </Tooltip>
                     <div className="presPanel-button-text">
                         Slide {this.itemIndex + 1} / {this.childDocs.length}
                         {this.playButtonFrames}
                     </div>
                     <div className="presPanel-divider" />
-                    <div className="presPanel-button-text" onPointerDown={e =>
-                        setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>EXIT</div>
+                    <div className="presPanel-button-text" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>
+                        EXIT
+                    </div>
                 </div>
-            </div >
-            :
-            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }} >
+            </div>
+        ) : (
+            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}>
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
                 <div className="presBox-listCont">
                     <div className="Slide" style={{ height: `calc(100% - 30px)` }}>
-                        {mode !== CollectionViewType.Invalid ?
-                            <CollectionView {...this.props}
+                        {mode !== CollectionViewType.Invalid ? (
+                            <CollectionView
+                                {...this.props}
                                 ContainingCollectionDoc={this.props.Document}
                                 PanelWidth={this.props.PanelWidth}
                                 PanelHeight={this.panelHeight}
@@ -2556,25 +3177,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 AddToMap={this.AddToMap}
                                 RemFromMap={this.RemFromMap}
                                 hierarchyIndex={[]}
-                            /> : (null)
-                        }
+                            />
+                        ) : null}
                     </div>
 
-                    { // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack
-                        <Tooltip title={<div className="dash-tooltip">{"Click on document to pin to presentaiton or make a marquee selection to pin your desired view"}</div>}>
+                    {
+                        // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack
+                        <Tooltip title={<div className="dash-tooltip">{'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}</div>}>
                             <button className="add-slide-button" onPointerDown={this.startMarqueeCreateSlide}>
                                 + NEW SLIDE
                             </button>
                         </Tooltip>
                     }
                 </div>
-            </div>;
+            </div>
+        );
     }
 }
 
-ScriptingGlobals.add(function navigateToDoc(bestTarget:Doc, activeItem:Doc) {
+ScriptingGlobals.add(function navigateToDoc(bestTarget: Doc, activeItem: Doc) {
     const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null);
-    const openInTab = (doc: Doc, finished?: () => void) => {CollectionDockingView.AddSplit(doc, "right"); finished?.(); };
-    DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext]:[], 
-        undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom));
-});
\ No newline at end of file
+    const openInTab = (doc: Doc, finished?: () => void) => {
+        CollectionDockingView.AddSplit(doc, 'right');
+        finished?.();
+    };
+    DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom));
+});
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 574193614..c5177de90 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,4 +1,4 @@
-import { Tooltip } from "@material-ui/core";
+import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -6,13 +6,12 @@ import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field } from '../..
 import { Id } from '../../../fields/FieldSymbols';
 import { StrCast } from '../../../fields/Types';
 import { DocUtils } from '../../documents/Documents';
-import { DocumentType } from "../../documents/DocumentTypes";
+import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { ViewBoxBaseComponent } from "../DocComponent";
+import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
-import "./SearchBox.scss";
-
+import './SearchBox.scss';
 
 const DAMPENING_FACTOR = 0.9;
 const MAX_ITERATIONS = 25;
@@ -29,13 +28,15 @@ export interface SearchBoxProps extends FieldViewProps {
  */
 @observer
 export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(SearchBox, fieldKey);
+    }
     public static Instance: SearchBox;
 
     private _inputRef = React.createRef<HTMLInputElement>();
 
-    @observable _searchString = "";
-    @observable _docTypeString = "all";
+    @observable _searchString = '';
+    @observable _docTypeString = 'all';
     @observable _results: Map<Doc, string[]> = new Map<Doc, string[]>();
     @observable _pageRanks: Map<Doc, number> = new Map<Doc, number>();
     @observable _linkedDocsOut: Map<Doc, Set<Doc>> = new Map<Doc, Set<Doc>>();
@@ -52,7 +53,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         SearchBox.Instance = this;
     }
 
-    /** 
+    /**
      * This method is called when the SearchBox component is first mounted. When the user opens
      * the search panel, the search input box is automatically selected. This allows the user to
      * type in the search input box immediately, without needing clicking on it first.
@@ -75,7 +76,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * This method is called when the text in the search input box is modified by the user. The
      * _searchString is updated to the new value of the text in the input box and submitSearch
      * is called to update the search results accordingly.
-     * 
+     *
      * (Note: There is no longer a need to press enter to submit a search. Any update to the input
      * causes a search to be submitted automatically.)
      */
@@ -88,7 +89,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * This method is called when the option in the select drop-down menu is changed. The
      * _docTypeString is updated to the new value of the option in the drop-down menu. This
      * is used to filter the results of the search to documents of a specific type.
-     * 
+     *
      * (Note: This doesn't affect the results array, so there is no need to submit a new
      * search here. The results of the search on the _searchString query are simply filtered
      * by type directly before rendering them.)
@@ -134,8 +135,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             docs.filter(d => d && !visited.includes(d)).forEach(d => {
                 visited.push(d);
                 const fieldKey = Doc.LayoutFieldKey(d);
-                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+                const data = d[annos ? fieldKey + '-annotations' : fieldKey];
                 data && newarray.push(...DocListCast(data));
                 func(depth, d);
             });
@@ -156,14 +157,18 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         var depth = 0;
         while (docs.length > 0) {
             newarray = [];
-            await Promise.all(docs.filter(d => d).map(async d => {
-                const fieldKey = Doc.LayoutFieldKey(d);
-                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                const data = d[annos ? fieldKey + "-annotations" : fieldKey];
-                const docs = await DocListCastAsync(data);
-                docs && newarray.push(...docs);
-                func(depth, d);
-            }));
+            await Promise.all(
+                docs
+                    .filter(d => d)
+                    .map(async d => {
+                        const fieldKey = Doc.LayoutFieldKey(d);
+                        const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+                        const data = d[annos ? fieldKey + '-annotations' : fieldKey];
+                        const docs = await DocListCastAsync(data);
+                        docs && newarray.push(...docs);
+                        func(depth, d);
+                    })
+            );
             docs = newarray;
             depth++;
         }
@@ -177,11 +182,10 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * right side of each search result.
      */
     static formatType(type: String): String {
-        if (type === "pdf") {
-            return "PDF";
-        }
-        else if (type === "image") {
-            return "Img";
+        if (type === 'pdf') {
+            return 'PDF';
+        } else if (type === 'image') {
+            return 'Img';
         }
 
         return type.charAt(0).toUpperCase() + type.substring(1, 3);
@@ -198,9 +202,40 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     @action
     searchCollection(query: string) {
         const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
-        const blockedKeys = ["x", "y", "proto", "width", "autoHeight", "acl-Override", "acl-Public", "context", "zIndex", "height", "text-scrollHeight", "text-height", "cloneFieldFilter", "isPrototype", "text-annotations",
-            "dragFactory-count", "text-noTemplate", "aliases", "system", "layoutKey", "baseProto", "xMargin", "yMargin", "links", "layout", "layout_keyValue", "fitWidth", "viewType", "title-custom",
-            "panX", "panY", "viewScale"];
+        const blockedKeys = [
+            'x',
+            'y',
+            'proto',
+            'width',
+            'autoHeight',
+            'acl-Override',
+            'acl-Public',
+            'context',
+            'zIndex',
+            'height',
+            'text-scrollHeight',
+            'text-height',
+            'cloneFieldFilter',
+            'isPrototype',
+            'text-annotations',
+            'dragFactory-count',
+            'text-noTemplate',
+            'aliases',
+            'system',
+            'layoutKey',
+            'baseProto',
+            'xMargin',
+            'yMargin',
+            'links',
+            'layout',
+            'layout_keyValue',
+            'fitWidth',
+            'viewType',
+            'title-custom',
+            'panX',
+            'panY',
+            'viewScale',
+        ];
         const collection = CollectionDockingView.Instance;
         query = query.toLowerCase();
 
@@ -211,10 +246,15 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             const docs = DocListCast(collection.rootDoc[Doc.LayoutFieldKey(collection.rootDoc)]);
             const docIDs: String[] = [];
             SearchBox.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
-                const dtype = StrCast(doc.type, "string") as DocumentType;
+                const dtype = StrCast(doc.type, 'string') as DocumentType;
                 if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth > 0) {
                     const hlights = new Set<string>();
-                    SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+                    SearchBox.documentKeys(doc).forEach(
+                        key =>
+                            Field.toString(doc[key] as Field)
+                                .toLowerCase()
+                                .includes(query) && hlights.add(key)
+                    );
                     blockedKeys.forEach(key => hlights.delete(key));
 
                     if (Array.from(hlights.keys()).length > 0) {
@@ -250,18 +290,16 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                 this._results.forEach((_, linkedDoc) => {
                     this._linkedDocsIn.get(linkedDoc)?.add(doc);
                 });
-            }
-            else {
+            } else {
                 const linkedDocSet = new Set<Doc>();
 
-                Doc.GetProto(doc)[DirectLinksSym].forEach((link) => {
+                Doc.GetProto(doc)[DirectLinksSym].forEach(link => {
                     const d1 = link?.anchor1 as Doc;
                     const d2 = link?.anchor2 as Doc;
                     if (doc === d1 && this._results.has(d2)) {
                         linkedDocSet.add(d2);
                         this._linkedDocsIn.get(d2)?.add(doc);
-                    }
-                    else if (doc === d2 && this._results.has(d1)) {
+                    } else if (doc === d2 && this._results.has(d1)) {
                         linkedDocSet.add(d1);
                         this._linkedDocsIn.get(d1)?.add(doc);
                     }
@@ -289,8 +327,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         this._results.forEach((_, doc) => {
             let nextPageRank = pageRankFromAll;
 
-            this._linkedDocsIn.get(doc)?.forEach((linkedDoc) => {
-                nextPageRank += DAMPENING_FACTOR * (this._pageRanks.get(linkedDoc) ?? 0) / (this._linkedDocsOut.get(linkedDoc)?.size ?? 1);
+            this._linkedDocsIn.get(doc)?.forEach(linkedDoc => {
+                nextPageRank += (DAMPENING_FACTOR * (this._pageRanks.get(linkedDoc) ?? 0)) / (this._linkedDocsOut.get(linkedDoc)?.size ?? 1);
             });
 
             nextPageRanks.set(doc, nextPageRank);
@@ -328,7 +366,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      */
     static documentKeys(doc: Doc) {
         const keys: { [key: string]: boolean } = {};
-        Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false));
+        Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)));
         return Array.from(Object.keys(keys));
     }
 
@@ -342,15 +380,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         const query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
-        Array.from(this._results.keys()).forEach(doc =>
-            DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true)
-        );
+        Array.from(this._results.keys()).forEach(doc => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true));
         this._results.clear();
 
         if (query) {
             this.searchCollection(query);
         }
-    }
+    };
 
     /**
      * This method resets the search by iterating through each result and removing all
@@ -372,7 +408,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      */
     selectElement = async (doc: Doc, finishFunc: () => void) => {
         await DocumentManager.Instance.jumpToDocument(doc, true, undefined, [], undefined, undefined, undefined, finishFunc);
-    }
+    };
 
     /**
      * This method returns a JSX list of the options in the select drop-down menu, which
@@ -380,9 +416,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      */
     @computed
     public get selectOptions() {
-        const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"];
+        const selectValues = ['all', 'rtf', 'image', 'pdf', 'web', 'video', 'audio', 'collection'];
 
-        return selectValues.map(value => <option key={value} value={value}>{SearchBox.formatType(value)}</option>);
+        return selectValues.map(value => (
+            <option key={value} value={value}>
+                {SearchBox.formatType(value)}
+            </option>
+        ));
     }
 
     /**
@@ -393,39 +433,37 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         const isLinkSearch: boolean = this.props.linkSearch;
 
-        const sortedResults = Array.from(this._results.entries()).sort((a, b) => (this._pageRanks.get(b[0]) ?? 0) - (this._pageRanks.get(a[0]) ?? 0));  // sorted by page rank
+        const sortedResults = Array.from(this._results.entries()).sort((a, b) => (this._pageRanks.get(b[0]) ?? 0) - (this._pageRanks.get(a[0]) ?? 0)); // sorted by page rank
 
         const resultsJSX = Array();
 
-        sortedResults.forEach((result) => {
-            var className = "searchBox-results-scroll-view-result";
+        sortedResults.forEach(result => {
+            var className = 'searchBox-results-scroll-view-result';
 
             if (this._selectedResult === result[0]) {
-                className += " searchBox-results-scroll-view-result-selected";
+                className += ' searchBox-results-scroll-view-result-selected';
             }
 
             const formattedType = SearchBox.formatType(StrCast(result[0].type));
             const title = result[0].title;
 
-            if (this._docTypeString === "all" || this._docTypeString === result[0].type) {
+            if (this._docTypeString === 'all' || this._docTypeString === result[0].type) {
                 validResults++;
                 resultsJSX.push(
-                    <Tooltip key={result[0][Id]} placement={"right"} title={<><div className="dash-tooltip">{title}</div></>}>
-                        <div onClick={isLinkSearch ?
-                            () => this.makeLink(result[0]) :
-                            e => {
-                                this.onResultClick(result[0]);
-                                e.stopPropagation();
-                            }} className={className}>
-                            <div className="searchBox-result-title">
-                                {title}
-                            </div>
-                            <div className="searchBox-result-type">
-                                {formattedType}
-                            </div>
-                            <div className="searchBox-result-keys">
-                                {result[1].join(", ")}
-                            </div>
+                    <Tooltip key={result[0][Id]} placement={'right'} title={<div className="dash-tooltip">{title as string}</div>}>
+                        <div
+                            onClick={
+                                isLinkSearch
+                                    ? () => this.makeLink(result[0])
+                                    : e => {
+                                          this.onResultClick(result[0]);
+                                          e.stopPropagation();
+                                      }
+                            }
+                            className={className}>
+                            <div className="searchBox-result-title">{title as string}</div>
+                            <div className="searchBox-result-type">{formattedType}</div>
+                            <div className="searchBox-result-keys">{result[1].join(', ')}</div>
                         </div>
                     </Tooltip>
                 );
@@ -433,22 +471,31 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         });
 
         return (
-            <div style={{ pointerEvents: "all" }} className="searchBox-container">
-                <div className="searchBox-bar" >
-                    {isLinkSearch ? (null) : <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
-                        {this.selectOptions}
-                    </select>}
-                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} onKeyPress={e => e.key === "Enter" ? this.submitSearch() : null} type="text" placeholder="Search..." id="search-input" className="searchBox-input" style={{ width: isLinkSearch ? "100%" : undefined, borderRadius: isLinkSearch ? "5px" : undefined }} ref={this._inputRef} />
-                </div >
+            <div style={{ pointerEvents: 'all' }} className="searchBox-container">
+                <div className="searchBox-bar">
+                    {isLinkSearch ? null : (
+                        <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
+                            {this.selectOptions}
+                        </select>
+                    )}
+                    <input
+                        defaultValue={''}
+                        autoComplete="off"
+                        onChange={this.onInputChange}
+                        onKeyPress={e => (e.key === 'Enter' ? this.submitSearch() : null)}
+                        type="text"
+                        placeholder="Search..."
+                        id="search-input"
+                        className="searchBox-input"
+                        style={{ width: isLinkSearch ? '100%' : undefined, borderRadius: isLinkSearch ? '5px' : undefined }}
+                        ref={this._inputRef}
+                    />
+                </div>
                 <div className="searchBox-results-container">
-                    <div className="searchBox-results-count">
-                        {`${validResults}` + " result" + (validResults === 1 ? "" : "s")}
-                    </div>
-                    <div className="searchBox-results-scroll-view">
-                        {resultsJSX}
-                    </div>
+                    <div className="searchBox-results-count">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+                    <div className="searchBox-results-scroll-view">{resultsJSX}</div>
                 </div>
-            </div >
+            </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index bf06faeb9..e1360553a 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -1,52 +1,236 @@
-
 import { library } from '@fortawesome/fontawesome-svg-core';
 import {
-    faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
-    faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
-    faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
-    faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
-    faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
-    faThumbtack, faTree, faTv, faBook, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faHome, faLongArrowAltLeft, faBars, faTh, faChevronLeft,
-    faAlignRight, faAlignLeft
+    faTasks,
+    faReply,
+    faQuoteLeft,
+    faHandPointLeft,
+    faFolderOpen,
+    faAngleDoubleLeft,
+    faExternalLinkSquareAlt,
+    faMobile,
+    faThLarge,
+    faWindowClose,
+    faEdit,
+    faTrashAlt,
+    faPalette,
+    faAngleRight,
+    faBell,
+    faTrash,
+    faCamera,
+    faExpand,
+    faCaretDown,
+    faCaretLeft,
+    faCaretRight,
+    faCaretSquareDown,
+    faCaretSquareRight,
+    faArrowsAltH,
+    faPlus,
+    faMinus,
+    faTerminal,
+    faToggleOn,
+    faFile as fileSolid,
+    faExternalLinkAlt,
+    faLocationArrow,
+    faSearch,
+    faFileDownload,
+    faStop,
+    faCalculator,
+    faWindowMaximize,
+    faAddressCard,
+    faQuestionCircle,
+    faArrowLeft,
+    faArrowRight,
+    faArrowDown,
+    faArrowUp,
+    faBolt,
+    faBullseye,
+    faCaretUp,
+    faCat,
+    faCheck,
+    faChevronRight,
+    faClipboard,
+    faClone,
+    faCloudUploadAlt,
+    faCommentAlt,
+    faCompressArrowsAlt,
+    faCut,
+    faEllipsisV,
+    faEraser,
+    faExclamation,
+    faFileAlt,
+    faFileAudio,
+    faFilePdf,
+    faFilm,
+    faFilter,
+    faFont,
+    faGlobeAsia,
+    faHighlighter,
+    faLongArrowAltRight,
+    faMicrophone,
+    faMousePointer,
+    faMusic,
+    faObjectGroup,
+    faPause,
+    faPen,
+    faPenNib,
+    faPhone,
+    faPlay,
+    faPortrait,
+    faRedoAlt,
+    faStamp,
+    faStickyNote,
+    faThumbtack,
+    faTree,
+    faTv,
+    faBook,
+    faUndoAlt,
+    faVideo,
+    faAsterisk,
+    faBrain,
+    faImage,
+    faPaintBrush,
+    faTimes,
+    faEye,
+    faHome,
+    faLongArrowAltLeft,
+    faBars,
+    faTh,
+    faChevronLeft,
+    faAlignRight,
+    faAlignLeft,
 } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import * as React from "react";
+import * as React from 'react';
 import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
-import { DocumentType } from "../client/documents/DocumentTypes";
+import { DocumentType } from '../client/documents/DocumentTypes';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { SettingsManager, ColorScheme } from '../client/util/SettingsManager';
 import { Transform } from '../client/util/Transform';
-import { UndoManager } from "../client/util/UndoManager";
+import { UndoManager } from '../client/util/UndoManager';
 import { TabDocView } from '../client/views/collections/TabDocView';
-import { CollectionViewType } from "../client/views/collections/CollectionView";
-import { GestureOverlay } from "../client/views/GestureOverlay";
-import { AudioBox } from "../client/views/nodes/AudioBox";
+import { CollectionViewType } from '../client/views/collections/CollectionView';
+import { GestureOverlay } from '../client/views/GestureOverlay';
+import { AudioBox } from '../client/views/nodes/AudioBox';
 import { DocumentView } from '../client/views/nodes/DocumentView';
-import { RichTextMenu } from "../client/views/nodes/formattedText/RichTextMenu";
-import { RadialMenu } from "../client/views/nodes/RadialMenu";
+import { RichTextMenu } from '../client/views/nodes/formattedText/RichTextMenu';
+import { RadialMenu } from '../client/views/nodes/RadialMenu';
 import { Doc, DocListCast } from '../fields/Doc';
 import { InkTool } from '../fields/InkField';
-import { List } from "../fields/List";
-import { ScriptField } from "../fields/ScriptField";
-import { Cast, FieldValue } from '../fields/Types';
+import { List } from '../fields/List';
+import { ScriptField } from '../fields/ScriptField';
+import { Cast, FieldValue, StrCast } from '../fields/Types';
 import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero } from '../Utils';
-import { AudioUpload } from "./AudioUpload";
-import { Uploader } from "./ImageUpload";
-import "./AudioUpload.scss";
-import "./ImageUpload.scss";
-import "./MobileInterface.scss";
-
-library.add(...[faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
-    faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
-    faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
-    faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
-    faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
-    faThumbtack, faTree, faTv, faUndoAlt, faBook, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faHome, faLongArrowAltLeft, faBars, faTh, faChevronLeft,
-    faAlignLeft, faAlignRight].map(m => m as any));
-
+import { AudioUpload } from './AudioUpload';
+import { Uploader } from './ImageUpload';
+import './AudioUpload.scss';
+import './ImageUpload.scss';
+import './MobileInterface.scss';
+
+library.add(
+    ...[
+        faTasks,
+        faReply,
+        faQuoteLeft,
+        faHandPointLeft,
+        faFolderOpen,
+        faAngleDoubleLeft,
+        faExternalLinkSquareAlt,
+        faMobile,
+        faThLarge,
+        faWindowClose,
+        faEdit,
+        faTrashAlt,
+        faPalette,
+        faAngleRight,
+        faBell,
+        faTrash,
+        faCamera,
+        faExpand,
+        faCaretDown,
+        faCaretLeft,
+        faCaretRight,
+        faCaretSquareDown,
+        faCaretSquareRight,
+        faArrowsAltH,
+        faPlus,
+        faMinus,
+        faTerminal,
+        faToggleOn,
+        fileSolid,
+        faExternalLinkAlt,
+        faLocationArrow,
+        faSearch,
+        faFileDownload,
+        faStop,
+        faCalculator,
+        faWindowMaximize,
+        faAddressCard,
+        faQuestionCircle,
+        faArrowLeft,
+        faArrowRight,
+        faArrowDown,
+        faArrowUp,
+        faBolt,
+        faBullseye,
+        faCaretUp,
+        faCat,
+        faCheck,
+        faChevronRight,
+        faClipboard,
+        faClone,
+        faCloudUploadAlt,
+        faCommentAlt,
+        faCompressArrowsAlt,
+        faCut,
+        faEllipsisV,
+        faEraser,
+        faExclamation,
+        faFileAlt,
+        faFileAudio,
+        faFilePdf,
+        faFilm,
+        faFilter,
+        faFont,
+        faGlobeAsia,
+        faHighlighter,
+        faLongArrowAltRight,
+        faMicrophone,
+        faMousePointer,
+        faMusic,
+        faObjectGroup,
+        faPause,
+        faPen,
+        faPenNib,
+        faPhone,
+        faPlay,
+        faPortrait,
+        faRedoAlt,
+        faStamp,
+        faStickyNote,
+        faThumbtack,
+        faTree,
+        faTv,
+        faUndoAlt,
+        faBook,
+        faVideo,
+        faAsterisk,
+        faBrain,
+        faImage,
+        faPaintBrush,
+        faTimes,
+        faEye,
+        faHome,
+        faLongArrowAltLeft,
+        faBars,
+        faTh,
+        faChevronLeft,
+        faAlignLeft,
+        faAlignRight,
+    ].map(m => m as any)
+);
 
 @observer
 export class MobileInterface extends React.Component {
@@ -64,36 +248,38 @@ export class MobileInterface extends React.Component {
     @observable private _homeDoc: Doc = this._mainDoc; // home menu as a document
     @observable private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
 
-    @computed private get mainContainer() { return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : CurrentUserUtils.GuestMobile; }
+    @computed private get mainContainer() {
+        return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : CurrentUserUtils.GuestMobile;
+    }
 
     constructor(props: Readonly<{}>) {
         super(props);
-        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), "myDashboards"); // to access documents in Dash Web
+        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), 'myDashboards'); // to access documents in Dash Web
         MobileInterface.Instance = this;
     }
 
     @action
     componentDidMount = () => {
         // if the home menu is in list view -> adjust the menu toggle appropriately
-        this._menuListView = this._homeDoc._viewType === "stacking" ? true : false;
+        this._menuListView = this._homeDoc._viewType === 'stacking' ? true : false;
         CurrentUserUtils.ActiveTool = InkTool.None; // ink should intially be set to none
         Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
         AudioBox.Enabled = true;
 
         // remove double click to avoid mobile zoom in
-        document.removeEventListener("dblclick", this.onReactDoubleClick);
-        document.addEventListener("dblclick", this.onReactDoubleClick);
-    }
+        document.removeEventListener('dblclick', this.onReactDoubleClick);
+        document.addEventListener('dblclick', this.onReactDoubleClick);
+    };
 
     @action
     componentWillUnmount = () => {
         document.removeEventListener('dblclick', this.onReactDoubleClick);
-    }
+    };
 
     // Prevent zooming in when double tapping the screen
     onReactDoubleClick = (e: MouseEvent) => {
         e.stopPropagation();
-    }
+    };
 
     // Switch the mobile view to the given doc
     @action
@@ -110,7 +296,7 @@ export class MobileInterface extends React.Component {
         // Ensures that switching to home is not registed
         UndoManager.undoStack.length = 0;
         UndoManager.redoStack.length = 0;
-    }
+    };
 
     // For toggling the hamburger menu
     @action
@@ -120,29 +306,29 @@ export class MobileInterface extends React.Component {
         if (this._ink) {
             this.onSwitchInking();
         }
-    }
+    };
     /**
      * Method called when 'Library' button is pressed on the home screen
      */
     switchToLibrary = async () => {
         this.switchCurrentView(this._library);
-        runInAction(() => this._homeMenu = false);
+        runInAction(() => (this._homeMenu = false));
         this.toggleSidebar();
-    }
+    };
 
     /**
      * Back method for navigating through items
      */
     @action
     back = () => {
-        const header = document.getElementById("header") as HTMLElement;
+        const header = document.getElementById('header') as HTMLElement;
         const doc = Cast(this._parents.pop(), Doc) as Doc; // Parent document
         // Case 1: Parent document is 'dashboards'
-        if (doc === Cast(this._library, Doc) as Doc) {
+        if (doc === (Cast(this._library, Doc) as Doc)) {
             this.dashboards = null;
             this.switchCurrentView(this._library);
             // Case 2: Parent document is the 'home' menu (root node)
-        } else if (doc === Cast(this._homeDoc, Doc) as Doc) {
+        } else if (doc === (Cast(this._homeDoc, Doc) as Doc)) {
             this._homeMenu = true;
             this._parents = [];
             this.dashboards = null;
@@ -155,7 +341,7 @@ export class MobileInterface extends React.Component {
             header.textContent = String(doc.title);
         }
         this._ink = false; // turns ink off
-    }
+    };
 
     /**
      * Return 'Home", which implies returning to 'Home' menu buttons
@@ -171,7 +357,7 @@ export class MobileInterface extends React.Component {
         if (this._sidebarActive) {
             this.toggleSidebar();
         }
-    }
+    };
 
     /**
      * Return to primary Dashboard in library (Dashboards Doc)
@@ -182,7 +368,7 @@ export class MobileInterface extends React.Component {
         this.switchCurrentView(this._library);
         this._homeMenu = false;
         this.dashboards = null;
-    }
+    };
 
     /**
      * Note: window.innerWidth and window.screen.width compute different values.
@@ -190,14 +376,14 @@ export class MobileInterface extends React.Component {
      * display resolution which computes differently.
      */
     returnWidth = () => window.innerWidth; //The windows width
-    returnHeight = () => (window.innerHeight - 300); //Calculating the windows height (-300 to account for topbar)
-    whitebackground = () => "white";
+    returnHeight = () => window.innerHeight - 300; //Calculating the windows height (-300 to account for topbar)
+    whitebackground = () => 'white';
     /**
      * DocumentView for graphic display of all documents
      */
     @computed get displayDashboards() {
-        return !this.mainContainer ? (null) :
-            <div style={{ position: "relative", top: '198px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}>
+        return !this.mainContainer ? null : (
+            <div style={{ position: 'relative', top: '198px', height: `calc(100% - 350px)`, width: '100%', left: '0%' }}>
                 <DocumentView
                     Document={this.mainContainer}
                     DataDoc={undefined}
@@ -223,7 +409,8 @@ export class MobileInterface extends React.Component {
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                 />
-            </div>;
+            </div>
+        );
     }
 
     /**
@@ -233,20 +420,19 @@ export class MobileInterface extends React.Component {
      */
     handleClick = async (doc: Doc) => {
         runInAction(() => {
-            if (doc.type !== "collection" && this._sidebarActive) {
+            if (doc.type !== 'collection' && this._sidebarActive) {
                 this._parents.push(this._activeDoc);
                 this.switchCurrentView(doc);
                 this._homeMenu = false;
                 this.toggleSidebar();
-            }
-            else {
+            } else {
                 this._parents.push(this._activeDoc);
                 this.switchCurrentView(doc);
                 this._homeMenu = false;
                 this.dashboards = doc;
             }
         });
-    }
+    };
 
     /**
      * Called when an item in the library is clicked and should
@@ -260,30 +446,31 @@ export class MobileInterface extends React.Component {
         this._homeMenu = false;
         this.dashboards = doc;
         this.toggleSidebar();
-    }
+    };
 
     // Renders the graphical pathbar
     renderPathbar = () => {
         const docPath = [...this._parents, this._activeDoc];
-        const items = docPath.map((doc: Doc, index: any) =>
+        const items = docPath.map((doc: Doc, index: any) => (
             <div className="pathbarItem" key={index}>
-                {index === 0 ? (null) : <FontAwesomeIcon key="icon" className="pathIcon" icon="angle-right" size="lg" />}
-                <div className="pathbarText"
-                    style={{ backgroundColor: this._homeMenu || doc === this._activeDoc ? "rgb(119,17,37)" : undefined }}
-                    onClick={() => this.handlePathClick(doc, index)}>{doc.title}
+                {index === 0 ? null : <FontAwesomeIcon key="icon" className="pathIcon" icon="angle-right" size="lg" />}
+                <div className="pathbarText" style={{ backgroundColor: this._homeMenu || doc === this._activeDoc ? 'rgb(119,17,37)' : undefined }} onClick={() => this.handlePathClick(doc, index)}>
+                    {StrCast(doc.title)}
                 </div>
-            </div>);
-        return (<div className="pathbar">
-            <div className="scrollmenu">
-                {items}
             </div>
-            {!this._parents.length ? (null) :
-                <div className="back" >
-                    <FontAwesomeIcon onClick={this.back} icon={"chevron-left"} color="white" size={"2x"} />
-                </div>}
-            <div className="hidePath" />
-        </div>);
-    }
+        ));
+        return (
+            <div className="pathbar">
+                <div className="scrollmenu">{items}</div>
+                {!this._parents.length ? null : (
+                    <div className="back">
+                        <FontAwesomeIcon onClick={this.back} icon={'chevron-left'} color="white" size={'2x'} />
+                    </div>
+                )}
+                <div className="hidePath" />
+            </div>
+        );
+    };
 
     // Handles when user clicks on a document in the pathbar
     @action
@@ -300,7 +487,7 @@ export class MobileInterface extends React.Component {
             this.switchCurrentView(doc);
             this._parents.length = index;
         }
-    }
+    };
 
     // Renders the contents of the menu and sidebar
     @computed get renderDefaultContent() {
@@ -309,7 +496,9 @@ export class MobileInterface extends React.Component {
                 <div>
                     <div className="navbar">
                         <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} />
-                        <div className="header" id="header">{this._homeDoc.title}</div>
+                        <div className="header" id="header">
+                            {StrCast(this._homeDoc.title)}
+                        </div>
                         <div className="cover" id="cover" onClick={e => e.stopPropagation()}></div>
                         <div className="toggle-btn" id="menuButton" onClick={this.toggleSidebar}>
                             <span></span>
@@ -328,14 +517,17 @@ export class MobileInterface extends React.Component {
         }
         // returns a list of navbar buttons as 'buttons'
         const buttons = DocListCast(dashboards.data).map((doc: Doc, index: any) => {
-            if (doc.type !== "ink") {
+            if (doc.type !== 'ink') {
                 return (
-                    <div
-                        className="item"
-                        key={index}>
-                        <div className="item-title" onClick={() => this.handleClick(doc)}> {doc.title} </div>
-                        <div className="item-type" onClick={() => this.handleClick(doc)}>{doc.type}</div>
-                        <FontAwesomeIcon onClick={() => this.handleClick(doc)} className="right" icon="angle-right" size="lg" style={{ display: `${doc.type === "collection" ? "block" : "none"}` }} />
+                    <div className="item" key={index}>
+                        <div className="item-title" onClick={() => this.handleClick(doc)}>
+                            {' '}
+                            {doc.title as string}{' '}
+                        </div>
+                        <div className="item-type" onClick={() => this.handleClick(doc)}>
+                            {doc.type as string}
+                        </div>
+                        <FontAwesomeIcon onClick={() => this.handleClick(doc)} className="right" icon="angle-right" size="lg" style={{ display: `${doc.type === 'collection' ? 'block' : 'none'}` }} />
                         <FontAwesomeIcon className="open" onClick={() => this.openFromSidebar(doc)} icon="external-link-alt" size="lg" />
                     </div>
                 );
@@ -346,43 +538,39 @@ export class MobileInterface extends React.Component {
             <div>
                 <div className="navbar">
                     <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} />
-                    <div className="header" id="header">{this._sidebarActive ? "library" : this._activeDoc.title}</div>
-                    <div className={`toggle-btn ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}>
+                    <div className="header" id="header">
+                        {this._sidebarActive ? 'library' : (this._activeDoc.title as string)}
+                    </div>
+                    <div className={`toggle-btn ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}>
                         <span></span>
                         <span></span>
                         <span></span>
                     </div>
-                    <div className={`background ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}></div>
+                    <div className={`background ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}></div>
                 </div>
                 {this.renderPathbar()}
-                <div className={`sidebar ${this._sidebarActive ? "active" : ""}`}>
+                <div className={`sidebar ${this._sidebarActive ? 'active' : ''}`}>
                     <div className="sidebarButtons">
-                        {this.dashboards ?
+                        {this.dashboards ? (
                             <>
                                 {buttons}
-                                <div
-                                    className="item" key="home"
-                                    onClick={this.returnMain}
-                                    style={{ opacity: 0.7 }}>
+                                <div className="item" key="home" onClick={this.returnMain} style={{ opacity: 0.7 }}>
                                     <FontAwesomeIcon className="right" icon="angle-double-left" size="lg" />
                                     <div className="item-type">Return to dashboards</div>
                                 </div>
-                            </> :
+                            </>
+                        ) : (
                             <>
                                 {buttons}
-                                <div
-                                    className="item"
-                                    style={{ opacity: 0.7 }}
-                                    onClick={() => this.createNewDashboard()}>
+                                <div className="item" style={{ opacity: 0.7 }} onClick={() => this.createNewDashboard()}>
                                     <FontAwesomeIcon className="right" icon="plus" size="lg" />
                                     <div className="item-type">Create New Dashboard</div>
                                 </div>
                             </>
-                        }
+                        )}
                     </div>
                 </div>
-                <div className={`blanket ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}>
-                </div>
+                <div className={`blanket ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}></div>
             </div>
         );
     }
@@ -397,26 +585,26 @@ export class MobileInterface extends React.Component {
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
-            title: "Collection " + dashboardCount,
+            title: 'Collection ' + dashboardCount,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row');
 
         const toggleTheme = ScriptField.MakeScript(`self.colorScheme = self.colorScheme ? undefined: ${ColorScheme.Dark}}`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
         const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`);
         dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneDashboard!]);
-        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Dashboard Layout"]);
+        dashboardDoc.contextMenuLabels = new List<string>(['Toggle Theme Colors', 'Toggle Comic Mode', 'New Dashboard Layout']);
 
-        Doc.AddDocToList(scens, "data", dashboardDoc);
-    }
+        Doc.AddDocToList(scens, 'data', dashboardDoc);
+    };
 
     // Button for switching between pen and ink mode
     @action
     onSwitchInking = () => {
-        const button = document.getElementById("inkButton") as HTMLElement;
-        button.style.backgroundColor = this._ink ? "white" : "black";
-        button.style.color = this._ink ? "black" : "white";
+        const button = document.getElementById('inkButton') as HTMLElement;
+        button.style.backgroundColor = this._ink ? 'white' : 'black';
+        button.style.color = this._ink ? 'black' : 'white';
 
         if (!this._ink) {
             CurrentUserUtils.ActiveTool = InkTool.Pen;
@@ -425,23 +613,20 @@ export class MobileInterface extends React.Component {
             CurrentUserUtils.ActiveTool = InkTool.None;
             this._ink = false;
         }
-    }
+    };
 
     // The static ink menu that appears at the top
     @computed get inkMenu() {
-        return this._activeDoc._viewType !== CollectionViewType.Docking || !this._ink ? (null) :
-            <div className="colorSelector">
-                {/* <CollectionFreeFormViewChrome /> */}
-            </div>;
+        return this._activeDoc._viewType !== CollectionViewType.Docking || !this._ink ? null : <div className="colorSelector">{/* <CollectionFreeFormViewChrome /> */}</div>;
     }
 
     // DocButton that uses UndoManager and handles the opacity change if CanUndo is true
     @computed get undo() {
-        if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== "WORKSPACES") {
+        if (this.mainContainer && this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== 'WORKSPACES') {
             return (
-                <div className="docButton"
-                    style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanUndo() ? "1" : "0.4", }}
+                <div
+                    className="docButton"
+                    style={{ backgroundColor: 'black', color: 'white', fontSize: '60', opacity: UndoManager.CanUndo() ? '1' : '0.4' }}
                     id="undoButton"
                     title="undo"
                     onClick={(e: React.MouseEvent) => {
@@ -449,17 +634,18 @@ export class MobileInterface extends React.Component {
                         e.stopPropagation();
                     }}>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="undo-alt" />
-                </div>);
-        } else return (null);
+                </div>
+            );
+        } else return null;
     }
 
     // DocButton that uses UndoManager and handles the opacity change if CanRedo is true
     @computed get redo() {
-        if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== "WORKSPACES") {
+        if (this.mainContainer && this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== 'WORKSPACES') {
             return (
-                <div className="docButton"
-                    style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanRedo() ? "1" : "0.4", }}
+                <div
+                    className="docButton"
+                    style={{ backgroundColor: 'black', color: 'white', fontSize: '60', opacity: UndoManager.CanRedo() ? '1' : '0.4' }}
                     id="undoButton"
                     title="redo"
                     onClick={(e: React.MouseEvent) => {
@@ -467,78 +653,81 @@ export class MobileInterface extends React.Component {
                         e.stopPropagation();
                     }}>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="redo-alt" />
-                </div>);
-        } else return (null);
+                </div>
+            );
+        } else return null;
     }
 
     // DocButton for switching into ink mode
     @computed get drawInk() {
-        return !this.mainContainer || this._activeDoc._viewType !== CollectionViewType.Docking ? (null) :
-            <div className="docButton"
-                id="inkButton"
-                title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"}
-                onClick={this.onSwitchInking}>
+        return !this.mainContainer || this._activeDoc._viewType !== CollectionViewType.Docking ? null : (
+            <div className="docButton" id="inkButton" title={Doc.isDocPinned(this._activeDoc) ? 'Pen on' : 'Pen off'} onClick={this.onSwitchInking}>
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="pen-nib" />
-            </div>;
+            </div>
+        );
     }
 
     // DocButton: Button that appears on the bottom of the screen to initiate image upload
     @computed get uploadImageButton() {
-        if (this._activeDoc.type === DocumentType.COL && this._activeDoc !== this._homeDoc && this._activeDoc._viewType !== CollectionViewType.Docking && this._activeDoc.title !== "WORKSPACES") {
-            return <div className="docButton"
-                id="imageButton"
-                title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"}
-                onClick={this.toggleUpload}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" />
-            </div>;
-        } else return (null);
+        if (this._activeDoc.type === DocumentType.COL && this._activeDoc !== this._homeDoc && this._activeDoc._viewType !== CollectionViewType.Docking && this._activeDoc.title !== 'WORKSPACES') {
+            return (
+                <div className="docButton" id="imageButton" title={Doc.isDocPinned(this._activeDoc) ? 'Pen on' : 'Pen off'} onClick={this.toggleUpload}>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" />
+                </div>
+            );
+        } else return null;
     }
 
     // DocButton to download images on the mobile
     @computed get downloadDocument() {
-        if (this._activeDoc.type === "image" || this._activeDoc.type === "pdf" || this._activeDoc.type === "video") {
-            return <div className="docButton"
-                title={"Download Image"}
-                style={{ backgroundColor: "white", color: "black" }}
-                onClick={e => window.open(this._activeDoc["data-path"]?.toString())}> {/*  daa-path holds the url */}
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="download" />
-            </div>;
-        } else return (null);
+        if (this._activeDoc.type === 'image' || this._activeDoc.type === 'pdf' || this._activeDoc.type === 'video') {
+            return (
+                <div className="docButton" title={'Download Image'} style={{ backgroundColor: 'white', color: 'black' }} onClick={e => window.open(this._activeDoc['data-path']?.toString())}>
+                    {' '}
+                    {/*  daa-path holds the url */}
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="download" />
+                </div>
+            );
+        } else return null;
     }
 
     // DocButton for pinning images to presentation
     @computed get pinToPresentation() {
         // Only making button available if it is an image
-        if (!(this._activeDoc.type === "collection" || this._activeDoc.type === "presentation")) {
+        if (!(this._activeDoc.type === 'collection' || this._activeDoc.type === 'presentation')) {
             const isPinned = this._activeDoc && Doc.isDocPinned(this._activeDoc);
-            return <div className="docButton"
-                title={Doc.isDocPinned(this._activeDoc) ? "Unpin from presentation" : "Pin to presentation"}
-                style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }}
-                onClick={e => TabDocView.PinDoc(this._activeDoc)}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
-            </div>;
-        } else return (null);
+            return (
+                <div
+                    className="docButton"
+                    title={Doc.isDocPinned(this._activeDoc) ? 'Unpin from presentation' : 'Pin to presentation'}
+                    style={{ backgroundColor: isPinned ? 'black' : 'white', color: isPinned ? 'white' : 'black' }}
+                    onClick={e => TabDocView.PinDoc(this._activeDoc)}>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
+                </div>
+            );
+        } else return null;
     }
 
     // Buttons for switching the menu between large and small icons
     @computed get switchMenuView() {
-        return this._activeDoc.title !== this._homeDoc.title ? (null) :
+        return this._activeDoc.title !== this._homeDoc.title ? null : (
             <div className="homeSwitch">
-                <div className={`list ${!this._menuListView ? "active" : ""}`} onClick={this.changeToIconView}>
+                <div className={`list ${!this._menuListView ? 'active' : ''}`} onClick={this.changeToIconView}>
                     <FontAwesomeIcon size="sm" icon="th-large" />
                 </div>
-                <div className={`list ${this._menuListView ? "active" : ""}`} onClick={this.changeToListView}>
+                <div className={`list ${this._menuListView ? 'active' : ''}`} onClick={this.changeToListView}>
                     <FontAwesomeIcon size="sm" icon="bars" />
                 </div>
-            </div>;
+            </div>
+        );
     }
 
     // Logic for switching the menu into the icons
     @action
     changeToIconView = () => {
-        if (this._homeDoc._viewType = "stacking") {
+        if ((this._homeDoc._viewType = 'stacking')) {
             this._menuListView = false;
-            this._homeDoc._viewType = "masonry";
+            this._homeDoc._viewType = 'masonry';
             this._homeDoc.columnWidth = 300;
             this._homeDoc._columnWidth = 300;
             const menuButtons = DocListCast(this._homeDoc.data);
@@ -551,13 +740,13 @@ export class MobileInterface extends React.Component {
                 doc._nativeWidth = 400;
             });
         }
-    }
+    };
 
     // Logic for switching the menu into the stacking view
     @action
     changeToListView = () => {
-        if (this._homeDoc._viewType = "masonry") {
-            this._homeDoc._viewType = "stacking";
+        if ((this._homeDoc._viewType = 'masonry')) {
+            this._homeDoc._viewType = 'stacking';
             this._menuListView = true;
             const menuButtons = DocListCast(this._homeDoc.data);
             menuButtons.map(doc => {
@@ -568,7 +757,7 @@ export class MobileInterface extends React.Component {
                 doc._nativeWidth = 900;
             });
         }
-    }
+    };
 
     // For setting up the presentation document for the home menu
     @action
@@ -579,49 +768,48 @@ export class MobileInterface extends React.Component {
             this.switchCurrentView(presentation);
             this._homeMenu = false;
         }
-    }
+    };
 
     // For toggling image upload pop up
     @action
-    toggleUpload = () => this._imageUploadActive = !this._imageUploadActive
+    toggleUpload = () => (this._imageUploadActive = !this._imageUploadActive);
 
     // For toggling audio record and dictate pop up
     @action
-    toggleAudio = () => this._audioUploadActive = !this._audioUploadActive
+    toggleAudio = () => (this._audioUploadActive = !this._audioUploadActive);
 
     // Button for toggling the upload pop up in a collection
     @action
     toggleUploadInCollection = () => {
-        const button = document.getElementById("imageButton") as HTMLElement;
-        button.style.backgroundColor = this._imageUploadActive ? "white" : "black";
-        button.style.color = this._imageUploadActive ? "black" : "white";
+        const button = document.getElementById('imageButton') as HTMLElement;
+        button.style.backgroundColor = this._imageUploadActive ? 'white' : 'black';
+        button.style.color = this._imageUploadActive ? 'black' : 'white';
 
         this._imageUploadActive = !this._imageUploadActive;
-    }
+    };
 
     // For closing the image upload pop up
     @action
     closeUpload = () => {
         this._imageUploadActive = false;
-    }
+    };
 
     // Returns the image upload pop up
     @computed get uploadImage() {
-        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.SharingDoc(), Doc) as Doc;
+        const doc = !this._homeMenu ? this._activeDoc : (Cast(Doc.SharingDoc(), Doc) as Doc);
         return <Uploader Document={doc} />;
     }
 
-    // Radial menu can only be used if it is a colleciton and it is not a homeDoc 
+    // Radial menu can only be used if it is a colleciton and it is not a homeDoc
     // (and cannot be used on Dashboard to avoid pin to presentation opening on right)
     @computed get displayRadialMenu() {
-        return this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc._viewType !== CollectionViewType.Docking ? <RadialMenu /> : (null);
+        return this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc._viewType !== CollectionViewType.Docking ? <RadialMenu /> : null;
     }
 
     onDragOver = (e: React.DragEvent) => {
         e.preventDefault();
         e.stopPropagation();
-    }
+    };
 
     /**
      * MENU BUTTON
@@ -633,22 +821,22 @@ export class MobileInterface extends React.Component {
         const mobileUpload = Cast(Doc.SharingDoc(), Doc) as Doc;
         this.switchCurrentView(mobileUpload);
         this._homeMenu = false;
-    }
+    };
 
     render() {
         return (
             <div className="mobileInterface-container" onDragOver={this.onDragOver}>
                 <SettingsManager />
-                <div className={`image-upload ${this._imageUploadActive ? "active" : ""}`}>
-                    {this.uploadImage}
-                </div>
-                <div className={`audio-upload ${this._audioUploadActive ? "active" : ""}`}>
+                <div className={`image-upload ${this._imageUploadActive ? 'active' : ''}`}>{this.uploadImage}</div>
+                <div className={`audio-upload ${this._audioUploadActive ? 'active' : ''}`}>
                     <AudioUpload />
                 </div>
                 {this.switchMenuView}
                 {this.inkMenu}
                 <GestureOverlay>
-                    <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
+                    <div style={{ display: 'none' }}>
+                        <RichTextMenu key="rich" />
+                    </div>
                     <div className="docButtonContainer">
                         {this.pinToPresentation}
                         {this.downloadDocument}
@@ -666,21 +854,31 @@ export class MobileInterface extends React.Component {
     }
 }
 
-
 //Global functions for mobile menu
-ScriptingGlobals.add(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
-    "opens the library to navigate through dashboards on Dash Mobile");
-ScriptingGlobals.add(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
-    "opens the upload files menu for Dash Mobile");
-ScriptingGlobals.add(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },
-    "opens the mobile uploads collection on Dash Mobile");
-ScriptingGlobals.add(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); },
-    "opens the record and dictate menu on Dash Mobile");
-ScriptingGlobals.add(function switchToMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); },
-    "opens the presentation on Dash Mobile");
-ScriptingGlobals.add(function openMobileSettings() { return SettingsManager.Instance.open(); },
-    "opens settings on Dash Mobile");
+ScriptingGlobals.add(function switchToMobileLibrary() {
+    return MobileInterface.Instance.switchToLibrary();
+}, 'opens the library to navigate through dashboards on Dash Mobile');
+ScriptingGlobals.add(function openMobileUploads() {
+    return MobileInterface.Instance.toggleUpload();
+}, 'opens the upload files menu for Dash Mobile');
+ScriptingGlobals.add(function switchToMobileUploadCollection() {
+    return MobileInterface.Instance.switchToMobileUploads();
+}, 'opens the mobile uploads collection on Dash Mobile');
+ScriptingGlobals.add(function openMobileAudio() {
+    return MobileInterface.Instance.toggleAudio();
+}, 'opens the record and dictate menu on Dash Mobile');
+ScriptingGlobals.add(function switchToMobilePresentation() {
+    return MobileInterface.Instance.setupDefaultPresentation();
+}, 'opens the presentation on Dash Mobile');
+ScriptingGlobals.add(function openMobileSettings() {
+    return SettingsManager.Instance.open();
+}, 'opens settings on Dash Mobile');
 
 // Other global functions for mobile
-ScriptingGlobals.add(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
-    "changes the active document displayed on the Dash Mobile", "(doc: any)");
\ No newline at end of file
+ScriptingGlobals.add(
+    function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) {
+        return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch);
+    },
+    'changes the active document displayed on the Dash Mobile',
+    '(doc: any)'
+);
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index b736f0d35..3622be4c5 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -1,14 +1,14 @@
-import { default as User, DashUserModel } from "./DashUserModel";
-import { Request, Response, NextFunction } from "express";
-import * as passport from "passport";
-import { IVerifyOptions } from "passport-local";
-import "./Passport";
-import flash = require("express-flash");
+import { default as User, DashUserModel } from './DashUserModel';
+import { Request, Response, NextFunction } from 'express';
+import * as passport from 'passport';
+import { IVerifyOptions } from 'passport-local';
+import './Passport';
+import flash = require('express-flash');
 import * as async from 'async';
 import * as nodemailer from 'nodemailer';
-import c = require("crypto");
-import { Utils } from "../../Utils";
-import { MailOptions } from "nodemailer/lib/stream-transport";
+import c = require('crypto');
+import { emptyFunction, Utils } from '../../Utils';
+import { MailOptions } from 'nodemailer/lib/stream-transport';
 
 /**
  * GET /signup
@@ -17,10 +17,10 @@ import { MailOptions } from "nodemailer/lib/stream-transport";
  */
 export let getSignup = (req: Request, res: Response) => {
     if (req.user) {
-        return res.redirect("/home");
+        return res.redirect('/home');
     }
-    res.render("signup.pug", {
-        title: "Sign Up",
+    res.render('signup.pug', {
+        title: 'Sign Up',
         user: req.user,
     });
 };
@@ -30,15 +30,15 @@ export let getSignup = (req: Request, res: Response) => {
  * Create a new local account.
  */
 export let postSignup = (req: Request, res: Response, next: NextFunction) => {
-    req.assert("email", "Email is not valid").isEmail();
-    req.assert("password", "Password must be at least 4 characters long").len({ min: 4 });
-    req.assert("confirmPassword", "Passwords do not match").equals(req.body.password);
-    req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
+    req.assert('email', 'Email is not valid').isEmail();
+    req.assert('password', 'Password must be at least 4 characters long').len({ min: 4 });
+    req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
+    req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
 
     const errors = req.validationErrors();
 
     if (errors) {
-        return res.redirect("/signup");
+        return res.redirect('/signup');
     }
 
     const email = req.body.email as String;
@@ -50,25 +50,30 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
         userDocumentId: Utils.GenerateGuid(),
         sharingDocumentId: Utils.GenerateGuid(),
         linkDatabaseId: Utils.GenerateGuid(),
-        cacheDocumentIds: ""
+        cacheDocumentIds: '',
     } as Partial<DashUserModel>;
 
     const user = new User(model);
 
     User.findOne({ email }, (err: any, existingUser: any) => {
-        if (err) { return next(err); }
+        if (err) {
+            return next(err);
+        }
         if (existingUser) {
-            return res.redirect("/login");
+            return res.redirect('/login');
         }
         user.save((err: any) => {
-            if (err) { return next(err); }
-            req.logIn(user, (err) => {
-                if (err) { return next(err); }
+            if (err) {
+                return next(err);
+            }
+            req.logIn(user, err => {
+                if (err) {
+                    return next(err);
+                }
                 tryRedirectToTarget(req, res);
             });
         });
     });
-
 };
 
 const tryRedirectToTarget = (req: Request, res: Response) => {
@@ -76,11 +81,10 @@ const tryRedirectToTarget = (req: Request, res: Response) => {
     if (req.session && target) {
         res.redirect(target);
     } else {
-        res.redirect("/home");
+        res.redirect('/home');
     }
 };
 
-
 /**
  * GET /login
  * Login page.
@@ -88,11 +92,11 @@ const tryRedirectToTarget = (req: Request, res: Response) => {
 export let getLogin = (req: Request, res: Response) => {
     if (req.user) {
         //req.session.target = undefined;
-        return res.redirect("/home");
+        return res.redirect('/home');
     }
-    res.render("login.pug", {
-        title: "Log In",
-        user: req.user
+    res.render('login.pug', {
+        title: 'Log In',
+        user: req.user,
     });
 };
 
@@ -102,24 +106,30 @@ export let getLogin = (req: Request, res: Response) => {
  * On failure, redirect to signup page
  */
 export let postLogin = (req: Request, res: Response, next: NextFunction) => {
-    req.assert("email", "Email is not valid").isEmail();
-    req.assert("password", "Password cannot be blank").notEmpty();
-    req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
+    req.assert('email', 'Email is not valid').isEmail();
+    req.assert('password', 'Password cannot be blank').notEmpty();
+    req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
 
     const errors = req.validationErrors();
 
     if (errors) {
-        req.flash("errors", "Unable to login at this time. Please try again.");
-        return res.redirect("/signup");
+        req.flash('errors', 'Unable to login at this time. Please try again.');
+        return res.redirect('/signup');
     }
 
-    passport.authenticate("local", (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
-        if (err) { next(err); return; }
+    passport.authenticate('local', (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
+        if (err) {
+            next(err);
+            return;
+        }
         if (!user) {
-            return res.redirect("/signup");
+            return res.redirect('/signup');
         }
-        req.logIn(user, (err) => {
-            if (err) { next(err); return; }
+        req.logIn(user, err => {
+            if (err) {
+                next(err);
+                return;
+            }
             tryRedirectToTarget(req, res);
         });
     })(req, res, next);
@@ -131,140 +141,154 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
  * and destroys the user's current session.
  */
 export let getLogout = (req: Request, res: Response) => {
-    req.logout();
+    req.logout(emptyFunction);
     const sess = req.session;
     if (sess) {
-        sess.destroy((err) => { if (err) { console.log(err); } });
+        sess.destroy(err => {
+            if (err) {
+                console.log(err);
+            }
+        });
     }
-    res.redirect("/login");
+    res.redirect('/login');
 };
 
 export let getForgot = function (req: Request, res: Response) {
-    res.render("forgot.pug", {
-        title: "Recover Password",
+    res.render('forgot.pug', {
+        title: 'Recover Password',
         user: req.user,
     });
 };
 
 export let postForgot = function (req: Request, res: Response, next: NextFunction) {
     const email = req.body.email;
-    async.waterfall([
-        function (done: any) {
-            c.randomBytes(20, function (err: any, buffer: Buffer) {
-                if (err) {
-                    done(null);
-                    return;
-                }
-                done(null, buffer.toString('hex'));
-            });
-        },
-        function (token: string, done: any) {
-            User.findOne({ email }, function (err: any, user: DashUserModel) {
-                if (!user) {
-                    // NO ACCOUNT WITH SUBMITTED EMAIL
-                    res.redirect("/forgotPassword");
-                    return;
-                }
-                user.passwordResetToken = token;
-                user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR
-                user.save(function (err: any) {
-                    done(null, token, user);
+    async.waterfall(
+        [
+            function (done: any) {
+                c.randomBytes(20, function (err: any, buffer: Buffer) {
+                    if (err) {
+                        done(null);
+                        return;
+                    }
+                    done(null, buffer.toString('hex'));
                 });
-            });
-        },
-        function (token: Uint16Array, user: DashUserModel, done: any) {
-            const smtpTransport = nodemailer.createTransport({
-                service: 'Gmail',
-                auth: {
-                    user: 'browndashptc@gmail.com',
-                    pass: 'TsarNicholas#2'
-                }
-            });
-            const mailOptions = {
-                to: user.email,
-                from: 'browndashptc@gmail.com',
-                subject: 'Dash Password Reset',
-                text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
-                    'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
-                    'http://' + req.headers.host + '/resetPassword/' + token + '\n\n' +
-                    'If you did not request this, please ignore this email and your password will remain unchanged.\n'
-            } as MailOptions;
-            smtpTransport.sendMail(mailOptions, function (err: Error | null) {
-                // req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
-                done(null, err, 'done');
-            });
+            },
+            function (token: string, done: any) {
+                User.findOne({ email }, function (err: any, user: DashUserModel) {
+                    if (!user) {
+                        // NO ACCOUNT WITH SUBMITTED EMAIL
+                        res.redirect('/forgotPassword');
+                        return;
+                    }
+                    user.passwordResetToken = token;
+                    user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR
+                    user.save(function (err: any) {
+                        done(null, token, user);
+                    });
+                });
+            },
+            function (token: Uint16Array, user: DashUserModel, done: any) {
+                const smtpTransport = nodemailer.createTransport({
+                    service: 'Gmail',
+                    auth: {
+                        user: 'browndashptc@gmail.com',
+                        pass: 'TsarNicholas#2',
+                    },
+                });
+                const mailOptions = {
+                    to: user.email,
+                    from: 'browndashptc@gmail.com',
+                    subject: 'Dash Password Reset',
+                    text:
+                        'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
+                        'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
+                        'http://' +
+                        req.headers.host +
+                        '/resetPassword/' +
+                        token +
+                        '\n\n' +
+                        'If you did not request this, please ignore this email and your password will remain unchanged.\n',
+                } as MailOptions;
+                smtpTransport.sendMail(mailOptions, function (err: Error | null) {
+                    // req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
+                    done(null, err, 'done');
+                });
+            },
+        ],
+        function (err) {
+            if (err) return next(err);
+            res.redirect('/forgotPassword');
         }
-    ], function (err) {
-        if (err) return next(err);
-        res.redirect("/forgotPassword");
-    });
+    );
 };
 
 export let getReset = function (req: Request, res: Response) {
     User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
         if (!user || err) {
-            return res.redirect("/forgotPassword");
+            return res.redirect('/forgotPassword');
         }
-        res.render("reset.pug", {
-            title: "Reset Password",
+        res.render('reset.pug', {
+            title: 'Reset Password',
             user: req.user,
         });
     });
 };
 
 export let postReset = function (req: Request, res: Response) {
-    async.waterfall([
-        function (done: any) {
-            User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
-                if (!user || err) {
-                    return res.redirect('back');
-                }
+    async.waterfall(
+        [
+            function (done: any) {
+                User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
+                    if (!user || err) {
+                        return res.redirect('back');
+                    }
 
-                req.assert("password", "Password must be at least 4 characters long").len({ min: 4 });
-                req.assert("confirmPassword", "Passwords do not match").equals(req.body.password);
+                    req.assert('password', 'Password must be at least 4 characters long').len({ min: 4 });
+                    req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
 
-                if (req.validationErrors()) {
-                    return res.redirect('back');
-                }
+                    if (req.validationErrors()) {
+                        return res.redirect('back');
+                    }
 
-                user.password = req.body.password;
-                user.passwordResetToken = undefined;
-                user.passwordResetExpires = undefined;
+                    user.password = req.body.password;
+                    user.passwordResetToken = undefined;
+                    user.passwordResetExpires = undefined;
 
-                user.save(function (err) {
-                    if (err) {
-                        res.redirect("/login");
-                        return;
-                    }
-                    req.logIn(user, function (err) {
+                    user.save(function (err) {
                         if (err) {
+                            res.redirect('/login');
                             return;
                         }
+                        req.logIn(user, function (err) {
+                            if (err) {
+                                return;
+                            }
+                        });
+                        done(null, user);
                     });
-                    done(null, user);
                 });
-            });
-        },
-        function (user: DashUserModel, done: any) {
-            const smtpTransport = nodemailer.createTransport({
-                service: 'Gmail',
-                auth: {
-                    user: 'browndashptc@gmail.com',
-                    pass: 'TsarNicholas#2'
-                }
-            });
-            const mailOptions = {
-                to: user.email,
-                from: 'browndashptc@gmail.com',
-                subject: 'Your password has been changed',
-                text: 'Hello,\n\n' +
-                    'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
-            } as MailOptions;
-            smtpTransport.sendMail(mailOptions, function (err) {
-                done(null, err);
-            });
+            },
+            function (user: DashUserModel, done: any) {
+                const smtpTransport = nodemailer.createTransport({
+                    service: 'Gmail',
+                    auth: {
+                        user: 'browndashptc@gmail.com',
+                        pass: 'TsarNicholas#2',
+                    },
+                });
+                const mailOptions = {
+                    to: user.email,
+                    from: 'browndashptc@gmail.com',
+                    subject: 'Your password has been changed',
+                    text: 'Hello,\n\n' + 'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n',
+                } as MailOptions;
+                smtpTransport.sendMail(mailOptions, function (err) {
+                    done(null, err);
+                });
+            },
+        ],
+        function (err) {
+            res.redirect('/login');
         }
-    ], function (err) {
-        res.redirect("/login");
-    });
-};
\ No newline at end of file
+    );
+};
-- 
cgit v1.2.3-70-g09d2

