From e9d5dbeef2bf1dab9dfb863d970b70b3074e3d0a Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@michaels-mbp-21.devices.brown.edu>
Date: Wed, 2 Nov 2022 14:56:20 -0400
Subject: add basic heartbeat functinality througha ping/pong api cycle

---
 src/client/views/Main.tsx | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'src/client/views/Main.tsx')

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index f327f3184..a1d221af2 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -14,6 +14,7 @@ import { TrackMovements } from '../util/TrackMovements';
 import { CollectionView } from './collections/CollectionView';
 import { MainView } from './MainView';
 import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
+import { PingManager } from '../util/PingManager';
 dotenv.config();
 
 AssignAllExtensions();
@@ -44,6 +45,7 @@ AssignAllExtensions();
         new LinkManager();
         new TrackMovements();
         new ReplayMovements();
+        new PingManager();
         ReactDOM.createRoot(document.getElementById('root')!).render(<MainView />);
     }, 0);
 })();
-- 
cgit v1.2.3-70-g09d2


From 50876028b994044f2a9426a4efb85a363f0d7168 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 4 May 2023 10:46:20 -0400
Subject: properties view + sharing manager are consistent, and all text
 updates properly

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/SharingManager.scss                |   5 +
 src/client/util/SharingManager.tsx                 | 120 ++++-----------------
 src/client/views/DocComponent.tsx                  |  10 +-
 src/client/views/DocumentDecorations.scss          |   3 +-
 src/client/views/DocumentDecorations.tsx           |   6 +-
 src/client/views/Main.tsx                          |   2 +-
 src/client/views/MarqueeAnnotator.tsx              |   4 +-
 src/client/views/PropertiesView.scss               |   4 +-
 src/client/views/PropertiesView.tsx                |  93 ++++++----------
 .../views/nodes/formattedText/FormattedTextBox.tsx |   9 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |  12 +--
 src/fields/Doc.ts                                  |   3 -
 src/fields/util.ts                                 |   6 +-
 14 files changed, 81 insertions(+), 198 deletions(-)

(limited to 'src/client/views/Main.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f5510d7e1..9c67f1f37 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -506,7 +506,7 @@ export namespace Docs {
                         description: '',
                         showCaption: 'description',
                         backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
-                        _removeDropProperties: new List(['isLinkButton']),
+                        // _removeDropsties: new List(['isLinkButton']),
                     },
                 },
             ],
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index f47c48805..3cab77816 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -23,6 +23,11 @@
             z-index: 999;
         }
 
+        .share-title {
+            display: inline-flex;
+            gap: 5px;
+        }
+
         .share-container {
             .share-setup {
                 display: flex;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 5e61f6d3c..40a282db0 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -25,6 +25,7 @@ import { GroupMemberView } from './GroupMemberView';
 import { LinkManager } from './LinkManager';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
+import { IconButton, Size } from 'browndash-components';
 
 export interface User {
     email: string;
@@ -159,27 +160,14 @@ export class SharingManager extends React.Component<{}> {
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
         
-        // var docs: Doc[] = [];
-        // const dashboardList = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-        // const dashboard = dashboardList[0]
-        // docs.push(dashboard!)
-        // const tabs = DocListCast(dashboard?.data)
-        // tabs.forEach(tab => {
-        //     docs.push(tab)
-        //     var newDocs = DocListCast(tab.data)
-        //     newDocs.forEach(newDoc => 
-        //         docs.push(newDoc))
-        // })
-
-        // if (!docs){
-        //     return null;
-        // }
+        // setting the same acl for a docs within the doc being shared
+        var childDocs = DocListCast(target.data)
+        childDocs.map(doc => {
+            this.setInternalSharing(recipient, permission, doc)
+        })
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
-        //var docs: Doc[] = [];
-        docs.push(targetDoc!)
-
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
@@ -191,7 +179,6 @@ export class SharingManager extends React.Component<{}> {
                 } else {
                     if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
                 }
-                doc = targetDoc
 
                 distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
                 this.setDashboardBackground(doc, permission as SharingPermissions);
@@ -211,19 +198,15 @@ export class SharingManager extends React.Component<{}> {
         const key = normalizeEmail(StrCast(group.title));
         const acl = `acl-${key}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
-
-        var docs: Doc[] = [];
-        const dashboardList = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
-        const dashboard = dashboardList[0]
-        const tabs = DocListCast(dashboard?.data)
-        tabs.forEach(tab => {
-            var newDocs = DocListCast(tab.data)
-            newDocs.forEach(newDoc => docs.push(newDoc))
-        })
         
-        if (!docs){
-            return null;
-        }
+        // setting the same acl for a docs within the doc being shared
+        var childDocs = DocListCast(target.data)
+        childDocs.map(doc => {
+            this.setInternalGroupSharing(group, permission, doc)
+        })
+
+        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 => (this.layoutDocAcls ? doc : doc[DataSym]))
@@ -292,6 +275,7 @@ export class SharingManager extends React.Component<{}> {
             const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
+                this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data))
                 if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
                 this.setDashboardBackground(doc, permission as SharingPermissions);
             });
@@ -364,30 +348,15 @@ export class SharingManager extends React.Component<{}> {
     //         return;
     //     }
     //     targetDoc["acl-" + PublicKey] = permission;
-    // }
-
-    private get sharingUrl() {
-        if (!this.targetDoc) {
-            return undefined;
-        }
-        const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
-        return `${baseUrl}?sharing=true`;
-    }
+    // }s
 
     /**
      * Copies the Public sharing url to the user's clipboard.
      */
     private copyURL = (e: any) => {
-        Utils.CopyText(this.sharingUrl!)
+        Utils.CopyText(Utils.shareUrl(this.targetDoc![Id]))
     }
 
-    // copy = action(() => {
-    //     if (this.sharingUrl) {
-    //         Utils.CopyText(this.sharingUrl);
-    //         this.copied = true;
-    //     }
-    // });
-
     /**
      * Returns the SharingPermissions (Admin, Can Edit etc) access that's used to share
      */
@@ -396,7 +365,6 @@ export class SharingManager extends React.Component<{}> {
         if (!uniform) dropdownValues.unshift('-multiple-');
         if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return dropdownValues
-            .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)))
             .map(permission => (
                 <option key={permission} value={permission}>
                     {permission}
@@ -546,19 +514,6 @@ export class SharingManager extends React.Component<{}> {
         // users in common between all docs
         const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
 
-        // including all users on dashboard
-        // SharingManager.Instance.users.forEach(eachUser => {
-        //     var userOnDashboard = true;
-        //     if(Doc.ActiveDashboard){
-        //         if(Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]==undefined){
-        //             userOnDashboard = false;
-        //         }
-        //     }
-        //     if (userOnDashboard && !commonKeys.includes(`acl-${normalizeEmail(eachUser.user.email)}`)){
-        //         users.push(eachUser.user);
-        //     }
-        // });
-
 
         // the list of users shared with
         const userListContents: (JSX.Element | null)[] = users
@@ -570,19 +525,6 @@ export class SharingManager extends React.Component<{}> {
                 const dashboard = Doc.ActiveDashboard
                 var docToUse = dashboard
 
-                // const tabs = DocListCast(dashboard?.data)
-                // tabs.forEach(tab => {
-                //     if (tab.title == targetDoc.title){
-                //         docToUse = tab
-                //     }
-                //     var newdocsList = DocListCast(tab.data)
-                //     newdocsList.forEach(newDoc => {
-                //         if (newDoc.title == targetDoc.title){
-                //             docToUse = newDoc
-                //         }
-                //     })
-                // })
-
                 docToUse = Doc.GetProto(this.targetDoc!)
 
                 const userKey = `acl-${normalizeEmail(user.email)}`;
@@ -641,22 +583,6 @@ export class SharingManager extends React.Component<{}> {
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
                     <div className={'padding'} >{StrCast(group.title)}</div>
-                    <div>
-                        {StrCast(group.title)==='Public' ? (
-                            <div title={"Copy Public URL"} onClick={this.copyURL}>
-                                {/* <IconButton
-                                    size={Size.SMALL}
-                                    isCircle={true}
-                                    hoverStyle="gray"
-                                    onClick={() => this.copyURL}
-                                    icon={<FontAwesomeIcon icon="copy" />}
-                                /> */}
-                                <FontAwesomeIcon icon={'copy'} size={'sm'} />
-                            </div>
-                        ) : (
-                            <div/>
-                        )}
-                    </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' }} />
@@ -679,19 +605,16 @@ export class SharingManager extends React.Component<{}> {
             <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'}>
+                    <p className="share-title">
                         <b>Share </b>
                         {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}
+                        {/* <FontAwesomeIcon title={"Copy Public URL"} icon={'copy'} size={'sm'} onClick={this.copyURL}/> */}
+                        <IconButton size={Size.SMALL} isCircle={true} tooltip='Copy Public URL' hoverStyle="gray" onClick={this.copyURL}icon={<FontAwesomeIcon icon="copy" />}/>
                     </p>
                     <div className={'close-button'} onClick={this.close}>
                         <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
-                    {/* {this.linkVisible ?
-                        <div>
-                            {this.sharingUrl}
-                        </div> :
-                        (null)} */}
-                    {
+                    { admin ? 
                         <div className="share-container">
                             <div className="share-setup">
                                 <Select
@@ -730,6 +653,7 @@ export class SharingManager extends React.Component<{}> {
                                 )}
                             </div>
                         </div>
+                        : <br></br>
                     }
                     <div className="main-container">
                         <div className={'individual-container'}>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 0b92fd864..5a1dae8db 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -222,8 +222,12 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                     if (effectiveAcl === AclAugment) {
                         added.map(doc => {
-                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
                             doc.context = this.props.Document;
+                            const contextDoc = Cast(doc.context, Doc, null)
+                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) { 
+                                if (contextDoc) inheritParentAcls(contextDoc, doc);
+                                else if (Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
+                            };
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         });
@@ -237,7 +241,9 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                                 doc.context = this.props.Document;
                                 if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
 
-                                Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
+                                const contextDoc = Cast(doc.context, Doc, null)
+                                if (contextDoc) inheritParentAcls(contextDoc, doc);
+                                else if (Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
                             });
                         const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                         if (annoDocs instanceof List) annoDocs.push(...added);
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ce5378e69..32bcc872a 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -237,8 +237,7 @@ $resizeHandler: 8px;
             color: rgb(71, 71, 71);
             border-color: rgb(71, 71, 71);
         }
-        .documentDecorations-shareEdit,
-        .documentDecorations-shareSelf-Edit{
+        .documentDecorations-shareEdit{
             width: calc(100% + 10px);
             background: rgb(235, 235, 145);
             color: rgb(75, 75, 5);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3e0a1f7a4..b769056d8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -32,6 +32,7 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
 import { SharingManager } from '../util/SharingManager';
+import { DocServer } from '../DocServer';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -754,9 +755,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             case ("Edit"):
                 shareSymbolIcon = "⬢ ";
                 break;
-            case ("Self-Edit"):
-                shareSymbolIcon = "⬢ ";
-                break;
             case ("Augment"):
                 shareSymbolIcon = "⬟ ";
                 break;
@@ -794,7 +792,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 // const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
                 // return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
                 const effectiveAcl = GetEffectiveAcl(Doc.GetProto(seldocview.rootDoc))
-                return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && effectiveAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 6b18caed0..7da9dc603 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -26,7 +26,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure
     root.render(<FieldLoader />);
     window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
     const info = await CurrentUserUtils.loadCurrentUser();
-    if (info.email === 'guest') DocServer.Control.makeReadOnly();
+    // if (info.email === 'guest') DocServer.Control.makeReadOnly();
     await CurrentUserUtils.loadUserDocument(info.id);
     setTimeout(() => {
         document.getElementById('root')!.addEventListener(
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 30867a774..094c9cc61 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,6 +1,6 @@
 import { action, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, Opt } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast } from '../../fields/Types';
@@ -202,7 +202,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
-        const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
+        const annotationDoc = [AclAugment, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
         addAsAnnotation && !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
         return (annotationDoc as Doc) ?? undefined;
     };
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 50bf6ce74..71107a5f9 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -172,8 +172,7 @@
             border-radius: 6px;
             border: 1px solid rgb(71, 71, 71);
         }
-        & .propertiesView-shareDropDownEdit,
-        .propertiesView-shareDropDownSelf-Edit{
+        & .propertiesView-shareDropDownEdit{
             padding: 5px;
             background: rgb(235, 235, 145);
             color: rgb(75, 75, 5);
@@ -426,7 +425,6 @@
         background: inherit;
         border: none;
         background: inherit;
-        // width: 100%;
         text-align: justify; // for Edge
         text-align-last: end;
         &:hover {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index a6a5347ad..984c2dc04 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -369,7 +369,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     changePermissions = (e: any, user: string) => {
         const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym]));
         if (user=="Public"){
-            docs[0]['acl-' +user] = e.currentTarget.value as SharingPermissions;
+            // docs[0]['acl-' +user] = e.currentTarget.value as SharingPermissions;
+            SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
         }
         else{
 
@@ -385,12 +386,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (permission === '-multiple-') dropdownValues.unshift(permission);
         if (user !== 'Override') {
             dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
-            // dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         }
         return (
             <select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
                 {dropdownValues
-                    .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)))
+                    .filter(permission => !Doc.noviceMode || ![SharingPermissions.View].includes(permission as any))
                     .map(permission => (
                         <option className="propertiesView-permisssions-select" key={permission} value={permission}>
                             {' '}{permission}{' '}
@@ -464,9 +464,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             case ("Edit"):
                 dropDownText = "⬢ ";
                 break;
-            case ("Self-Edit"):
-                dropDownText = "⬢ ";
-                break;
             case ("Augment"):
                 dropDownText = "⬟ ";
                 break;
@@ -493,53 +490,36 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    /**
+     * Sorting algorithm to sort users.
+     */
+    sortUsers = (u1: String, u2: String) => {
+        return u1 > u2 ? -1 : u1 === u2 ? 0 : 1;
+    };
+
     /**
      * @returns the sharing and permissions panel.
      */
     @computed get sharingTable() {
-        // const docToUse = this.selectedDocumentView?.rootDoc;
-
-        // const dashboard = Doc.ActiveDashboard
-        // var docToUse = dashboard
-        // const tabs = DocListCast(dashboard?.data)
-        // tabs.forEach(tab => {
-        //     if (tab.title == this.selectedDoc?.title){
-        //         docToUse = tab
-        //     }
-        //     var newdocsList = DocListCast(tab.data)
-        //     newdocsList.forEach(newDoc => {
-        //         if (newDoc.title == this.selectedDoc?.title){
-        //             docToUse = newDoc
-        //         }
-        //     })
-        // })
 
         const docToUse = this.selectedDoc;
-
         if (!docToUse){
             return null;
         }
         // all selected docs
         const docs =
             SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? docToUse : docToUse?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]));
-
         const target = docs[0];
 
-        // tslint:disable-next-line: no-unnecessary-callback-wrapper
-        // const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
-        // const effectiveAcls = GetEffectiveAcl(docToUse)
         const effectiveAcls = GetEffectiveAcl(target)
-
-        // const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
         const showAdmin= effectiveAcls==AclAdmin || docToUse!['acl-'+normalizeEmail(Doc.CurrentUserEmail)]=='Owner';
 
         // users in common between all docs
-        const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
+        // const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
+        // const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
         const tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
-        const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
 
-        usersAdded.push(target.author);
         SharingManager.Instance.users.forEach(eachUser => {
             var userOnDashboard = true;
             var permission = StrCast(target[`acl-${normalizeEmail(eachUser.user.email)}`])
@@ -548,46 +528,33 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     userOnDashboard = false;
                 }
             }
-            if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email!='Public'){
-                tableEntries.unshift(this.sharingItem(eachUser.user.email, showAdmin, permission, false)); // adds each user
+            if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email!='Public' && eachUser.user.email!=target.author){
+                // tableEntries.unshift(this.sharingItem(eachUser.user.email, showAdmin, permission, false)); // adds each user
                 usersAdded.push(eachUser.user.email);
             }
         });
-
-        // commonKeys.forEach(user => {
-        //     const userEmail = user.slice(4)
-        //     var userOnDashboard = true;
-        //     if(Doc.ActiveDashboard){
-        //         if(Doc.ActiveDashboard[user]=='' || Doc.ActiveDashboard[user]==undefined){
-        //             userOnDashboard = false;
-        //         }
-        //     }
-        //     if (userOnDashboard && !usersAdded.includes(denormalizeEmail(userEmail)) && userEmail!='Public'){
-        //         tableEntries.unshift(this.sharingItem(denormalizeEmail(userEmail), showAdmin, StrCast(docToUse![`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
-        //         usersAdded.push(denormalizeEmail(userEmail));
-        //     }
-        // })
-
-        // current user
-        const userEmail = Doc.CurrentUserEmail
-        var userOnDashboard = true;
-        if(Doc.ActiveDashboard){
-            if(Doc.ActiveDashboard['acl-'+normalizeEmail(userEmail)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(userEmail)]==undefined){
-                userOnDashboard = false;
-            }
-        }
-        if (userOnDashboard && !usersAdded.includes(denormalizeEmail(userEmail)) && userEmail!='Public'){
-            tableEntries.unshift(this.sharingItem(denormalizeEmail(userEmail), showAdmin, StrCast(target[`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
-            usersAdded.push(denormalizeEmail(userEmail));
+        
+        usersAdded.sort(this.sortUsers)
+        usersAdded.map(userEmail => {
+            const permission = StrCast(target[`acl-${normalizeEmail(userEmail)}`])
+            tableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission, false)); // adds each user
+        })
+
+        // add current user
+        var userEmail = Doc.CurrentUserEmail
+        if (userEmail == 'guest') userEmail = 'Public';
+        if (!usersAdded.includes(userEmail) && userEmail!='Public' && userEmail!=target.author){
+            tableEntries.unshift(this.sharingItem(userEmail, showAdmin, StrCast(target[`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
+            usersAdded.push(userEmail);
         }
 
-        // if (ownerSame ) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false); // shift owner to top
-        tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false); // shift owner to top
+        // shift owner to top
+        tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
 
         return (
             <div > Sharing Mode
                 <div>{
-                    this.publicACLDropDown(true, StrCast(target['acl-Public']), false)}
+                    this.publicACLDropDown(showAdmin, StrCast(target['acl-Public']), false)}
                 </div>
             <div>  <br></br> Who has access to the Dashboard? </div>
             <div className="propertiesView-sharingTable">{
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0e4330fa5..c073c7468 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -11,7 +11,7 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
@@ -295,12 +295,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const curProto = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
             const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
             const json = JSON.stringify(state.toJSON());
-            // const effectiveAcl = GetEffectiveAcl(dataDoc);
             const effectiveAcl = GetEffectiveAcl(this.rootDoc);
             
             const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
             
-            if ([AclEdit, AclAdmin, AclSelfEdit, AclAugment].includes(effectiveAcl)) {
+            if ([AclEdit, AclAdmin, AclAugment].includes(effectiveAcl)) {
                 const accumTags = [] as string[];
                 state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
                     if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith('#')) {
@@ -1690,7 +1689,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         e.stopPropagation();
         for (var i = state.selection.from; i <= state.selection.to; i++) {
             const node = state.doc.resolve(i);
-            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
+            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) ) {
                 e.preventDefault();
             }
         }
@@ -1712,7 +1711,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 if (e.code == "Space"){
                     break;
                 }
-                [AclEdit, AclAugment, AclSelfEdit, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
+                [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
                     this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
         }
         this.startUndoTypingBatch();
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 68b0488a2..64cc7addc 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -4,7 +4,7 @@ import { Schema } from 'prosemirror-model';
 import { splitListItem, wrapInList } from 'prosemirror-schema-list';
 import { EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
 import { liftTarget } from 'prosemirror-transform';
-import { AclAugment, AclSelfEdit, Doc } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit} from '../../../../fields/Doc';
 import { GetEffectiveAcl } from '../../../../fields/util';
 import { Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
@@ -48,14 +48,6 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         switch (GetEffectiveAcl(props.DataDoc)) {
             case AclAugment:
                 return false;
-            case AclSelfEdit:
-                for (var i = state.selection.from; i < state.selection.to; i++) {
-                    const marks = state.doc.resolve(i)?.marks?.();
-                    if (marks?.some((mark: any) => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail)) {
-                        return false;
-                    }
-                }
-                break;
         }
         return true;
     };
@@ -259,7 +251,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     //Command to create a blank space
     bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        if (!canEdit(state)) return true;
+        if (GetEffectiveAcl(props.DataDoc)!=AclEdit && GetEffectiveAcl(props.DataDoc)!=AclAugment && GetEffectiveAcl(props.DataDoc)!=AclAdmin) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         dispatch(splitMetadata(marks, state.tr));
         return false;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5e2e40979..68279372a 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -102,7 +102,6 @@ export const AclUnset = Symbol('AclUnset');
 export const AclPrivate = Symbol('AclOwnerOnly');
 export const AclReadonly = Symbol('AclReadOnly');
 export const AclAugment = Symbol('AclAugment');
-export const AclSelfEdit = Symbol('AclSelfEdit');
 export const AclEdit = Symbol('AclEdit');
 export const AclAdmin = Symbol('AclAdmin');
 export const UpdatingFromServer = Symbol('UpdatingFromServer');
@@ -115,7 +114,6 @@ export enum aclLevel {
     unshared = 0,
     viewable = 1,
     augmentable = 2,
-    selfEditable = 2.5,
     editable = 3,
     admin = 4,
 }
@@ -124,7 +122,6 @@ export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermis
     [AclPrivate,  { level: aclLevel.unshared,     name: SharingPermissions.None }],
     [AclReadonly, { level: aclLevel.viewable,     name: SharingPermissions.View }],
     [AclAugment,  { level: aclLevel.augmentable,  name: SharingPermissions.Augment}],
-    [AclSelfEdit, { level: aclLevel.selfEditable, name: SharingPermissions.SelfEdit }],
     [AclEdit,     { level: aclLevel.editable,     name: SharingPermissions.Edit }],
     [AclAdmin,    { level: aclLevel.admin,        name: SharingPermissions.Admin }],
     [AclUnset,    { level: aclLevel.unset,        name: SharingPermissions.Unset }],
diff --git a/src/fields/util.ts b/src/fields/util.ts
index c1e1a7111..b0ad4db5d 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -14,7 +14,6 @@ import {
     aclLevel,
     AclPrivate,
     AclReadonly,
-    AclSelfEdit,
     AclSym,
     DataSym,
     Doc,
@@ -87,7 +86,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     const fromServer = target[UpdatingFromServer];
     const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
     const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
-    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
+    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin ) && !DocServer.Control.isReadOnly();
 
     if (writeToDoc) {
         if (value === undefined) {
@@ -171,7 +170,6 @@ export enum SharingPermissions {
     Unset = 'None',
     Admin = 'Admin',
     Edit = 'Edit',
-    SelfEdit = 'Self-Edit',
     Augment = 'Augment',
     View = 'View',
     None = 'Not-Shared',
@@ -330,7 +328,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
     let prop = in_prop;
     const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : getPropAcl(target, prop);
-    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true;
+    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin ) return true;
     // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
     if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true;
 
-- 
cgit v1.2.3-70-g09d2


From 241332557f10fa97718200821374755b68e4fe10 Mon Sep 17 00:00:00 2001
From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com>
Date: Tue, 11 Jul 2023 23:42:29 -0400
Subject: updated settings manager

- fixed some things with top toolbar and refactored files
- removed FontIconBadge
---
 .vscode/launch.json                                |  10 +
 package-lock.json                                  |  36 +-
 package.json                                       |   2 +-
 src/client/util/CaptureManager.scss                |  18 -
 src/client/util/CurrentUserUtils.ts                |  14 +-
 src/client/util/GroupManager.scss                  |   6 +-
 src/client/util/SettingsManager.scss               |  72 +-
 src/client/util/SettingsManager.tsx                | 338 ++++++----
 src/client/util/SharingManager.scss                |   6 +-
 src/client/views/DashboardView.tsx                 |   1 +
 src/client/views/Main.tsx                          |   1 +
 src/client/views/global/globalScripts.ts           | 407 +++++++++++
 src/client/views/nodes/button/ButtonInterface.ts   |  12 -
 src/client/views/nodes/button/FontIconBadge.scss   |  11 -
 src/client/views/nodes/button/FontIconBadge.tsx    |  37 -
 src/client/views/nodes/button/FontIconBox.tsx      | 751 +++------------------
 .../views/nodes/button/textButton/TextButton.tsx   |  30 -
 src/client/views/nodes/button/textButton/index.ts  |   1 -
 .../nodes/button/toggleButton/ToggleButton.tsx     |  34 -
 .../views/nodes/button/toggleButton/index.ts       |   1 -
 src/client/views/nodes/trails/PresBox.tsx          |   2 +-
 21 files changed, 748 insertions(+), 1042 deletions(-)
 create mode 100644 src/client/views/global/globalScripts.ts
 delete mode 100644 src/client/views/nodes/button/ButtonInterface.ts
 delete mode 100644 src/client/views/nodes/button/FontIconBadge.scss
 delete mode 100644 src/client/views/nodes/button/FontIconBadge.tsx
 delete mode 100644 src/client/views/nodes/button/textButton/TextButton.tsx
 delete mode 100644 src/client/views/nodes/button/textButton/index.ts
 delete mode 100644 src/client/views/nodes/button/toggleButton/ToggleButton.tsx
 delete mode 100644 src/client/views/nodes/button/toggleButton/index.ts

(limited to 'src/client/views/Main.tsx')

diff --git a/.vscode/launch.json b/.vscode/launch.json
index ce9f50f67..9ba624af9 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -14,6 +14,16 @@
             "webRoot": "${workspaceFolder}",
             "runtimeArgs": ["--experimental-modules"]
         },
+        {
+            "type": "chrome",
+            "request": "launch",
+            "name": "Launch Brave against localhost",
+            "sourceMaps": true,
+            "url": "http://localhost:1050/home",
+            "runtimeExecutable": "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
+            "webRoot": "${workspaceFolder}",
+            "runtimeArgs": ["--experimental-modules"]
+        },
         {
             "type": "chrome",
             "request": "launch",
diff --git a/package-lock.json b/package-lock.json
index bae345949..84c1e6219 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6195,9 +6195,9 @@
             }
         },
         "browndash-components": {
-            "version": "0.0.54",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.54.tgz",
-            "integrity": "sha512-cwwghwtCtdgbU1eWDH/Ptq03CsQObIjRfK5gcQJsQFS4MrY68ldvTQJnonsuFWJUBXuCIW8I/mBRE92yikYdng==",
+            "version": "0.0.62",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.62.tgz",
+            "integrity": "sha512-lhIuqOylZF7y4ZQXOdTim2r8Et61AaUROl5UYaooCObHvjJb7WIGpFv9B1RQjlbg8vUJ4jeM3e3qb++Cf6ajPg==",
             "requires": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
@@ -6314,9 +6314,9 @@
                     "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
                 },
                 "@mui/base": {
-                    "version": "5.0.0-beta.6",
-                    "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.6.tgz",
-                    "integrity": "sha512-jcHy6HwOX7KzRhRtL8nvIvUlxvLx2Fl6NMRCyUSQSvMTyfou9kndekz0H4HJaXvG1Y4WEifk23RYedOlrD1kEQ==",
+                    "version": "5.0.0-beta.7",
+                    "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.7.tgz",
+                    "integrity": "sha512-Pjbwm6gjiS96kOMF7E5fjEJsenc0tZBesrLQ4rrdi3eT/c/yhSWnPbCUkHSz8bnS0l3/VQ8bA+oERSGSV2PK6A==",
                     "requires": {
                         "@babel/runtime": "^7.22.5",
                         "@emotion/is-prop-valid": "^1.2.1",
@@ -6339,19 +6339,19 @@
                     }
                 },
                 "@mui/core-downloads-tracker": {
-                    "version": "5.13.7",
-                    "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.7.tgz",
-                    "integrity": "sha512-/suIo4WoeL/OyO3KUsFVpdOmKiSAr6NpWXmQ4WLSxwKrTiha1FJxM6vwAki5W/5kR9WnVLw5E8JC4oHHsutT8w=="
+                    "version": "5.14.0",
+                    "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.0.tgz",
+                    "integrity": "sha512-SYBOVCatVDUf/lbrLGah09bHhX5WfUXg7kSskfLILr6SvKRni0NLp0aonxQ0SMALVVK3Qwa6cW4CdWuwS0gC1w=="
                 },
                 "@mui/material": {
-                    "version": "5.13.7",
-                    "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.13.7.tgz",
-                    "integrity": "sha512-+n453jDDm88zZM3b5YK29nZ7gXY+s+rryH9ovDbhmfSkOlFtp+KSqbXy5cTaC/UlDqDM7sYYJGq8BmJov3v9Tg==",
+                    "version": "5.14.0",
+                    "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.0.tgz",
+                    "integrity": "sha512-HP7CP71NhMkui2HUIEKl2/JfuHMuoarSUWAKlNw6s17bl/Num9rN61EM6uUzc2A2zHjj/00A66GnvDnmixEJEw==",
                     "requires": {
                         "@babel/runtime": "^7.22.5",
-                        "@mui/base": "5.0.0-beta.6",
-                        "@mui/core-downloads-tracker": "^5.13.7",
-                        "@mui/system": "^5.13.7",
+                        "@mui/base": "5.0.0-beta.7",
+                        "@mui/core-downloads-tracker": "^5.14.0",
+                        "@mui/system": "^5.14.0",
                         "@mui/types": "^7.2.4",
                         "@mui/utils": "^5.13.7",
                         "@types/react-transition-group": "^4.4.6",
@@ -6414,9 +6414,9 @@
                     }
                 },
                 "@mui/system": {
-                    "version": "5.13.7",
-                    "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.7.tgz",
-                    "integrity": "sha512-7R2KdI6vr8KtnauEfg9e9xQmPk6Gg/1vGNiALYyhSI+cYztxN6WmlSqGX4bjWn/Sygp1TUE1jhFEgs7MWruhkQ==",
+                    "version": "5.14.0",
+                    "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.0.tgz",
+                    "integrity": "sha512-0HZGkX8miJbiNw+rjlZ9l0Cfkz1bSqfSHQH0EH9J+nx0aAm5cBleg9piOlLdCNIWGgecCqsw4x62erGrGjjcJg==",
                     "requires": {
                         "@babel/runtime": "^7.22.5",
                         "@mui/private-theming": "^5.13.7",
diff --git a/package.json b/package.json
index 8ad11054c..b137ddd67 100644
--- a/package.json
+++ b/package.json
@@ -177,7 +177,7 @@
         "body-parser": "^1.19.2",
         "bootstrap": "^4.6.1",
         "brotli": "^1.3.3",
-        "browndash-components": "0.0.54",
+        "browndash-components": "0.0.62",
         "browser-assert": "^1.2.1",
         "bson": "^4.6.1",
         "canvas": "^2.9.3",
diff --git a/src/client/util/CaptureManager.scss b/src/client/util/CaptureManager.scss
index a5024247e..11e31fe2e 100644
--- a/src/client/util/CaptureManager.scss
+++ b/src/client/util/CaptureManager.scss
@@ -155,21 +155,3 @@
     }
 }
 
-.close-button {
-    position: absolute;
-    top: 10;
-    right: 10;
-    background:transparent;
-    border-radius:100%;
-    width: 25px;
-    height: 25px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    transition: 0.2s;
-}
-
-.close-button:hover {
-    background: rgba(0,0,0,0.1);
-}
-
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 736aeceae..32229f22f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -523,7 +523,7 @@ export class CurrentUserUtils {
         const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
             _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _width: 30, _height: 30, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
-            title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
+            title: "New folder", color: Colors.BLACK, btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", isSystem: true
         };
         const newFolderScript = { onClick: newFolder};
         const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
@@ -557,7 +557,7 @@ export class CurrentUserUtils {
 
         const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
         const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, 
-            title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", isSystem: true,
+            title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, color: Colors.BLACK, buttonText: "Empty", icon: "trash", isSystem: true,
             toolTip: "Empty recently closed",};
         DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
 
@@ -611,8 +611,8 @@ export class CurrentUserUtils {
             title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move',
             childDontRegisterViews: true, linearView_IsExpanded: true, linearView_Expandable: true, ignoreClick: true
         };
-        reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
-        reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
+        reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "Redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
+        reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "Undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
         return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
     }
 
@@ -637,7 +637,7 @@ export class CurrentUserUtils {
         return [
             { title: "Font",     toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},
               btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
-            { title: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton, width: 75,           toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0 },
+            { title: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton,              toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 6 },
             { title: "Color",    toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'}},
             { title: "Highlight",toolTip:"Font highlight",        btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
             { title: "Bold",     toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
@@ -698,7 +698,7 @@ export class CurrentUserUtils {
             { title: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         expertMode: false,                                                                                                          ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'}},
             { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'},       scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform
             { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
-            { title: "Num",     icon:"",                toolTip: "Frame Number (click to toggle edit mode)",    btnType: ButtonType.TextButton,          expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
+            { title: "Num",     icon:"",               toolTip: "Frame Number (click to toggle edit mode)",    btnType: ButtonType.TextButton,          expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
             { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,         expertMode: true,  toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
             { title: "Text",    icon: "Text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   expertMode: false, toolType:DocumentType.RTF,            funcs: {                                                                             linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
             { title: "Ink",     icon: "Ink",           toolTip: "Ink functions",        subMenu: CurrentUserUtils.inkTools(),    expertMode: false, toolType:DocumentType.INK,            funcs: {                                                                             linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
@@ -809,7 +809,7 @@ export class CurrentUserUtils {
         const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
     
         const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
-            _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
+            _width: 30, _height: 30, color: Colors.BLACK, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
             buttonText: "Import", icon: "upload", isSystem: true };
         DocUtils.AssignDocField(myImports, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
         return myImports;
diff --git a/src/client/util/GroupManager.scss b/src/client/util/GroupManager.scss
index 9438bdd72..70fa88f6d 100644
--- a/src/client/util/GroupManager.scss
+++ b/src/client/util/GroupManager.scss
@@ -66,10 +66,8 @@
 
     .close-button {
         position: absolute;
-        right: 1em;
-        top: 1em;
-        cursor: pointer;
-        z-index: 999;
+        right: 2px;
+        top: 2px;
     }
 
     .group-heading {
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss
index 1289ca2b4..c41adfdd7 100644
--- a/src/client/util/SettingsManager.scss
+++ b/src/client/util/SettingsManager.scss
@@ -62,26 +62,31 @@
 .password-content {
     display: flex;
     flex-direction: column;
+    align-items: flex-start;
 
     .password-content-inputs {
-        width: 100;
-        // margin-bottom: 10px;
+        display: flex;
+        flex-direction: column;
+        width: 100%;
         font-size: 10px;
+        align-items: flex-start;
 
         .password-inputs {
             border: 1px solid rgb(160, 160, 160);
-            margin-bottom: 8px;
+            margin-bottom: 15px;
             width: 130;
-            color: black;
+            color: $medium-gray;
             border-radius: 5px;
             padding: 7px;
         }
     }
 
     .password-content-buttons {
-        //margin-left: 84px;
-        //width: 100;
-        padding: 7px;
+        display: flex;
+        gap: 5px;
+        justify-content: center;
+        align-items: flex-start;
+        flex-direction: column;
 
         .password-submit {
             //margin-left: 85px;
@@ -103,7 +108,6 @@
 
 .modes-content {
     display: flex;
-    margin-left: 10px;
     font-size: 12px;
 
     .modes-select {
@@ -172,12 +176,12 @@
 
 .appearances-content {
     display: flex;
-    margin-top: 4px;
     color: black;
     font-size: 10px;
 
     .preferences-color {
         display: flex;
+        align-items: center;
         margin-top: 2px;
 
         .preferences-color-text {
@@ -252,40 +256,18 @@
     display: flex;
     flex-direction: column;
 
-    button {
-        width: auto;
-        align-self: center;
-        background: #252b33;
-        margin-right: 15px;
-
-        //margin-top: 4px;
-
-        &:hover {
-            background: $medium-gray;
-        }
-    }
-
-    // .delete-button {
-    //     background: rgb(227, 86, 86);
-    // }
 
     .close-button {
         position: absolute;
-        right: 1em;
-        top: 1em;
-        cursor: pointer;
+        right: 2px;
+        top: 2px;
     }
 
-    // .logout-button {
-    //     right: 355;
-    //     position: absolute;
-    // }
 
     .settings-content {
         background: #e4e4e4;
-        //border-radius: 6px;
         padding: 10px;
-        //width: 560px;
+        width: 500px;
         flex: 1 1 auto;
     }
 
@@ -296,11 +278,8 @@
 
     .error-text {
         color: #c40233;
-        width: 300;
-        margin-left: -20;
         font-size: 10;
         margin-bottom: 4;
-        margin-top: -3;
     }
 
     .success-text {
@@ -370,8 +349,11 @@
 
     .settings-user {
         position: absolute;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex-direction: column;
         bottom: 10px;
-        text-align: center;
         left: 0;
         right: 0;
 
@@ -419,17 +401,25 @@
 
     .tab-column {
         flex: 0 0 50%;
+        flex-direction: column;
+        display: flex;
+        justify-content: flex-start;
+        align-items: flex-start;
 
         .tab-column-title {
-            color: black;
+            color: $dark-gray;
             font-size: 16px;
             font-weight: bold;
-            margin-bottom: 16px;
+            margin-bottom: 10px;
         }
 
         .tab-column-title,
         .tab-column-content {
-            padding-left: 16px;
+            display: flex;
+            justify-content: center;
+            align-items: flex-start;
+            flex-direction: column;
+            gap: 10px;
         }
     }
 }
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index f886ce2ca..09d6d034f 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -16,6 +16,9 @@ import { DragManager } from './DragManager';
 import { GroupManager } from './GroupManager';
 import './SettingsManager.scss';
 import { undoBatch } from './UndoManager';
+import { Button, ColorPicker, Dropdown, DropdownType, Group, Size, Toggle, ToggleType, Type } from 'browndash-components';
+import { BsGoogle } from 'react-icons/bs'
+import { FaFillDrip, FaPalette } from 'react-icons/fa'
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -73,14 +76,14 @@ export class SettingsManager extends React.Component<{}> {
         }
     };
 
-    @undoBatch selectUserMode = action((e: React.ChangeEvent) => (Doc.noviceMode = (e.currentTarget as any)?.value === 'Novice'));
+    @undoBatch selectUserMode = action((mode: string) => (Doc.noviceMode = mode === 'Novice'));
     @undoBatch changelayout_showTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().layout_showTitle = (e.currentTarget as any).value ? 'title' : undefined));
-    @undoBatch changeFontFamily = action((e: React.ChangeEvent) => (Doc.UserDoc().fontFamily = (e.currentTarget as any).value));
-    @undoBatch changeFontSize = action((e: React.ChangeEvent) => (Doc.UserDoc().fontSize = (e.currentTarget as any).value));
-    @undoBatch switchActiveBackgroundColor = action((color: ColorState) => (Doc.UserDoc().activeCollectionBackground = String(color.hex)));
-    @undoBatch switchUserColor = action((color: ColorState) => {
+    @undoBatch changeFontFamily = action((font: string) => (Doc.UserDoc().fontFamily = font));
+    @undoBatch changeFontSize = action((val: number) => (Doc.UserDoc().fontSize = val));
+    @undoBatch switchActiveBackgroundColor = action((color: string) => (Doc.UserDoc().activeCollectionBackground = color));
+    @undoBatch switchUserColor = action((color: string) => {
         Doc.SharingDoc().userColor = undefined;
-        Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex);
+        Doc.GetProto(Doc.SharingDoc()).userColor = color;
     });
     @undoBatch playgroundModeToggle = action(() => {
         this.playgroundMode = !this.playgroundMode;
@@ -92,13 +95,12 @@ export class SettingsManager extends React.Component<{}> {
 
     @undoBatch
     @action
-    changeColorScheme = action((e: React.ChangeEvent) => {
+    changeColorScheme = action((scheme: string) => {
         const activeDashboard = Doc.ActiveDashboard;
         if (!activeDashboard) return;
-        const scheme: ColorScheme = (e.currentTarget as any).value;
         switch (scheme) {
             case ColorScheme.Light:
-                activeDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+                activeDashboard.colorScheme = ColorScheme.Light
                 addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: '#d3d3d3 !important' });
                 break;
             case ColorScheme.Dark:
@@ -107,67 +109,39 @@ export class SettingsManager extends React.Component<{}> {
                 break;
             case ColorScheme.System:
             default:
+                activeDashboard.colorScheme = ColorScheme.System;
                 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
-                    activeDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+                    activeDashboard.colorScheme = e.matches ? ColorScheme.Dark : ColorScheme.Light; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 });
                 break;
         }
     });
 
     @computed get colorsContent() {
-        const colorBox = (func: (color: ColorState) => void) => (
-            <SketchPicker
-                onChange={func}
-                color={StrCast(this.backgroundColor)}
-                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
-            />
-        );
 
-        const colorFlyout = (
-            <div className="colorFlyout">
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchActiveBackgroundColor)}>
-                    <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()}>
-                        <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
-                    </div>
-                </Flyout>
-            </div>
-        );
-        const userColorFlyout = (
-            <div className="colorFlyout">
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchUserColor)}>
-                    <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()}>
-                        <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
-                    </div>
-                </Flyout>
-            </div>
-        );
 
         const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System];
-        const schemeMap = ['Light', 'Dark', 'Match system'];
+        const schemeMap = ['Light', 'Dark', 'Match System'];
 
         return (
             <div className="colors-content">
-                <div className="preferences-color">
-                    <div className="preferences-color-text">Background Color</div>
-                    {colorFlyout}
-                </div>
-                <div className="preferences-color">
-                    <div className="preferences-color-text">Border/Header Color</div>
-                    {userColorFlyout}
-                </div>
-                <div className="preferences-colorScheme">
-                    <div className="preferences-color-text">Color Scheme</div>
-                    <div className="preferences-color-controls">
-                        <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(Doc.ActiveDashboard?.colorScheme)}>
-                            {colorSchemes.map((scheme, i) => (
-                                <option key={scheme} value={scheme}>
-                                    {' '}
-                                    {schemeMap[i]}{' '}
-                                </option>
-                            ))}
-                        </select>
-                    </div>
-                </div>
+                <ColorPicker formLabel='Background Color' icon={<FaFillDrip/>} selectedColor={StrCast(Doc.UserDoc().activeCollectionBackground)} setSelectedColor={this.switchActiveBackgroundColor}/>
+                <ColorPicker formLabel='User Color' icon={<FaPalette/>} selectedColor={StrCast(Doc.SharingDoc().userColor)} setSelectedColor={this.switchUserColor}/>
+                <Dropdown 
+                    formLabel='Theme'
+                    size={Size.SMALL}
+                    type={Type.TERT}
+                    selectedVal={StrCast(Doc.ActiveDashboard?.colorScheme)}
+                    setSelectedVal={(scheme) => this.changeColorScheme(scheme as string)}
+                    items={colorSchemes.map((scheme, i) => (
+                        {
+                            text: schemeMap[i],
+                            val: scheme
+                        }
+                    ))} 
+                    dropdownType={DropdownType.SELECT}
+                    color={StrCast(Doc.SharingDoc().userColor)}
+                />
             </div>
         );
     }
@@ -175,30 +149,65 @@ export class SettingsManager extends React.Component<{}> {
     @computed get formatsContent() {
         return (
             <div className="prefs-content">
-                <div>
-                    <input type="checkbox" onChange={e => (Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'author_date')} checked={Doc.UserDoc().layout_showTitle !== undefined} />
-                    <div className="preferences-check">Show doc header</div>
-                </div>
-                <div>
-                    <input type="checkbox" onChange={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} checked={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} />
-                    <div className="preferences-check">Show full toolbar</div>
-                </div>
-                <div>
-                    <input type="checkbox" onChange={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} checked={FontIconBox.GetShowLabels()} />
-                    <div className="preferences-check">Show button labels</div>
-                </div>
-                <div>
-                    <input type="checkbox" onChange={e => FontIconBox.SetRecognizeGestures(!FontIconBox.GetRecognizeGestures())} checked={FontIconBox.GetRecognizeGestures()} />
-                    <div className="preferences-check">Recognize ink Gestures</div>
-                </div>
-                <div>
-                    <input type="checkbox" onChange={e => (Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)} checked={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} />
-                    <div className="preferences-check">Hide Labels In Ink Shapes</div>
-                </div>
-                <div>
-                    <input type="checkbox" onChange={e => (Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)} checked={BoolCast(Doc.UserDoc().openInkInLightbox)} />
-                    <div className="preferences-check">Open Ink Docs in Lightbox</div>
-                </div>
+                <Toggle 
+                    formLabel={'Show document header'} 
+                    formLabelPlacement={'right'} 
+                    toggleType={ToggleType.SWITCH} 
+                    onClick={e => (Doc.UserDoc().layout_showTitle = Doc.UserDoc().layout_showTitle ? undefined : 'author_date')} 
+                    toggleStatus={Doc.UserDoc().layout_showTitle !== undefined} size={Size.XSMALL}
+                    color={StrCast(Doc.SharingDoc().userColor)}
+
+                />
+                <Toggle 
+                    formLabel={'Show Full Toolbar'} 
+                    formLabelPlacement={'right'} 
+                    toggleType={ToggleType.SWITCH} 
+                    onClick={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} 
+                    toggleStatus={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} 
+                    size={Size.XSMALL}
+                    color={StrCast(Doc.SharingDoc().userColor)}
+
+                />
+                <Toggle 
+                    formLabel={'Show Button Labels'} 
+                    formLabelPlacement={'right'} 
+                    toggleType={ToggleType.SWITCH} 
+                    onClick={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} 
+                    toggleStatus={FontIconBox.GetShowLabels()} 
+                    size={Size.XSMALL}
+                    color={StrCast(Doc.SharingDoc().userColor)}
+
+                />
+                <Toggle 
+                    formLabel={'Recognize Ink Gestures'} 
+                    formLabelPlacement={'right'} 
+                    toggleType={ToggleType.SWITCH} 
+                    onClick={e => FontIconBox.SetRecognizeGestures(!FontIconBox.GetRecognizeGestures())} 
+                    toggleStatus={FontIconBox.GetRecognizeGestures()} 
+                    size={Size.XSMALL}
+                    color={StrCast(Doc.SharingDoc().userColor)}
+
+                />
+                <Toggle 
+                    formLabel={'Hide Labels In Ink Shapes'} 
+                    formLabelPlacement={'right'} 
+                    toggleType={ToggleType.SWITCH} 
+                    onClick={e => (Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)} 
+                    toggleStatus={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} 
+                    size={Size.XSMALL}
+                    color={StrCast(Doc.SharingDoc().userColor)}
+
+                />
+                <Toggle 
+                    formLabel={'Open Ink Docs in Lightbox'} 
+                    formLabelPlacement={'right'} 
+                    toggleType={ToggleType.SWITCH} 
+                    onClick={e => (Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)} 
+                    toggleStatus={BoolCast(Doc.UserDoc().openInkInLightbox)} 
+                    size={Size.XSMALL}
+                    color={StrCast(Doc.SharingDoc().userColor)}
+
+                />
             </div>
         );
     }
@@ -224,25 +233,28 @@ export class SettingsManager extends React.Component<{}> {
 
         return (
             <div className="tab-content appearances-content">
-                <div className="preferences-font">
-                    <div className="preferences-font-text">Default Font</div>
-                    <div className="preferences-font-controls">
-                        <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, '7px')}>
-                            {fontSizes.map(size => (
-                                <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}>
-                                    {' '}
-                                    {size}{' '}
-                                </option>
-                            ))}
-                        </select>
-                        <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, 'Times New Roman')}>
-                            {fontFamilies.map(font => (
-                                <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}>
-                                    {' '}
-                                    {font}{' '}
-                                </option>
-                            ))}
-                        </select>
+                <div className="tab-column">
+                    <div className="tab-column-title">Text</div>
+                    <div className="tab-column-content">
+                        {/* <NumberInput/> */}
+                        <Group formLabel={'Default Font'}>
+                            <Dropdown 
+                                items={fontFamilies.map((val) => {
+                                    return {
+                                        text: val,
+                                        val: val,
+                                        style: {
+                                            fontFamily: val
+                                        }
+                                    }
+                                })} 
+                                dropdownType={DropdownType.SELECT}
+                                type={Type.TERT}
+                                selectedVal={StrCast(Doc.UserDoc().fontFamily)}
+                                setSelectedVal={(val) => {this.changeFontFamily(val as string)}}
+                                color={StrCast(Doc.SharingDoc().userColor)}
+                            />
+                        </Group>
                     </div>
                 </div>
             </div>
@@ -275,12 +287,8 @@ export class SettingsManager extends React.Component<{}> {
                 </div>
                 <div className="password-content-buttons">
                     {!this.passwordResultText ? null : <div className={`${this.passwordResultText.startsWith('Error') ? 'error' : 'success'}-text`}>{this.passwordResultText}</div>}
-                    <a className="password-forgot" href="/forgotPassword">
-                        forgot password?
-                    </a>
-                    <button className="password-submit" onClick={this.changePassword}>
-                        submit
-                    </button>
+                    <Button type={Type.SEC} text={'Forgot Password'} color={StrCast(Doc.SharingDoc().userColor)}/>
+                    <Button type={Type.TERT} text={'Submit'} onClick={this.changePassword} color={StrCast(Doc.SharingDoc().userColor)}/>
                 </div>
             </div>
         );
@@ -289,9 +297,7 @@ export class SettingsManager extends React.Component<{}> {
     @computed get accountOthersContent() {
         return (
             <div className="account-others-content">
-                <button onClick={this.googleAuthorize} value="data">
-                    Authorize Google Acc
-                </button>
+                <Button type={Type.TERT} text={'Connect to Google'} iconPlacement='left' icon={<BsGoogle/>} onClick={() => this.googleAuthorize()}/>
             </div>
         );
     }
@@ -311,7 +317,7 @@ export class SettingsManager extends React.Component<{}> {
         );
     }
 
-    setFreeformScrollMode = (mode: freeformScrollMode) => {
+    setFreeformScrollMode = (mode: string) => {
         Doc.UserDoc().freeformScrollMode = mode;
     };
 
@@ -321,45 +327,77 @@ export class SettingsManager extends React.Component<{}> {
                 <div className="tab-column">
                     <div className="tab-column-title">Modes</div>
                     <div className="tab-column-content">
-                        <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.noviceMode ? 'Novice' : 'Developer'}>
-                            <option key={'Novice'} value={'Novice'}>
-                                {' '}
-                                Novice{' '}
-                            </option>
-                            <option key={'Developer'} value={'Developer'}>
-                                {' '}
-                                Developer
-                            </option>
-                        </select>
-                        <div className="modes-playground">
-                            <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
-                            <div className="playground-text">Playground Mode</div>
-                        </div>
+                        <Dropdown 
+                            formLabel={"Mode"}
+                            items={[
+                                {
+                                    text: 'Novice',
+                                    description: 'Novice mode is a user-friendly setting designed to cater to those who are new to Dash',
+                                    val: "Novice"
+                                },
+                                {
+                                    text: 'Developer',
+                                    description: 'Developer mode is an advanced setting that grants you greater control and access to the underlying mechanics and tools of a software or system. Developer mode is still under development as there are experimental features.',
+                                    val: "Developer"
+                                },
+                            ]} 
+                            selectedVal={Doc.noviceMode ? 'Novice' : 'Developer'}
+                            setSelectedVal={(val) => {this.selectUserMode(val as string)}}
+                            dropdownType={DropdownType.SELECT}
+                            type={Type.TERT}
+                            placement='bottom-start'
+                            color={StrCast(Doc.SharingDoc().userColor)}
+                        />
+                        <Toggle 
+                            formLabel={'Playground Mode'} 
+                            toggleType={ToggleType.SWITCH} 
+                            toggleStatus={this.playgroundMode} 
+                            onClick={this.playgroundModeToggle}
+                            color={StrCast(Doc.SharingDoc().userColor)}
+                        />
                     </div>
-                    <div className="tab-column-title" style={{ marginTop: 10, marginBottom: 0 }}>
-                        Freeform scrolling
+                    <div className="tab-column-title" style={{ marginTop: 20, marginBottom: 10 }}>
+                        Freeform Navigation
                     </div>
                     <div className="tab-column-content">
-                        <button style={{ backgroundColor: Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? 'blue' : '' }} onClick={() => this.setFreeformScrollMode(freeformScrollMode.Pan)}>
-                            Scroll to pan
-                        </button>
-                        <div>
-                            <div>Scrolling pans canvas, shift + scrolling zooms</div>
-                        </div>
-                        <button style={{ backgroundColor: Doc.UserDoc().freeformScrollMode === freeformScrollMode.Zoom ? 'blue' : '' }} onClick={() => this.setFreeformScrollMode(freeformScrollMode.Zoom)}>
-                            Scroll to zoom
-                        </button>
-                        <div>Scrolling zooms canvas</div>
+                        <Dropdown 
+                            formLabel={"Scroll Mode"}
+                            items={[
+                                {
+                                    text: 'Scroll to Pan',
+                                    description: 'Scrolling pans canvas, shift + scrolling zooms',
+                                    val: freeformScrollMode.Pan
+                                },
+                                {
+                                    text: 'Scroll to Zoom',
+                                    description: 'Scrolling zooms canvas',
+                                    val: freeformScrollMode.Zoom
+                                },
+                            ]} 
+                            selectedVal={StrCast(Doc.UserDoc().freeformScrollMode)}
+                            setSelectedVal={(val) => this.setFreeformScrollMode(val as string)}
+                            dropdownType={DropdownType.SELECT}
+                            type={Type.TERT}
+                            placement='bottom-start'
+                            color={StrCast(Doc.SharingDoc().userColor)}
+                        />
                     </div>
                 </div>
                 <div className="tab-column">
                     <div className="tab-column-title">Permissions</div>
                     <div className="tab-column-content">
-                        <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
-                        <div className="default-acl">
-                            <input className="acl-check" type="checkbox" checked={BoolCast(Doc.defaultAclPrivate)} onChange={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))} />
-                            <div className="acl-text">Default access private</div>
-                        </div>
+                        <Button
+                            text={"Manage Groups"}
+                            type={Type.TERT}
+                            onClick={() => GroupManager.Instance?.open()}
+                            color={StrCast(Doc.SharingDoc().userColor)}
+                        />
+                        <Toggle 
+                            toggleType={ToggleType.SWITCH} 
+                            formLabel={"Default access private"}
+                            color={StrCast(Doc.SharingDoc().userColor)}
+                            toggleStatus={BoolCast(Doc.defaultAclPrivate)} 
+                            onClick={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))}/>
                     </div>
                 </div>
             </div>
@@ -390,14 +428,20 @@ export class SettingsManager extends React.Component<{}> {
 
                     <div className="settings-user">
                         <div className="settings-username">{Doc.CurrentUserEmail}</div>
-                        <button className="logout-button" onClick={() => window.location.assign(Utils.prepend('/logout'))}>
-                            {Doc.GuestDashboard ? 'Exit' : 'Log Out'}
-                        </button>
+                        <Button 
+                            text={Doc.GuestDashboard ? 'Exit' : 'Log Out'}
+                            type={Type.TERT}
+                            onClick={() => window.location.assign(Utils.prepend('/logout'))}
+                        />
                     </div>
                 </div>
 
-                <div className="close-button" onClick={this.close}>
-                    <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
+                            
+                <div className="close-button">
+                    <Button
+                        icon={<FontAwesomeIcon icon={'times'} color="black" size={'lg'} />}
+                        onClick={this.close}
+                    />
                 </div>
 
                 <div className="settings-content">
@@ -418,7 +462,7 @@ export class SettingsManager extends React.Component<{}> {
                 isDisplayed={this.isOpen}
                 interactive={true}
                 closeOnExternalClick={this.close}
-                dialogueBoxStyle={{ width: '500px', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
+                dialogueBoxStyle={{ width: 'fit-content', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
             />
         );
     }
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 932e94664..1be27fa62 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -17,10 +17,8 @@
 
         .close-button {
             position: absolute;
-            right: 1em;
-            top: 1em;
-            cursor: pointer;
-            z-index: 999;
+            right: 2px;
+            top: 2px;
         }
 
         .share-container {
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 1a7a4d342..3bd6ac61c 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -394,6 +394,7 @@ export class DashboardView extends React.Component {
             _layout_hideContextMenu: true,
             title: 'New trail',
             toolTip: 'Create new trail',
+            color: Colors.BLACK,
             btnType: ButtonType.ClickButton,
             buttonText: 'New trail',
             icon: 'plus',
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index b0b757388..e84e2620f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -16,6 +16,7 @@ import { CollectionView } from './collections/CollectionView';
 import { MainView } from './MainView';
 import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
 import { PingManager } from '../util/PingManager';
+import './global/globalScripts';
 dotenv.config();
 
 AssignAllExtensions();
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
new file mode 100644
index 000000000..8f6d898fe
--- /dev/null
+++ b/src/client/views/global/globalScripts.ts
@@ -0,0 +1,407 @@
+import { Colors } from "browndash-components";
+import { runInAction, action } from "mobx";
+import { aggregateBounds } from "../../../Utils";
+import { Doc } from "../../../fields/Doc";
+import { Width, Height } from "../../../fields/DocSymbols";
+import { InkTool } from "../../../fields/InkField";
+import { Cast, StrCast, NumCast, BoolCast } from "../../../fields/Types";
+import { WebField } from "../../../fields/URLField";
+import { GestureUtils } from "../../../pen-gestures/GestureUtils";
+import { LinkManager } from "../../util/LinkManager";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
+import { SelectionManager } from "../../util/SelectionManager";
+import { UndoManager } from "../../util/UndoManager";
+import { GestureOverlay } from "../GestureOverlay";
+import { InkTranscription } from "../InkTranscription";
+import { ActiveFillColor, SetActiveFillColor, ActiveIsInkMask, SetActiveIsInkMask, ActiveInkWidth, SetActiveInkWidth, ActiveInkColor, SetActiveInkColor } from "../InkingStroke";
+import { CollectionFreeFormView } from "../collections/collectionFreeForm";
+import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
+import { WebBox } from "../nodes/WebBox";
+import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
+import { DocumentType } from "../../documents/DocumentTypes";
+
+// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function setView(view: string) {
+    const selected = SelectionManager.Docs().lastElement();
+    selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed');
+});
+
+// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
+    const selectedViews = SelectionManager.Views();
+    console.log(color, checkResult);
+    if (Doc.ActiveTool !== InkTool.None) {
+        if (checkResult) {
+            return ActiveFillColor();
+        }
+        SetActiveFillColor(color ?? 'transparent');
+    } else if (selectedViews.length) {
+        if (checkResult) {
+            const selView = selectedViews.lastElement();
+            const fieldKey = selView.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
+            const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+            return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber)[fieldKey] ?? 'transparent';
+        }
+        selectedViews.forEach(dv => {
+            const fieldKey = dv.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
+            const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
+            const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+            if (contentFrameNumber !== undefined) {
+                CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color });
+            } else {
+                console.log('setting color to: ', color)
+                dv.rootDoc['_' + fieldKey] = color;
+            }
+        });
+    } else {
+        const selected = SelectionManager.Docs().length ? SelectionManager.Docs() : LinkManager.currentLink ? [LinkManager.currentLink] : [];
+        if (checkResult) {
+            return selected.lastElement()?._backgroundColor ?? 'transparent';
+        }
+        selected.forEach(doc => (doc._backgroundColor = color));
+    }
+});
+
+// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
+    if (checkResult) {
+        return Doc.SharingDoc().userColor;
+    }
+    Doc.SharingDoc().userColor = undefined;
+    Doc.GetProto(Doc.SharingDoc()).userColor = color;
+    Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'author_date');
+});
+
+// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+    if (checkResult) {
+        if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE;
+        return 'transparent';
+    }
+    selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
+});
+
+ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
+    const selected = SelectionManager.Docs().lastElement();
+    // prettier-ignore
+    const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
+        ['grid', {
+            checkResult: (doc:Doc) => doc._freeform_backgroundGrid,
+            setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
+        }],
+        ['snaplines', {
+            checkResult: (doc:Doc) => doc._freeform_snapLines,
+            setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines,
+        }],
+        ['viewAll', {
+            checkResult: (doc:Doc) => doc._freeform_fitContentsToBox,
+            setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox,
+        }],
+        ['clusters', {
+            waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
+            checkResult: (doc:Doc) => doc._freeform_useClusters,
+            setDoc: (doc:Doc) => doc._freeform_useClusters = !doc._freeform_useClusters,
+        }],
+        ['arrange', {
+            waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
+            checkResult: (doc:Doc) => doc._autoArrange,
+            setDoc: (doc:Doc) => doc._autoArrange = !doc._autoArrange,
+        }],
+        ['flashcards', {
+            checkResult: (doc:Doc) => Doc.UserDoc().defaultToFlashcards,
+            setDoc: (doc:Doc) => Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards,
+        }],
+   ]);
+
+    if (checkResult) {
+        return map.get(attr)?.checkResult(selected) ? Colors.MEDIUM_BLUE : 'transparent';
+    }
+    const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
+    SelectionManager.Docs().map(dv => map.get(attr)?.setDoc(dv));
+    setTimeout(() => batch.end(), 100);
+});
+ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize', value: any, checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+    const selected = SelectionManager.Docs().lastElement();
+    // prettier-ignore
+    const map: Map<'font'|'fontColor'|'highlight'|'fontSize', { checkResult: () => any; setDoc: () => void;}> = new Map([
+        ['font', {
+            checkResult: () => RichTextMenu.Instance?.fontFamily,
+            setDoc: () => value && RichTextMenu.Instance.setFontFamily(value),
+        }],
+        ['highlight', {
+            checkResult: () =>(selected ?? Doc.UserDoc())._fontHighlight,
+            setDoc: () => value  && RichTextMenu.Instance.setHighlight(value),
+        }],
+        ['fontColor', {
+            checkResult: () => RichTextMenu.Instance?.fontColor,
+            setDoc: () => value && RichTextMenu.Instance.setColor(value),
+        }],
+        ['fontSize', {
+            checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
+            setDoc: () => {
+                if (typeof value === 'number') value = value.toString();
+                if (value && Number(value).toString() === value) value += 'px';
+                RichTextMenu.Instance.setFontSize(value);
+            },
+        }],
+    ]);
+
+    if (checkResult) {
+        return map.get(attr)?.checkResult();
+    }
+    map.get(attr)?.setDoc?.();
+});
+
+type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'bullet' | 'decimal';
+type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }];
+ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) {
+    const textView = RichTextMenu.Instance?.TextView;
+    const editorView = textView?.EditorView;
+    // prettier-ignore
+    const alignments:attrfuncs[] = (['left','right','center'] as ("left"|"center"|"right")[]).map((where) =>
+        [ where, {      checkResult: () =>(editorView ? (RichTextMenu.Instance.textAlign ===where): (Doc.UserDoc().textAlign ===where) ? true:false),
+                        toggle: () => (editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, where):(Doc.UserDoc().textAlign = where))}]);
+    // prettier-ignore
+    const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
+        [ list, {       checkResult: () => (editorView ? RichTextMenu.Instance.getActiveListStyle() === list:false),
+                        toggle: () => editorView?.state && RichTextMenu.Instance.changeListType(list) }]);
+    // prettier-ignore
+    const attrs:attrfuncs[] = [
+        ['dictation', { checkResult: () => textView?._recording ? true:false,
+                        toggle: () => textView && runInAction(() => (textView._recording = !textView._recording)) }],
+        ['noAutoLink',{ checkResult: () => (editorView ? RichTextMenu.Instance.noAutoLink : false),
+                        toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}],
+        ['bold',      { checkResult: () => (editorView ? RichTextMenu.Instance.bold : (Doc.UserDoc().fontWeight === 'bold') ? true:false),
+                        toggle: editorView ? RichTextMenu.Instance.toggleBold : () => (Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold')}],
+        ['italics',   { checkResult: () => (editorView ? RichTextMenu.Instance.italics : (Doc.UserDoc().fontStyle === 'italics') ? true:false),
+                        toggle: editorView ? RichTextMenu.Instance.toggleItalics : () => (Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics')}],
+        ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance.underline : (Doc.UserDoc().textDecoration === 'underline') ? true:false),
+                        toggle: editorView ? RichTextMenu.Instance.toggleUnderline : () => (Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline') }]]
+
+    const map = new Map(attrs.concat(alignments).concat(listings));
+    if (checkResult) return map.get(charStyle)?.checkResult() ? Colors.MEDIUM_BLUE : 'transparent';
+    map.get(charStyle)?.toggle();
+});
+
+export function checkInksToGroup() {
+    // console.log("getting here to inks group");
+    if (Doc.ActiveTool === InkTool.Write) {
+        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+            // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
+            const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => {
+                // console.log(inkDoc.x, inkDoc.y);
+            });
+        });
+    }
+}
+
+export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
+    // TODO nda - if document being added to is a inkGrouping then we can just add to that group
+    if (Doc.ActiveTool === InkTool.Write) {
+        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+            const selected = ffView.unprocessedDocs;
+            // loop through selected an get the bound
+            const bounds: { x: number; y: number; width?: number; height?: number }[] = [];
+
+            selected.map(
+                action(d => {
+                    const x = NumCast(d.x);
+                    const y = NumCast(d.y);
+                    const width = d[Width]();
+                    const height = d[Height]();
+                    bounds.push({ x, y, width, height });
+                })
+            );
+
+            const aggregBounds = aggregateBounds(bounds, 0, 0);
+            const marqViewRef = ffView._marqueeViewRef.current;
+
+            // set the vals for bounds in marqueeView
+            if (marqViewRef) {
+                marqViewRef._downX = aggregBounds.x;
+                marqViewRef._downY = aggregBounds.y;
+                marqViewRef._lastX = aggregBounds.r;
+                marqViewRef._lastY = aggregBounds.b;
+            }
+
+            selected.map(
+                action(d => {
+                    const dx = NumCast(d.x);
+                    const dy = NumCast(d.y);
+                    delete d.x;
+                    delete d.y;
+                    delete d.activeFrame;
+                    delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                    delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                    // calculate pos based on bounds
+                    if (marqViewRef?.Bounds) {
+                        d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+                        d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+                    }
+                    return d;
+                })
+            );
+            ffView.props.removeDocument?.(selected);
+            // TODO: nda - this is the code to actually get a new grouped collection
+            const newCollection = marqViewRef?.getCollection(selected, undefined, true);
+            if (newCollection) {
+                newCollection.height = newCollection[Height]();
+                newCollection.width = newCollection[Width]();
+            }
+
+            // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+            newCollection && ffView.props.addDocument?.(newCollection);
+            // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
+            ffView.unprocessedDocs = [];
+
+            InkTranscription.Instance.transcribeInk(newCollection, selected, false);
+        });
+    }
+    CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
+}
+
+function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean, checkResult?: boolean) {
+    InkTranscription.Instance?.createInkGroup();
+    if (checkResult) {
+        return (Doc.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool
+            ? GestureOverlay.Instance?.KeepPrimitiveMode || ![GestureUtils.Gestures.Circle, GestureUtils.Gestures.Line, GestureUtils.Gestures.Rectangle].includes(tool as GestureUtils.Gestures)
+                ? Colors.MEDIUM_BLUE
+                : Colors.MEDIUM_BLUE_ALT
+            : 'transparent';
+    }
+    runInAction(() => {
+        if (GestureOverlay.Instance) {
+            GestureOverlay.Instance.KeepPrimitiveMode = keepPrim;
+        }
+        if (Object.values(GestureUtils.Gestures).includes(tool as any)) {
+            if (GestureOverlay.Instance.InkShape === tool && !keepPrim) {
+                Doc.ActiveTool = InkTool.None;
+                GestureOverlay.Instance.InkShape = undefined;
+            } else {
+                Doc.ActiveTool = InkTool.Pen;
+                GestureOverlay.Instance.InkShape = tool as GestureUtils.Gestures;
+            }
+        } else if (tool) {
+            // pen or eraser
+            if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape && !keepPrim) {
+                Doc.ActiveTool = InkTool.None;
+            } else {
+                Doc.ActiveTool = tool as any;
+                GestureOverlay.Instance.InkShape = undefined;
+            }
+        } else {
+            Doc.ActiveTool = InkTool.None;
+        }
+    });
+}
+
+ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode');
+
+// toggle: Set overlay status of selected document
+ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) {
+    const selected = SelectionManager.Docs().lastElement();
+    // prettier-ignore
+    const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
+        ['inkMask', {
+            checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.stroke_isInkMask) : ActiveIsInkMask()) ? Colors.MEDIUM_BLUE : 'transparent'),
+            setInk: (doc: Doc) => (doc.stroke_isInkMask = !doc.stroke_isInkMask),
+            setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()),
+        }],
+        ['fillColor', {
+            checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.fillColor) : ActiveFillColor() ?? "transparent"),
+            setInk: (doc: Doc) => (doc.fillColor = StrCast(value)),
+            setMode: () => SetActiveFillColor(StrCast(value)),
+        }],
+        [ 'strokeWidth', {
+            checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.stroke_width) : ActiveInkWidth()),
+            setInk: (doc: Doc) => (doc.stroke_width = NumCast(value)),
+            setMode: () => SetActiveInkWidth(value.toString()),
+        }],
+        ['strokeColor', {
+            checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.color) : ActiveInkColor()),
+            setInk: (doc: Doc) => (doc.color = String(value)),
+            setMode: () => SetActiveInkColor(StrCast(value)),
+        }],
+    ]);
+
+    if (checkResult) {
+        return map.get(option)?.checkResult();
+    }
+    map.get(option)?.setMode();
+    SelectionManager.Docs()
+        .filter(doc => doc.type === DocumentType.INK)
+        .map(doc => map.get(option)?.setInk(doc));
+});
+
+/** WEB
+ *      webSetURL
+ **/
+ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
+    const selected = SelectionManager.Views().lastElement();
+    if (selected?.rootDoc.type === DocumentType.WEB) {
+        if (checkResult) {
+            return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href);
+        }
+        selected.ComponentView?.setData?.(url);
+        //selected.rootDoc.data = new WebField(url);
+    }
+});
+ScriptingGlobals.add(function webForward(checkResult?: boolean) {
+    const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox;
+    if (checkResult) {
+        return selected?.forward(checkResult) ? undefined : 'lightGray';
+    }
+    selected?.forward();
+});
+ScriptingGlobals.add(function webBack(checkResult?: boolean) {
+    const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox;
+    if (checkResult) {
+        return selected?.back(checkResult) ? undefined : 'lightGray';
+    }
+    selected?.back();
+});
+
+/** Schema
+ *      toggleSchemaPreview
+ **/
+ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
+    const selected = SelectionManager.Docs().lastElement();
+    if (checkResult && selected) {
+        const result: boolean = NumCast(selected.schema_previewWidth) > 0;
+        if (result) return Colors.MEDIUM_BLUE;
+        else return 'transparent';
+    } else if (selected) {
+        if (NumCast(selected.schema_previewWidth) > 0) {
+            selected.schema_previewWidth = 0;
+        } else {
+            selected.schema_previewWidth = 200;
+        }
+    }
+});
+ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
+    const selected = SelectionManager.Docs().lastElement();
+    if (checkResult && selected) {
+        return NumCast(selected._schema_singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent';
+    }
+    if (selected) {
+        selected._schema_singleLine = !selected._schema_singleLine;
+    }
+});
+
+/** STACK
+ *      groupBy
+ */
+ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
+    SelectionManager.Docs().map(doc => (doc._text_fontFamily = key));
+    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+    if (checkResult) {
+        return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+    }
+    if (editorView) RichTextMenu.Instance.setFontFamily(key);
+    else Doc.UserDoc().fontFamily = key;
+});
diff --git a/src/client/views/nodes/button/ButtonInterface.ts b/src/client/views/nodes/button/ButtonInterface.ts
deleted file mode 100644
index 0aa2ac8e1..000000000
--- a/src/client/views/nodes/button/ButtonInterface.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Doc } from "../../../../fields/Doc";
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { ButtonType } from "./FontIconBox";
-
-export interface IButtonProps {
-    type: string | ButtonType;
-    rootDoc: Doc;
-    label: any;
-    icon: IconProp;
-    color: string;
-    backgroundColor: string;
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBadge.scss b/src/client/views/nodes/button/FontIconBadge.scss
deleted file mode 100644
index 78f506e57..000000000
--- a/src/client/views/nodes/button/FontIconBadge.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.fontIconBadge {
-    background: red;
-    width: 15px;
-    height: 15px;
-    top: 8px;
-    display: block;
-    position: absolute;
-    right: 5;
-    border-radius: 50%;
-    text-align: center;
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx
deleted file mode 100644
index b50588ce2..000000000
--- a/src/client/views/nodes/button/FontIconBadge.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import './FontIconBadge.scss';
-
-interface FontIconBadgeProps {
-    value: string | undefined;
-}
-
-@observer
-export class FontIconBadge extends React.Component<FontIconBadgeProps> {
-    _notifsRef = React.createRef<HTMLDivElement>();
-
-    // onPointerDown = (e: React.PointerEvent) => {
-    //     setupMoveUpEvents(this, e,
-    //         (e: PointerEvent) => {
-    //             const dragData = new DragManager.DocumentDragData([this.props.collection!]);
-    //             DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
-    //             return true;
-    //         },
-    //         returnFalse, emptyFunction, false);
-    // }
-
-    render() {
-        if (this.props.value === undefined) return null;
-        return (
-            <div className="fontIconBadge-container" ref={this._notifsRef}>
-                <div
-                    className="fontIconBadge"
-                    style={{ display: 'initial' }}
-                    // onPointerDown={this.onPointerDown}
-                >
-                    {this.props.value}
-                </div>
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d181de8d0..e31407f4b 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,39 +1,23 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
-import { ColorPicker, Dropdown, DropdownType, IconButton, IListItemProps, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { action, computed, observable, runInAction } from 'mobx';
+import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Size, Toggle, ToggleType, Type } from 'browndash-components';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ColorState, SketchPicker } from 'react-color';
-import * as fa from 'react-icons/fa';
 import { Doc, StrListCast } from '../../../../fields/Doc';
-import { Height, Width } from '../../../../fields/DocSymbols';
-import { InkTool } from '../../../../fields/InkField';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { WebField } from '../../../../fields/URLField';
-import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, Utils } from '../../../../Utils';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
-import { LinkManager } from '../../../util/LinkManager';
-import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
-import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
+import { undoable, UndoManager } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
 import { EditableView } from '../../EditableView';
-import { GestureOverlay } from '../../GestureOverlay';
 import { Colors } from '../../global/globalEnums';
-import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../../InkingStroke';
-import { InkTranscription } from '../../InkTranscription';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
-import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
 import { OpenWhere } from '../DocumentView';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
-import { WebBox } from '../WebBox';
 import './FontIconBox.scss';
 
 export enum ButtonType {
@@ -136,116 +120,35 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     /**
      * Number button
      */
-    @computed get numberSliderButton() {
+    @computed get numberDropdown() {
+        let type: NumberDropdownType;
+        switch(this.type) {
+            case ButtonType.NumberDropdownButton: 
+                type = 'dropdown'
+                break;
+            case ButtonType.NumberInlineButton:
+                type = 'input'
+                break;
+            case ButtonType.NumberSliderButton:
+            default:
+                type = 'slider'
+                break;
+        }
         const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ self: this.rootDoc, value, _readOnly_: value === undefined });
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         // Script for checking the outcome of the toggle
-        const checkResult = Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
+        const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));
         const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
 
-        const dropdown = (
-            <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()}>
-                <input
-                    className="menu-slider"
-                    type="range"
-                    step="1"
-                    min={NumCast(this.rootDoc.numBtnMin, 0)}
-                    max={NumCast(this.rootDoc.numBtnMax, 100)}
-                    //readOnly={true}
-                    value={checkResult}
-                    onPointerDown={() => (this._batch = UndoManager.StartBatch('num slider changing'))}
-                    onPointerUp={() => this._batch?.end()}
-                    onChange={undoable(e => {
-                        e.stopPropagation();
-                        numScript(Number(e.target.value));
-                    }, 'set num value')}
-                />
-            </div>
-        );
-        return (
-            <div
-                className="menuButton numBtn slider"
-                onPointerDown={e => e.stopPropagation()}
-                onClick={action(() => {
-                    this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
-                    this.noTooltip = this.rootDoc.dropDownOpen;
-                    Doc.UnBrushAllDocs();
-                })}>
-                {checkResult}
-                {label}
-                {this.rootDoc.dropDownOpen ? dropdown : null}
-            </div>
-        );
-    }
-    /**
-     * Number button
-     */
-    @computed get numberDropdownButton() {
-        const numScript = (value?: number) => ScriptCast(this.rootDoc.script)?.script.run({ self: this.rootDoc, value, _readOnly_: value === undefined });
-
-        const checkResult = Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3));
-
-        const items: number[] = [];
-        for (let i = 0; i < 100; i += 2) items.push(i);
-
-        const list = items.map(value => {
-            return (
-                <div
-                    className="list-item"
-                    key={`${value}`}
-                    style={{
-                        backgroundColor: value.toString() === checkResult ? Colors.LIGHT_BLUE : undefined,
-                    }}
-                    onClick={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}>
-                    {value}
-                </div>
-            );
-        });
-        return (
-            <div className="menuButton numBtn list">
-                <div className="button" onClick={undoable(e => numScript(Number(checkResult) - 1), `${this.rootDoc.title} decrement value`)}>
-                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon="minus" />
-                </div>
-                <div
-                    className={`button ${'number'}`}
-                    onPointerDown={e => {
-                        e.stopPropagation();
-                        e.preventDefault();
-                    }}
-                    onClick={action(() => {
-                        this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
-                        this.noTooltip = this.rootDoc.dropDownOpen;
-                        Doc.UnBrushAllDocs();
-                    })}>
-                    <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} readOnly={true} onChange={undoable(e => numScript(Number(e.target.value)), `${this.rootDoc.title} button set value`)} />
-                </div>
-                <div className={`button`} onClick={undoable(e => numScript(Number(checkResult) + 1), `${this.rootDoc.title} increment value`)}>
-                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} />
-                </div>
-
-                {this.rootDoc.dropDownOpen ? (
-                    <div>
-                        <div className="menuButton-dropdownList" style={{ left: '25%' }}>
-                            {list}
-                        </div>
-                        <div
-                            className="dropbox-background"
-                            onClick={action(e => {
-                                e.stopPropagation();
-                                this.rootDoc.dropDownOpen = false;
-                                this.noTooltip = false;
-                                Doc.UnBrushAllDocs();
-                            })}
-                        />
-                    </div>
-                ) : null}
-            </div>
-        );
-    }
-    /**
-     * Number button
-     */
-    @computed get numberInlineButton() {
-        return <div />;
+        return <NumberDropdown 
+            color={color} 
+            numberDropdownType={type} 
+            showPlusMinus={false} 
+            min={NumCast(this.rootDoc.numBtnMin, 0)}
+            max={NumCast(this.rootDoc.numBtnMax, 100)}
+            number={checkResult} 
+            setNumber={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}
+        />
     }
 
     /**
@@ -296,18 +199,22 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         try {
             if (script?.script.originalScript.startsWith('setView')) {
                 const selected = SelectionManager.Docs().lastElement();
+                console.log('selected')
                 if (selected) {
                     if (StrCast(selected.type) === DocumentType.COL) {
                         text = StrCast(selected._type_collection);
                     } else {
+                        console.log("doc selected", selected.title)
                         dropdown = false;
                         text = selected.type === DocumentType.RTF ? 'Text' : StrCast(selected.type);
                         icon = Doc.toIcon(selected);
+                        return <div>Hi!</div>
                     }
                 } else {
                     dropdown = false;
                     icon = 'globe-asia';
                     text = 'User Default';
+                    return <div>Hi!</div>
                 }
                 noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
             } else text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
@@ -323,79 +230,32 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             .map(value => (
                 {
                     text: value,
+                    val: value,
                     style: { fontFamily: value },
                     onClick: undoable(() => script.script.run({ self: this.rootDoc, value }), value)
                     // shortcut: '#',
                 }
             ));
 
-        const label =
-            !this.label || !FontIconBox.GetShowLabels() ? null : (
-                <div className="fontIconBox-label" style={{ bottom: 0, position: 'absolute', color: color, backgroundColor: backgroundColor }}>
-                    {this.label}
-                </div>
-            );
 
         return (
-            <Dropdown selected={{text: text}} color={Colors.WHITE} type={Type.PRIM} dropdownType={DropdownType.SELECT} items={list}/>
+            <Dropdown 
+                selectedVal={text}
+                setSelectedVal={undoable((val) => script.script.run({ self: this.rootDoc, val }), `dropdown select ${this.label}`)}
+                color={Colors.WHITE} 
+                type={Type.PRIM} 
+                dropdownType={DropdownType.SELECT} 
+                items={list}
+                tooltip={this.label}
+            />
         )
-
-        return (
-            <div
-                className={`menuButton ${this.type} ${active}`}
-                style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : 'flex' }}
-                onClick={
-                    dropdown
-                        ? action(() => {
-                              this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
-                              this.noTooltip = this.rootDoc.dropDownOpen;
-                              Doc.UnBrushAllDocs();
-                          })
-                        : undefined
-                }>
-                {dropdown ? null : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
-                <div className="menuButton-dropdown-header">{text && text[0].toUpperCase() + text.slice(1)}</div>
-                {label}
-                {!dropdown ? null : (
-                    <div className="menuButton-dropDown">
-                        <FontAwesomeIcon icon={icon} color={color} size="sm" />
-                    </div>
-                )}
-                {this.rootDoc.dropDownOpen ? (
-                    <div>
-                        <div className="menuButton-dropdownList" style={{ left: 0 }}>
-                            {list}
-                        </div>
-                        <div
-                            className="dropbox-background"
-                            onClick={action(e => {
-                                e.stopPropagation();
-                                this.rootDoc.dropDownOpen = false;
-                                this.noTooltip = false;
-                                Doc.UnBrushAllDocs();
-                            })}
-                        />
-                    </div>
-                ) : null}
-            </div>
-        );
     }
 
-    @observable colorPickerClosed: boolean = true;
+
     @computed get colorScript() {
         return ScriptCast(this.rootDoc.script);
     }
 
-    colorPicker = (curColor: string) => {
-        const change = (value: ColorState, ev: MouseEvent) => {
-            ev.preventDefault();
-            ev.stopPropagation();
-            const s = this.colorScript;
-            s && undoBatch(() => s.script.run({ self: this.rootDoc, value: Utils.colorString(value), _readOnly_: false }).result)();
-        };
-        const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent'];
-        return <SketchPicker onChange={change as any /* SketchPicker passes the mouse event to the callback, but the type system doesn't know that */} color={curColor} presetColors={presets} />;
-    };
     /**
      * Color button
      */
@@ -405,61 +265,20 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const curColor = this.colorScript?.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent';
         const tooltip: string = StrCast(this.rootDoc.toolTip);
 
-        const label =
-            !this.label || !FontIconBox.GetShowLabels() ? null : (
-                <div className="fontIconBox-label" style={{ color, backgroundColor }}>
-                    {this.label}
-                </div>
-            );
-        
         return (
             <ColorPicker 
-                onChange={(e) => {
-                    this.colorPickerClosed = !this.colorPickerClosed;
-                    setTimeout(() => Doc.UnBrushAllDocs());
-                    e.stopPropagation();
+                setSelectedColor={(value) => {
+                    const s = this.colorScript;
+                    s && undoable(() => s.script.run({ self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)();
                 }}
-                color={color}
+                selectedColor={curColor}
                 type={Type.PRIM}
+                color={color}
                 icon={this.Icon(color)!}
                 tooltip={tooltip}
+                label={this.label}
             />
         )
-
-        return (
-            <div
-                className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')} ${this.colorPickerClosed}`}
-                style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
-                onClick={action(e => {
-                    this.colorPickerClosed = !this.colorPickerClosed;
-                    this.noTooltip = !this.colorPickerClosed;
-                    setTimeout(() => Doc.UnBrushAllDocs());
-                    e.stopPropagation();
-                })}
-                onPointerDown={e => e.stopPropagation()}>
-                {this.Icon(color)}
-                <div className="colorButton-color" style={{ backgroundColor: curColor }} />
-                {label}
-                {/* {dropdownCaret} */}
-                {this.colorPickerClosed ? null : (
-                    <div>
-                        <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
-                            {this.colorPicker(curColor)}
-                        </div>
-                        <div
-                            className="dropbox-background"
-                            onPointerDown={action(e => {
-                                e.preventDefault();
-                                e.stopPropagation();
-                                this.colorPickerClosed = true;
-                                this.noTooltip = false;
-                                Doc.UnBrushAllDocs();
-                            })}
-                        />
-                    </div>
-                )}
-            </div>
-        );
     }
 
     @computed get toggleButton() {
@@ -498,6 +317,11 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const checkResult = script?.script.run({ value: '', _readOnly_: true }).result;
 
         const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result;
+        
+        return <EditableText 
+            editing={false} onEdit={() => {}} setEditing={(editing: boolean) => {}}            
+        />
+        
         return (
             <div className="menuButton editableText">
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} />
@@ -514,78 +338,39 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const tooltip: string = StrCast(this.rootDoc.toolTip);
 
-        const label = (noBackground: boolean = false) =>
-            !this.label || !FontIconBox.GetShowLabels() ? null : (
-                <div className="fontIconBox-label" style={{ color, backgroundColor: noBackground ? 'transparent' : backgroundColor }}>
-                    {this.label}
-                </div>
-            );
         // TODO:glr Add label of button type
         let button: JSX.Element = this.defaultButton;
-
         // prettier-ignore
         switch (this.type) {
-            case ButtonType.EditableText: return this.editableText;
+            case ButtonType.EditableText: 
+                button = this.editableText;
+                break;
             case ButtonType.DropdownList:
-                let script = ScriptCast(this.rootDoc.script);
-                let noviceList: string[] = [];
-                let text: string | undefined;
-                let dropdown = true;
-                let icon: IconProp = 'caret-down';
-                try {
-                    if (script?.script.originalScript.startsWith('setView')) {
-                        const selected = SelectionManager.Docs().lastElement();
-                        if (selected) {
-                            if (StrCast(selected.type) === DocumentType.COL) {
-                                text = StrCast(selected._type_collection);
-                            } else {
-                                dropdown = false;
-                                text = selected.type === DocumentType.RTF ? 'Text' : StrCast(selected.type);
-                                icon = Doc.toIcon(selected);
-                            }
-                        } else {
-                            dropdown = false;
-                            icon = 'globe-asia';
-                            text = 'User Default';
-                        }
-                        noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
-                    } else text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-                } catch (e) {
-                    console.log(e);
-                } 
-                // Get items to place into the list
-                const list: IListItemProps[] = this.buttonList
-                .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
-                .map(value => (
-                    {
-                        text: value,
-                        style: { fontFamily: value },
-                        onClick: undoable(() => script.script.run({ self: this.rootDoc, value }), value)
-                        // shortcut: '#',
-                    }
-                ));
-                button = <Dropdown selected={{text: text}} color={Colors.WHITE} type={Type.PRIM} dropdownType={DropdownType.SELECT} items={list}/>
+                button = this.dropdownListButton;
+                break;
+            case ButtonType.ColorButton: 
+                button = this.colorButton; 
+                break;
+            case ButtonType.NumberDropdownButton: 
+            case ButtonType.NumberInlineButton:
+            case ButtonType.NumberSliderButton: 
+                button = this.numberDropdown; 
+                break;
+            case ButtonType.DropdownButton: 
+                button = this.dropdownButton; 
                 break;
-            case ButtonType.ColorButton: button = this.colorButton; break;
-            case ButtonType.NumberDropdownButton: button = this.numberDropdownButton; break;
-            case ButtonType.NumberInlineButton: button = this.numberInlineButton; break;
-            case ButtonType.NumberSliderButton: button = this.numberSliderButton; break;
-            case ButtonType.DropdownButton: button = this.dropdownButton; break;
             case ButtonType.ToggleButton: button = this.toggleButton; break;
             case ButtonType.TextButton: 
-                    // Script for checking the outcome of the toggle
-                    script = ScriptCast(this.rootDoc.script);
-                    const checkResult = script?.script.run({ _readOnly_: true }).result;
-                    button = (
-                    <div className={`menuButton ${this.type}`} style={{ color, backgroundColor:checkResult ?? backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
-                        {this.Icon(color)}
-                        {StrCast(this.rootDoc.buttonText) ? <div className="button-text">{StrCast(this.rootDoc.buttonText)}</div> : null}
-                        {label()}
-                    </div>
+                // Script for checking the outcome of the toggle
+                const script = ScriptCast(this.rootDoc.script);
+                const checkResult = script?.script.run({ _readOnly_: true }).result;
+                button = (
+                    <Button tooltip={tooltip} color={checkResult ?? backgroundColor} icon={this.Icon(color)!} text={StrCast(this.rootDoc.buttonText)} label={this.label}/>
                 );
                 break;
             case ButtonType.ClickButton:
-            case ButtonType.ToolButton: button = (
+            case ButtonType.ToolButton: 
+                button = (
                     <IconButton tooltip={tooltip} color={color} icon={this.Icon(color)!} label={this.label}/>
                 );
                 break;
@@ -598,387 +383,3 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         return button;
     }
 }
-
-// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setView(view: string) {
-    const selected = SelectionManager.Docs().lastElement();
-    selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed');
-});
-
-// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
-    const selectedViews = SelectionManager.Views();
-    if (Doc.ActiveTool !== InkTool.None) {
-        if (checkResult) {
-            return ActiveFillColor();
-        }
-        SetActiveFillColor(color ?? 'transparent');
-    } else if (selectedViews.length) {
-        if (checkResult) {
-            const selView = selectedViews.lastElement();
-            const fieldKey = selView.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
-            const layoutFrameNumber = Cast(selView.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
-            const contentFrameNumber = Cast(selView.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
-            return CollectionFreeFormDocumentView.getStringValues(selView?.rootDoc, contentFrameNumber)[fieldKey] ?? 'transparent';
-        }
-        selectedViews.forEach(dv => {
-            const fieldKey = dv.rootDoc.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
-            const layoutFrameNumber = Cast(dv.props.docViewPath().lastElement()?.rootDoc?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
-            const contentFrameNumber = Cast(dv.rootDoc?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
-            if (contentFrameNumber !== undefined) {
-                CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color });
-            } else {
-                dv.rootDoc['_' + fieldKey] = color;
-            }
-        });
-    } else {
-        const selected = SelectionManager.Docs().length ? SelectionManager.Docs() : LinkManager.currentLink ? [LinkManager.currentLink] : [];
-        if (checkResult) {
-            return selected.lastElement()?._backgroundColor ?? 'transparent';
-        }
-        selected.forEach(doc => (doc._backgroundColor = color));
-    }
-});
-
-// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
-    if (checkResult) {
-        return Doc.SharingDoc().userColor;
-    }
-    Doc.SharingDoc().userColor = undefined;
-    Doc.GetProto(Doc.SharingDoc()).userColor = color;
-    Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'author_date');
-});
-
-// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    if (checkResult) {
-        if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE;
-        return 'transparent';
-    }
-    selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
-});
-
-ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
-    // prettier-ignore
-    const map: Map<'flashcards' | 'grid' | 'snaplines' | 'clusters' | 'arrange'| 'viewAll', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([
-        ['grid', {
-            checkResult: (doc:Doc) => doc._freeform_backgroundGrid,
-            setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
-        }],
-        ['snaplines', {
-            checkResult: (doc:Doc) => doc._freeform_snapLines,
-            setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines,
-        }],
-        ['viewAll', {
-            checkResult: (doc:Doc) => doc._freeform_fitContentsToBox,
-            setDoc: (doc:Doc) => doc._freeform_fitContentsToBox = !doc._freeform_fitContentsToBox,
-        }],
-        ['clusters', {
-            waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
-            checkResult: (doc:Doc) => doc._freeform_useClusters,
-            setDoc: (doc:Doc) => doc._freeform_useClusters = !doc._freeform_useClusters,
-        }],
-        ['arrange', {
-            waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
-            checkResult: (doc:Doc) => doc._autoArrange,
-            setDoc: (doc:Doc) => doc._autoArrange = !doc._autoArrange,
-        }],
-        ['flashcards', {
-            checkResult: (doc:Doc) => Doc.UserDoc().defaultToFlashcards,
-            setDoc: (doc:Doc) => Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards,
-        }],
-   ]);
-
-    if (checkResult) {
-        return map.get(attr)?.checkResult(selected) ? Colors.MEDIUM_BLUE : 'transparent';
-    }
-    const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} };
-    SelectionManager.Docs().map(dv => map.get(attr)?.setDoc(dv));
-    setTimeout(() => batch.end(), 100);
-});
-ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize', value: any, checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-    const selected = SelectionManager.Docs().lastElement();
-    // prettier-ignore
-    const map: Map<'font'|'fontColor'|'highlight'|'fontSize', { checkResult: () => any; setDoc: () => void;}> = new Map([
-        ['font', {
-            checkResult: () => RichTextMenu.Instance?.fontFamily,
-            setDoc: () => value && RichTextMenu.Instance.setFontFamily(value),
-        }],
-        ['highlight', {
-            checkResult: () =>(selected ?? Doc.UserDoc())._fontHighlight,
-            setDoc: () => value  && RichTextMenu.Instance.setHighlight(value),
-        }],
-        ['fontColor', {
-            checkResult: () => RichTextMenu.Instance?.fontColor,
-            setDoc: () => value && RichTextMenu.Instance.setColor(value),
-        }],
-        ['fontSize', {
-            checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
-            setDoc: () => {
-                if (typeof value === 'number') value = value.toString();
-                if (value && Number(value).toString() === value) value += 'px';
-                RichTextMenu.Instance.setFontSize(value);
-            },
-        }],
-    ]);
-
-    if (checkResult) {
-        return map.get(attr)?.checkResult();
-    }
-    map.get(attr)?.setDoc?.();
-});
-
-type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'underline' | 'left' | 'center' | 'right' | 'bullet' | 'decimal';
-type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }];
-ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) {
-    const textView = RichTextMenu.Instance?.TextView;
-    const editorView = textView?.EditorView;
-    // prettier-ignore
-    const alignments:attrfuncs[] = (['left','right','center'] as ("left"|"center"|"right")[]).map((where) =>
-        [ where, {      checkResult: () =>(editorView ? (RichTextMenu.Instance.textAlign ===where): (Doc.UserDoc().textAlign ===where) ? true:false),
-                        toggle: () => (editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, where):(Doc.UserDoc().textAlign = where))}]);
-    // prettier-ignore
-    const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
-        [ list, {       checkResult: () => (editorView ? RichTextMenu.Instance.getActiveListStyle() === list:false),
-                        toggle: () => editorView?.state && RichTextMenu.Instance.changeListType(list) }]);
-    // prettier-ignore
-    const attrs:attrfuncs[] = [
-        ['dictation', { checkResult: () => textView?._recording ? true:false,
-                        toggle: () => textView && runInAction(() => (textView._recording = !textView._recording)) }],
-        ['noAutoLink',{ checkResult: () => (editorView ? RichTextMenu.Instance.noAutoLink : false),
-                        toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}],
-        ['bold',      { checkResult: () => (editorView ? RichTextMenu.Instance.bold : (Doc.UserDoc().fontWeight === 'bold') ? true:false),
-                        toggle: editorView ? RichTextMenu.Instance.toggleBold : () => (Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold')}],
-        ['italics',   { checkResult: () => (editorView ? RichTextMenu.Instance.italics : (Doc.UserDoc().fontStyle === 'italics') ? true:false),
-                        toggle: editorView ? RichTextMenu.Instance.toggleItalics : () => (Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics')}],
-        ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance.underline : (Doc.UserDoc().textDecoration === 'underline') ? true:false),
-                        toggle: editorView ? RichTextMenu.Instance.toggleUnderline : () => (Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline') }]]
-
-    const map = new Map(attrs.concat(alignments).concat(listings));
-    if (checkResult) return map.get(charStyle)?.checkResult() ? Colors.MEDIUM_BLUE : 'transparent';
-    map.get(charStyle)?.toggle();
-});
-
-export function checkInksToGroup() {
-    // console.log("getting here to inks group");
-    if (Doc.ActiveTool === InkTool.Write) {
-        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
-            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
-            // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
-            const inksToGroup = ffView.unprocessedDocs.filter(inkDoc => {
-                // console.log(inkDoc.x, inkDoc.y);
-            });
-        });
-    }
-}
-
-export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
-    // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (Doc.ActiveTool === InkTool.Write) {
-        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
-            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
-            const selected = ffView.unprocessedDocs;
-            // loop through selected an get the bound
-            const bounds: { x: number; y: number; width?: number; height?: number }[] = [];
-
-            selected.map(
-                action(d => {
-                    const x = NumCast(d.x);
-                    const y = NumCast(d.y);
-                    const width = d[Width]();
-                    const height = d[Height]();
-                    bounds.push({ x, y, width, height });
-                })
-            );
-
-            const aggregBounds = aggregateBounds(bounds, 0, 0);
-            const marqViewRef = ffView._marqueeViewRef.current;
-
-            // set the vals for bounds in marqueeView
-            if (marqViewRef) {
-                marqViewRef._downX = aggregBounds.x;
-                marqViewRef._downY = aggregBounds.y;
-                marqViewRef._lastX = aggregBounds.r;
-                marqViewRef._lastY = aggregBounds.b;
-            }
-
-            selected.map(
-                action(d => {
-                    const dx = NumCast(d.x);
-                    const dy = NumCast(d.y);
-                    delete d.x;
-                    delete d.y;
-                    delete d.activeFrame;
-                    delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                    delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                    // calculate pos based on bounds
-                    if (marqViewRef?.Bounds) {
-                        d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
-                        d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
-                    }
-                    return d;
-                })
-            );
-            ffView.props.removeDocument?.(selected);
-            // TODO: nda - this is the code to actually get a new grouped collection
-            const newCollection = marqViewRef?.getCollection(selected, undefined, true);
-            if (newCollection) {
-                newCollection.height = newCollection[Height]();
-                newCollection.width = newCollection[Width]();
-            }
-
-            // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-            newCollection && ffView.props.addDocument?.(newCollection);
-            // TODO: nda - will probably need to go through and only remove the unprocessed selected docs
-            ffView.unprocessedDocs = [];
-
-            InkTranscription.Instance.transcribeInk(newCollection, selected, false);
-        });
-    }
-    CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
-}
-
-function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean, checkResult?: boolean) {
-    InkTranscription.Instance?.createInkGroup();
-    if (checkResult) {
-        return (Doc.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool
-            ? GestureOverlay.Instance?.KeepPrimitiveMode || ![GestureUtils.Gestures.Circle, GestureUtils.Gestures.Line, GestureUtils.Gestures.Rectangle].includes(tool as GestureUtils.Gestures)
-                ? Colors.MEDIUM_BLUE
-                : Colors.MEDIUM_BLUE_ALT
-            : 'transparent';
-    }
-    runInAction(() => {
-        if (GestureOverlay.Instance) {
-            GestureOverlay.Instance.KeepPrimitiveMode = keepPrim;
-        }
-        if (Object.values(GestureUtils.Gestures).includes(tool as any)) {
-            if (GestureOverlay.Instance.InkShape === tool && !keepPrim) {
-                Doc.ActiveTool = InkTool.None;
-                GestureOverlay.Instance.InkShape = undefined;
-            } else {
-                Doc.ActiveTool = InkTool.Pen;
-                GestureOverlay.Instance.InkShape = tool as GestureUtils.Gestures;
-            }
-        } else if (tool) {
-            // pen or eraser
-            if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape && !keepPrim) {
-                Doc.ActiveTool = InkTool.None;
-            } else {
-                Doc.ActiveTool = tool as any;
-                GestureOverlay.Instance.InkShape = undefined;
-            }
-        } else {
-            Doc.ActiveTool = InkTool.None;
-        }
-    });
-}
-
-ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode');
-
-// toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
-    // prettier-ignore
-    const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
-        ['inkMask', {
-            checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.stroke_isInkMask) : ActiveIsInkMask()) ? Colors.MEDIUM_BLUE : 'transparent'),
-            setInk: (doc: Doc) => (doc.stroke_isInkMask = !doc.stroke_isInkMask),
-            setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()),
-        }],
-        ['fillColor', {
-            checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.fillColor) : ActiveFillColor() ?? "transparent"),
-            setInk: (doc: Doc) => (doc.fillColor = StrCast(value)),
-            setMode: () => SetActiveFillColor(StrCast(value)),
-        }],
-        [ 'strokeWidth', {
-            checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.stroke_width) : ActiveInkWidth()),
-            setInk: (doc: Doc) => (doc.stroke_width = NumCast(value)),
-            setMode: () => SetActiveInkWidth(value.toString()),
-        }],
-        ['strokeColor', {
-            checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.color) : ActiveInkColor()),
-            setInk: (doc: Doc) => (doc.color = String(value)),
-            setMode: () => SetActiveInkColor(StrCast(value)),
-        }],
-    ]);
-
-    if (checkResult) {
-        return map.get(option)?.checkResult();
-    }
-    map.get(option)?.setMode();
-    SelectionManager.Docs()
-        .filter(doc => doc.type === DocumentType.INK)
-        .map(doc => map.get(option)?.setInk(doc));
-});
-
-/** WEB
- *      webSetURL
- **/
-ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement();
-    if (selected?.rootDoc.type === DocumentType.WEB) {
-        if (checkResult) {
-            return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href);
-        }
-        selected.ComponentView?.setData?.(url);
-        //selected.rootDoc.data = new WebField(url);
-    }
-});
-ScriptingGlobals.add(function webForward(checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox;
-    if (checkResult) {
-        return selected?.forward(checkResult) ? undefined : 'lightGray';
-    }
-    selected?.forward();
-});
-ScriptingGlobals.add(function webBack(checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox;
-    if (checkResult) {
-        return selected?.back(checkResult) ? undefined : 'lightGray';
-    }
-    selected?.back();
-});
-
-/** Schema
- *      toggleSchemaPreview
- **/
-ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
-    if (checkResult && selected) {
-        const result: boolean = NumCast(selected.schema_previewWidth) > 0;
-        if (result) return Colors.MEDIUM_BLUE;
-        else return 'transparent';
-    } else if (selected) {
-        if (NumCast(selected.schema_previewWidth) > 0) {
-            selected.schema_previewWidth = 0;
-        } else {
-            selected.schema_previewWidth = 200;
-        }
-    }
-});
-ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
-    if (checkResult && selected) {
-        return NumCast(selected._schema_singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent';
-    }
-    if (selected) {
-        selected._schema_singleLine = !selected._schema_singleLine;
-    }
-});
-
-/** STACK
- *      groupBy
- */
-ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
-    SelectionManager.Docs().map(doc => (doc._text_fontFamily = key));
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    if (checkResult) {
-        return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-    }
-    if (editorView) RichTextMenu.Instance.setFontFamily(key);
-    else Doc.UserDoc().fontFamily = key;
-});
diff --git a/src/client/views/nodes/button/textButton/TextButton.tsx b/src/client/views/nodes/button/textButton/TextButton.tsx
deleted file mode 100644
index 5d7d55863..000000000
--- a/src/client/views/nodes/button/textButton/TextButton.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import React, { Component } from 'react';
-import { BoolCast } from '../../../../../fields/Types';
-import { IButtonProps } from '../ButtonInterface';
-
-export class TextButton extends Component<IButtonProps> {
-    render() {
-        const type = this.props.type;
-        // Determine the type of toggle button
-        const buttonText: boolean = BoolCast(this.props.rootDoc.switchToggle);
-
-        return (
-            <div
-                className={`menuButton ${this.props.type}`}
-                style={{
-                    opacity: 1,
-                    backgroundColor: this.props.backgroundColor,
-                    color: this.props.color,
-                }}
-            >
-                <FontAwesomeIcon
-                    className={`fontIconBox-icon-${this.props.type}`}
-                    icon={this.props.icon}
-                    color={this.props.color}
-                />
-                {this.props.label}
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/nodes/button/textButton/index.ts b/src/client/views/nodes/button/textButton/index.ts
deleted file mode 100644
index 01d62eb7e..000000000
--- a/src/client/views/nodes/button/textButton/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './TextButton';
\ No newline at end of file
diff --git a/src/client/views/nodes/button/toggleButton/ToggleButton.tsx b/src/client/views/nodes/button/toggleButton/ToggleButton.tsx
deleted file mode 100644
index dca6487d8..000000000
--- a/src/client/views/nodes/button/toggleButton/ToggleButton.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import React, { Component } from 'react';
-import { BoolCast } from '../../../../../fields/Types';
-import { Colors } from '../../../global/globalEnums';
-import { IButtonProps } from '../ButtonInterface';
-
-export class ToggleButton extends Component<IButtonProps> {
-    render() {
-        const type = this.props.type;
-        // Determine the type of toggle button
-        const switchToggle: boolean = BoolCast(this.props.rootDoc.switchToggle);
-
-        if (switchToggle) {
-            return (
-                <div className={`menuButton ${type} ${'switch'}`}>
-                    <label className="switch">
-                        <input type="checkbox"
-                            checked={this.props.backgroundColor === Colors.MEDIUM_BLUE}
-                        />
-                        <span className="slider round"></span>
-                    </label>
-                </div>
-            );
-        } else {
-            return (
-                <div className={`menuButton ${type}`}
-                    style={{ opacity: 1, backgroundColor: this.props.backgroundColor, color: this.props.color }}>
-                    <FontAwesomeIcon className={`fontIconBox-icon-${type}`} icon={this.props.icon} color={this.props.color} />
-                    {this.props.label}
-                </div>
-            );
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/button/toggleButton/index.ts b/src/client/views/nodes/button/toggleButton/index.ts
deleted file mode 100644
index cdb9c527c..000000000
--- a/src/client/views/nodes/button/toggleButton/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './ToggleButton';
\ 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 ef1c3911c..56af67802 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1025,7 +1025,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     };
-    childLayoutTemplate = () => (!this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement));
+    childLayoutTemplate = () => (!this.isTreeOrStack ? DocCast(Doc.UserDoc().presElement) : 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
     panelHeight = () => this.props.PanelHeight() - 40;
-- 
cgit v1.2.3-70-g09d2


From 9e5d835bf7d3415ec24fa8e79c18a2fece6a6ba7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 12 Jul 2023 00:10:38 -0400
Subject: from last

---
 src/client/views/Main.tsx | 1 -
 1 file changed, 1 deletion(-)

(limited to 'src/client/views/Main.tsx')

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index ed39cde13..d4db5e3e8 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -46,7 +46,6 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure
         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();
         new PingManager();
-- 
cgit v1.2.3-70-g09d2


From 4c8eee9811abd072d2a6adfe24eaf04f980ccf21 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 9 Aug 2023 15:21:20 -0400
Subject: updated file system to include recentlyClosed, Shared, and Dashboards
 and fixed drag drop to make sense for the filesystem.   Fixed loading
 documents to happen in one batch by fixing UPDATE_CACHED_DOCS to save only
 documents accessible from current dashboard.

---
 src/Utils.ts                                       |   2 +-
 src/client/DocServer.ts                            |  60 +-
 src/client/documents/DocumentTypes.ts              |   4 -
 src/client/documents/Documents.ts                  |  12 +-
 src/client/util/CurrentUserUtils.ts                |  37 +-
 src/client/util/DocumentManager.ts                 |   5 +-
 src/client/util/DragManager.ts                     |   4 +-
 src/client/util/GroupManager.tsx                   |   5 +-
 src/client/util/GroupMemberView.tsx                |   3 +-
 src/client/util/LinkManager.ts                     |   4 +-
 src/client/util/ReplayMovements.ts                 |   1 -
 src/client/util/ServerStats.tsx                    |  26 +-
 src/client/util/SettingsManager.tsx                |  11 +-
 src/client/util/SharingManager.tsx                 |  11 +-
 src/client/util/reportManager/ReportManager.tsx    |   5 +-
 src/client/views/DashboardView.tsx                 |  83 +--
 src/client/views/DocComponent.tsx                  |   4 +-
 src/client/views/FilterPanel.tsx                   |  18 +-
 src/client/views/LightboxView.tsx                  |   4 +-
 src/client/views/Main.tsx                          |   5 +-
 src/client/views/MainView.tsx                      |  12 +-
 src/client/views/PropertiesButtons.tsx             |   1 -
 src/client/views/PropertiesDocContextSelector.tsx  |   1 -
 src/client/views/PropertiesSection.tsx             |  80 ++-
 src/client/views/PropertiesView.tsx                | 758 ++++++++++-----------
 src/client/views/StyleProvider.tsx                 |   8 +-
 src/client/views/UndoStack.tsx                     |  43 +-
 .../views/collections/CollectionDockingView.tsx    |   7 +-
 src/client/views/collections/CollectionMenu.tsx    |   2 +-
 .../views/collections/CollectionStackingView.tsx   |   1 -
 .../views/collections/CollectionTreeView.tsx       |   1 +
 src/client/views/collections/TabDocView.tsx        |   2 +-
 src/client/views/collections/TreeView.tsx          |   2 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |   1 -
 src/client/views/global/globalScripts.ts           |   1 -
 .../views/newlightbox/ExploreView/ExploreView.tsx  |   3 -
 .../components/Recommendation/Recommendation.tsx   |   1 -
 src/client/views/nodes/DataVizBox/utils/D3Utils.ts |   1 -
 src/client/views/nodes/DocumentView.tsx            |   3 +-
 src/client/views/nodes/LinkDocPreview.tsx          |   2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   1 -
 src/client/views/nodes/ScreenshotBox.tsx           |   5 +-
 src/client/views/nodes/ScriptingBox.tsx            |   2 -
 .../views/nodes/formattedText/FormattedTextBox.tsx |  20 +-
 .../views/nodes/formattedText/RichTextRules.ts     |  26 +-
 src/client/views/pdf/AnchorMenu.tsx                |   5 +-
 src/client/views/search/SearchBox.tsx              |  78 ++-
 src/client/views/topbar/TopBar.tsx                 |  38 +-
 src/fields/Doc.ts                                  |  81 ++-
 src/fields/FieldLoader.tsx                         |   5 +-
 src/server/DashUploadUtils.ts                      |   3 +-
 51 files changed, 727 insertions(+), 771 deletions(-)

(limited to 'src/client/views/Main.tsx')

diff --git a/src/Utils.ts b/src/Utils.ts
index 8b9fe2aab..7f83ab8f5 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -76,7 +76,7 @@ export namespace Utils {
             });
             return returnedUri;
         } catch (e) {
-            console.log('VideoBox :' + e);
+            console.log('ConvertDataURI :' + e);
         }
     }
 
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index ad5a73598..40979d631 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,16 +1,18 @@
 import { runInAction } from 'mobx';
 import * as rp from 'request-promise';
 import * as io from 'socket.io-client';
-import { Doc, Opt } from '../fields/Doc';
+import { Doc, DocListCast, Opt } from '../fields/Doc';
 import { UpdatingFromServer } from '../fields/DocSymbols';
 import { FieldLoader } from '../fields/FieldLoader';
 import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols';
 import { ObjectField } from '../fields/ObjectField';
 import { RefField } from '../fields/RefField';
-import { StrCast } from '../fields/Types';
+import { DocCast, StrCast } from '../fields/Types';
 import MobileInkOverlay from '../mobile/MobileInkOverlay';
 import { emptyFunction, Utils } from '../Utils';
 import { GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message';
+import { DocumentType } from './documents/DocumentTypes';
+import { LinkManager } from './util/LinkManager';
 import { SerializationHelper } from './util/SerializationHelper';
 import { GestureOverlay } from './views/GestureOverlay';
 
@@ -30,35 +32,44 @@ import { GestureOverlay } from './views/GestureOverlay';
 export namespace DocServer {
     let _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
 
-    export function QUERY_SERVER_CACHE(title: string) {
+    export function FindDocByTitle(title: string) {
         const foundDocId = Array.from(Object.keys(_cache))
             .filter(key => _cache[key] instanceof Doc)
             .find(key => (_cache[key] as Doc).title === title);
 
         return foundDocId ? (_cache[foundDocId] as Doc) : undefined;
     }
-    let lastCacheUpdate = 0;
-    export function UPDATE_SERVER_CACHE(print: boolean = false) {
-        if (print) {
-            const strings: string[] = [];
-            Array.from(Object.keys(_cache)).forEach(key => {
-                const doc = _cache[key];
-                if (doc instanceof Doc) strings.push(StrCast(doc.author) + ' ' + StrCast(doc.title) + ' ' + StrCast(Doc.GetT(doc, 'title', 'string', true)));
-            });
-            strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
-        }
-        const filtered = Array.from(Object.keys(_cache)).filter(key => {
-            const doc = _cache[key] as Doc;
-            return true;
-            if (!(StrCast(doc.author).includes('.edu') || StrCast(doc.author).includes('.com')) || doc.author === Doc.CurrentUserEmail) return true;
-            return false;
+    let cacheDocumentIds = ''; // ; separate string of all documents ids in the user's working set (cached on the server)
+    export let CacheNeedsUpdate = false;
+    export function UPDATE_SERVER_CACHE() {
+        const prototypes = Object.values(DocumentType)
+            .filter(type => type !== DocumentType.NONE)
+            .map(type => _cache[type + 'Proto'])
+            .filter(doc => doc instanceof Doc)
+            .map(doc => doc as Doc);
+        const references = new Set<Doc>(prototypes);
+        Doc.FindReferences(Doc.UserDoc(), references, undefined);
+        DocListCast(DocCast(Doc.UserDoc().myLinkDatabase).data).forEach(link => {
+            if (!references.has(DocCast(link.link_anchor_1)) && !references.has(DocCast(link.link_anchor_2))) {
+                Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myLinkDatabase), 'data', link);
+            }
         });
-        if (filtered.length === lastCacheUpdate) return;
-        lastCacheUpdate = filtered.length;
+        LinkManager.userLinkDBs.forEach(linkDb => Doc.FindReferences(linkDb, references, undefined));
+        const filtered = Array.from(references);
+
+        const newCacheUpdate = filtered.map(doc => doc[Id]).join(';');
+        if (newCacheUpdate === cacheDocumentIds) return;
+        cacheDocumentIds = newCacheUpdate;
+
+        // print out cached docs
+        console.log('Set cached docs = ');
+        const is_filtered = filtered.filter(doc => !Doc.IsSystem(doc));
+        const strings = is_filtered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
+        strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
 
         rp.post(Utils.prepend('/setCacheDocumentIds'), {
             body: {
-                cacheDocumentIds: filtered.join(';'),
+                cacheDocumentIds,
             },
             json: true,
         });
@@ -353,7 +364,7 @@ export namespace DocServer {
         // i) which documents need to be fetched
         // ii) which are already in the process of being fetched
         // iii) which already exist in the cache
-        for (const id of ids) {
+        for (const id of ids.filter(id => id)) {
             const cached = _cache[id];
             if (cached === undefined) {
                 defaultPromises.push({
@@ -382,7 +393,7 @@ export namespace DocServer {
             // fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
             // the fields have been returned from the server
             console.log('Requesting ' + requestedIds.length);
-            FieldLoader.active && runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length));
+            setTimeout(() => runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length)));
             const serializedFields = await Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
 
             // 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
@@ -392,7 +403,7 @@ export namespace DocServer {
             console.log('deserializing ' + serializedFields.length + ' fields');
             for (const field of serializedFields) {
                 processed++;
-                if (FieldLoader.active && processed % 150 === 0) {
+                if (processed % 150 === 0) {
                     runInAction(() => (FieldLoader.ServerLoadStatus.retrieved = processed));
                     await new Promise(res => setTimeout(res)); // force loading to yield to splash screen rendering to update progress
                 }
@@ -478,6 +489,7 @@ export namespace DocServer {
      * @param field the [RefField] to be serialized and sent to the server to be stored in the database
      */
     export function CreateField(field: RefField) {
+        CacheNeedsUpdate = true;
         _CreateField(field);
     }
 
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index b629d9b8c..1d0ddce40 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -14,13 +14,11 @@ export enum DocumentType {
     INK = 'ink',
     SCREENSHOT = 'screenshot',
     FONTICON = 'fonticonbox',
-    FILTER = 'filter',
     SEARCH = 'search', // search query
     LABEL = 'label', // simple text label
     BUTTON = 'button', // onClick button
     WEBCAM = 'webcam', // webcam
     CONFIG = 'config', // configuration document intended to specify a view layout configuration, but not be directly rendered (e.g., for saving the page# of a PDF, or view transform of a collection)
-    DATE = 'date', // calendar view of a date
     SCRIPTING = 'script', // script editor
     EQUATION = 'equation', // equation editor
     FUNCPLOT = 'funcplot', // function plotter
@@ -31,14 +29,12 @@ export enum DocumentType {
 
     // special purpose wrappers that either take no data or are compositions of lower level types
     LINK = 'link',
-    LINKANCHOR = 'linkanchor',
     IMPORT = 'import',
     SLIDER = 'slider',
     PRES = 'presentation',
     PRESELEMENT = 'preselement',
     COLOR = 'color',
     YOUTUBE = 'youtube',
-    SEARCHITEM = 'searchitem',
     COMPARISON = 'comparison',
     GROUP = 'group',
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8eeceaa15..fccad80ee 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,5 +1,5 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { action, runInAction } from 'mobx';
+import { action, reaction, runInAction } from 'mobx';
 import { basename } from 'path';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Field, Opt, updateCachedAcls } from '../../fields/Doc';
@@ -744,6 +744,10 @@ export namespace Docs {
                 // an entry dedicated to the given DocumentType)
                 target && PrototypeMap.set(type, target);
             });
+            reaction(
+                () => (proto => StrCast(proto?.BROADCAST_MESSAGE))(DocServer.GetCachedRefField('rtfProto') as Doc),
+                msg => msg && alert(msg)
+            );
         }
 
         /**
@@ -887,8 +891,6 @@ export namespace Docs {
                 DocUtils.MakeLinkToActiveAudio(() => viewDoc);
             }
 
-            Doc.AddFileOrphan(dataDoc);
-
             updateCachedAcls(dataDoc);
             updateCachedAcls(viewDoc);
 
@@ -1150,9 +1152,6 @@ export namespace Docs {
         export function FontIconDocument(options?: DocumentOptions) {
             return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { ...(options || {}) });
         }
-        export function FilterDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.FILTER), undefined, { ...(options || {}) });
-        }
 
         export function PresElementBoxDocument() {
             return Prototypes.get(DocumentType.PRESELEMENT);
@@ -1872,7 +1871,6 @@ export namespace DocUtils {
     export function copyDragFactory(dragFactory: Doc) {
         if (!dragFactory) return undefined;
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
-        ndoc && Doc.AddFileOrphan(Doc.GetProto(ndoc));
         if (ndoc && dragFactory['dragFactory_count'] !== undefined) {
             dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1;
             Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 21bcec6d1..e8262ff3b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -152,7 +152,7 @@ export class CurrentUserUtils {
             { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
             { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
         const reqdNoteList = reqdTempOpts.map(opts => {
-            const reqdOpts = {...opts, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
+            const reqdOpts = {...opts, isSystem:true, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
             const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
             return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
         });
@@ -339,7 +339,7 @@ export class CurrentUserUtils {
     }
 
     /// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
-    static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] {
+    static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}, hidden?: boolean}[] {
       const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())"; 
       const getActiveDashTrails = "Doc.ActiveDashboard?.myTrails";
         return [
@@ -348,7 +348,7 @@ export class CurrentUserUtils {
             { title: "Files",           toolTip: "Files",           target: this.setupFilesystem(doc, "myFilesystem"),         ignoreClick: true, icon: "folder-open", },
             { title: "Tools",           toolTip: "Tools",           target: this.setupToolsBtnPanel(doc, "myTools"),           ignoreClick: true, icon: "wrench",      funcs: {hidden: "IsNoviceMode()"} },
             { title: "Imports",         toolTip: "Imports ⌘I",      target: this.setupImportSidebar(doc, "myImports"),         ignoreClick:false, icon: "upload",      },
-            { title: "Closed",          toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), ignoreClick: true, icon: "archive",     },
+            { title: "Closed",          toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), ignoreClick: true, icon: "archive",     hidden: true }, // this doc is hidden from the Sidebar, but it's still being used in MyFilesystem which ignores the hidden field
             { title: "Shared",          toolTip: "Shared Docs",     target: Doc.MySharedDocs,                                  ignoreClick: true, icon: "users",       funcs: {badgeValue: badgeValue}},
             { title: "Trails",          toolTip: "Trails ⌘R",       target: Doc.UserDoc(),                                     ignoreClick: true, icon: "pres-trail",  funcs: {target: getActiveDashTrails}},
             { title: "User Doc",        toolTip: "User Doc",        target: this.setupUserDocView(doc, "myUserDocView"),       ignoreClick: true, icon: "address-card",funcs: {hidden: "IsNoviceMode()"} },
@@ -357,17 +357,17 @@ export class CurrentUserUtils {
 
     /// the empty panel that is filled with whichever left menu button's panel has been selected
     static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
-        DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
+        DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {title:"leftSidebarPanel", isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
     }
 
     /// Initializes the left sidebar menu buttons and the panels they open up
     static setupLeftSidebarMenu(doc: Doc, field="myLeftSidebarMenu") {
         this.setupLeftSidebarPanel(doc);
         const myLeftSidebarMenu = DocCast(doc[field]);
-        const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, toolTip, scripts, funcs }) => {
+        const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, toolTip, hidden, scripts, funcs }) => {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
             const reqdBtnOpts:DocumentOptions = {
-                title, icon, target, toolTip, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List<string>(['height', 'data_columnHeaders']), dontRegisterView: true,
+                title, icon, target, toolTip, hidden, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List<string>(['height', 'data_columnHeaders']), dontRegisterView: true,
                 _width: 60, _height: 60,  _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
             };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
@@ -495,7 +495,7 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
             dropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
-            layout_headerButton: newDashboardButton, childDragAction: "embed",
+            layout_headerButton: newDashboardButton, childDragAction: "none",
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
@@ -519,7 +519,6 @@ export class CurrentUserUtils {
     /// initializes the left sidebar File system pane
     static setupFilesystem(doc: Doc, field:string) {
         var myFilesystem = DocCast(doc[field]);
-        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", undoIgnoreFields:new List<string>(['treeViewSortCriterion']), _dragOnlyWithinContainer: true, isSystem: true, isFolder: true });
         
         const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
@@ -530,14 +529,15 @@ export class CurrentUserUtils {
         const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.layout_headerButton), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
         const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
-            title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: "proto", isSystem: true,
+            title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: 'add', isSystem: true,
             isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
             treeViewTruncateTitleWidth: 350, ignoreClick: true, childDragAction: "embed",
             childContextMenuLabels: new List<string>(["Create new folder"]),
             childContextMenuIcons: new List<string>(["plus"]),
             layout_explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
         };
-        myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
+        const fileFolders = new Set(DocListCast(DocCast(doc[field])?.data));
+        myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, Array.from(fileFolders));
         const childContextMenuScripts = [newFolder];
         if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
             myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
@@ -547,8 +547,8 @@ export class CurrentUserUtils {
 
     /// initializes the panel displaying docs that have been recently closed
     static setupRecentlyClosed(doc: Doc, field:string) {
-        const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
-            title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "embed", isSystem: true,
+        const reqdOpts:DocumentOptions = { _layout_showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, isFolder: true,
+            title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "move", isSystem: true,
             treeViewTruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same",
             contextMenuLabels: new List<string>(["Empty recently closed"]),
             contextMenuIcons:new List<string>(["trash"]),
@@ -562,8 +562,6 @@ export class CurrentUserUtils {
             toolTip: "Empty recently closed",};
         DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
 
-        //if (recentlyClosed.layout_headerButton !== clearDocsButton) Doc.GetProto(recentlyClosed).layout_headerButton = clearDocsButton;
-        
         if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
             recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
         }
@@ -777,6 +775,7 @@ export class CurrentUserUtils {
             const linkDocs = new Doc(linkDatabaseId, true);
             linkDocs.title = "LINK DATABASE: " + Doc.CurrentUserEmail;
             linkDocs.author = Doc.CurrentUserEmail;
+            linkDocs.isSystem = true;
             linkDocs.data = new List<Doc>([]);
             linkDocs["acl-Guest"] = SharingPermissions.Augment;
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
@@ -877,11 +876,13 @@ export class CurrentUserUtils {
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
         this.setupDocTemplates(doc); // sets up the template menu of templates
-        this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
+        //this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
         DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, childDocumentsActive:false, dropAction: 'move'}); // drop down panel at top of dashboard for stashing documents
         
+        Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards)
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
+        Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed)
 
         if (doc.activeDashboard instanceof Doc) {
             // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
@@ -889,7 +890,7 @@ export class CurrentUserUtils {
         }
         new LinkManager();
 
-        setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
+        DocServer.CacheNeedsUpdate && setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
         setInterval(DocServer.UPDATE_SERVER_CACHE, 120000);
         return doc;
     }
@@ -923,11 +924,9 @@ export class CurrentUserUtils {
                 {
                     const ids = result.cacheDocumentIds.split(";");
                     const batch = 30000;
-                    FieldLoader.active = true;
                     for (let i = 0; i < ids.length; i = Math.min(ids.length, i+batch)) {
                         await DocServer.GetRefFields(ids.slice(i, i+batch));
                     }
-                    FieldLoader.active = false;
                 }
                 return result;
             } else {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 8e4e0d8f3..42132c2d7 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -75,7 +75,7 @@ export class DocumentManager {
 
     @action
     public AddView = (view: DocumentView) => {
-        //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
+        if (view.props.LayoutTemplateString?.includes(KeyValueBox.name)) return;
         if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
             const viewAnchorIndex = view.props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1';
             const link = view.rootDoc;
@@ -251,6 +251,7 @@ export class DocumentManager {
         options: DocFocusOptions, // options for how to navigate to target
         finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
     ) => {
+        Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
         const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
         if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
         let rootContextView =
@@ -335,7 +336,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe
         if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) {
             DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc));
         } else {
-            const container = DocCast(containingDoc ?? doc.embedContainer ?? doc);
+            const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc));
             const showDoc = !Doc.IsSystem(container) ? container : doc;
             options.toggleTarget = undefined;
             DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 85101fcab..f4ff38515 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,7 +15,7 @@ import { SelectionManager } from './SelectionManager';
 import { SnappingManager } from './SnappingManager';
 import { UndoManager } from './UndoManager';
 
-export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
+export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
 
 /**
  * Initialize drag
@@ -213,6 +213,8 @@ export namespace DragManager {
                                 ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
                                 : docDragData.dropAction === 'embed'
                                 ? Doc.BestEmbedding(d)
+                                : docDragData.dropAction === 'add'
+                                ? d
                                 : docDragData.dropAction === 'proto'
                                 ? Doc.GetProto(d)
                                 : docDragData.dropAction === 'copy'
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index e406d89e7..f35844020 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -16,6 +16,7 @@ import { listSpec } from '../../fields/Schema';
 import { DateField } from '../../fields/DateField';
 import { Id } from '../../fields/FieldSymbols';
 import { Button, IconButton, Size, Type } from 'browndash-components';
+import { SettingsManager } from './SettingsManager';
 
 /**
  * Interface for options for the react-select component
@@ -281,7 +282,7 @@ export class GroupManager extends React.Component<{}> {
      */
     private get groupCreationModal() {
         const contents = (
-            <div className="group-create" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
+            <div className="group-create" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
                 <div className="group-heading" style={{ marginBottom: 0 }}>
                     <p>
                         <b>New Group</b>
@@ -366,7 +367,7 @@ export class GroupManager extends React.Component<{}> {
         const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups;
 
         return (
-            <div className="group-interface" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
+            <div className="group-interface" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
                 {this.groupCreationModal}
                 {this.currentGroup ? <GroupMemberView group={this.currentGroup} onCloseButtonClick={action(() => (this.currentGroup = undefined))} /> : null}
                 <div className="group-heading">
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx
index 057f94f64..535d8ccc2 100644
--- a/src/client/util/GroupMemberView.tsx
+++ b/src/client/util/GroupMemberView.tsx
@@ -9,6 +9,7 @@ import { MainViewModal } from '../views/MainViewModal';
 import { GroupManager, UserOptions } from './GroupManager';
 import './GroupMemberView.scss';
 import { Button, IconButton, Size, Type } from 'browndash-components';
+import { SettingsManager } from './SettingsManager';
 
 interface GroupMemberViewProps {
     group: Doc;
@@ -28,7 +29,7 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> {
         const hasEditAccess = GroupManager.Instance.hasEditAccess(this.props.group);
 
         return !this.props.group ? null : (
-            <div className="editing-interface" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
+            <div className="editing-interface" style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
                 <div className="editing-header">
                     <input
                         className="group-title"
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index c7f092565..ef4b21b05 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,8 @@
-import { action, observable, observe } from 'mobx';
+import { action, observable, observe, runInAction } from 'mobx';
 import { computedFn } from 'mobx-utils';
 import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
 import { DirectLinks } from '../../fields/DocSymbols';
+import { FieldLoader } from '../../fields/FieldLoader';
 import { List } from '../../fields/List';
 import { ProxyField } from '../../fields/Proxy';
 import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
@@ -132,6 +133,7 @@ export class LinkManager {
             },
             true
         );
+        runInAction(() => (FieldLoader.ServerLoadStatus.message = 'links'));
         LinkManager.addLinkDB(Doc.LinkDBDoc());
     }
 
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index cbc465d6a..d99630f82 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -187,7 +187,6 @@ export class ReplayMovements {
                 } else {
                     // tab wasn't open - open it and play the movement
                     const openedColFFView = this.openTab(movement.doc);
-                    console.log('openedColFFView', openedColFFView);
                     openedColFFView && this.zoomAndPan(movement, openedColFFView);
                 }
 
diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
index 6a6ec158e..3c7c35a7e 100644
--- a/src/client/util/ServerStats.tsx
+++ b/src/client/util/ServerStats.tsx
@@ -6,6 +6,7 @@ import './SharingManager.scss';
 import { PingManager } from './PingManager';
 import { StrCast } from '../../fields/Types';
 import { Doc } from '../../fields/Doc';
+import { SettingsManager } from './SettingsManager';
 
 @observer
 export class ServerStats extends React.Component<{}> {
@@ -42,21 +43,22 @@ export class ServerStats extends React.Component<{}> {
      */
     @computed get sharingInterface() {
         return (
-            <div style={{
-                display: "flex",
-                height: 300,
-                width: 400,
-                background: StrCast(Doc.UserDoc().userBackgroundColor),
-                opacity: 0.6}}>
-                    <div style={{width: 300,height: 100,margin: "auto",display: "flex",flexDirection: "column"}}>
-                        {PingManager.Instance.IsBeating ? 'The server connection is active' :
-                            'The server connection has been interrupted.NOTE: Any changes made will appear to persist but will be lost after a browser refreshes.'}
-                          
-                    <br/>
+            <div
+                style={{
+                    display: 'flex',
+                    height: 300,
+                    width: 400,
+                    background: SettingsManager.Instance?.userBackgroundColor,
+                    opacity: 0.6,
+                }}>
+                <div style={{ width: 300, height: 100, margin: 'auto', display: 'flex', flexDirection: 'column' }}>
+                    {PingManager.Instance.IsBeating ? 'The server connection is active' : 'The server connection has been interrupted.NOTE: Any changes made will appear to persist but will be lost after a browser refreshes.'}
+
+                    <br />
                     <span>Active users:{this._stats?.socketMap.length}</span>
                     {this._stats?.socketMap.map((user: any) => (
                         <p>{user.username}</p>
-                    ))}  
+                    ))}
                 </div>
             </div>
         );
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index b8e327968..573900825 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,10 +1,11 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { ColorState, SketchPicker } from 'react-color';
+import { BsGoogle } from 'react-icons/bs';
+import { FaFillDrip, FaPalette } from 'react-icons/fa';
 import { Doc } from '../../fields/Doc';
-import { Id } from '../../fields/FieldSymbols';
 import { BoolCast, Cast, StrCast } from '../../fields/Types';
 import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
@@ -12,13 +13,9 @@ import { DocServer } from '../DocServer';
 import { Networking } from '../Network';
 import { MainViewModal } from '../views/MainViewModal';
 import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
-import { DragManager } from './DragManager';
 import { GroupManager } from './GroupManager';
 import './SettingsManager.scss';
 import { undoBatch } from './UndoManager';
-import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { BsGoogle } from 'react-icons/bs';
-import { FaFillDrip, FaPalette } from 'react-icons/fa';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -39,6 +36,7 @@ export enum freeformScrollMode {
 
 @observer
 export class SettingsManager extends React.Component<{}> {
+    static Version = 'v0.5';
     public static Instance: SettingsManager;
     static _settingsStyle = addStyleSheet();
     @observable public isOpen = false;
@@ -452,6 +450,7 @@ export class SettingsManager extends React.Component<{}> {
                     </div>
 
                     <div className="settings-user">
+                        <div style={{ color: 'black' }}>{SettingsManager.Version}</div>
                         <div className="settings-username" style={{ color: this.userBackgroundColor }}>
                             {Doc.CurrentUserEmail}
                         </div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index cadcb1f8a..6171c01d7 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -8,6 +8,7 @@ import Select from 'react-select';
 import * as RequestPromise from 'request-promise';
 import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols';
+import { FieldLoader } from '../../fields/FieldLoader';
 import { Id } from '../../fields/FieldSymbols';
 import { StrCast } from '../../fields/Types';
 import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
@@ -23,6 +24,7 @@ import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
 import { LinkManager } from './LinkManager';
 import { SelectionManager } from './SelectionManager';
+import { SettingsManager } from './SettingsManager';
 import './SharingManager.scss';
 import { undoable } from './UndoManager';
 
@@ -136,6 +138,7 @@ export class SharingManager extends React.Component<{}> {
             this.populating = true;
             const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
             const raw = (JSON.parse(userList) as User[]).filter(user => user.email !== 'guest' && user.email !== Doc.CurrentUserEmail);
+            runInAction(() => (FieldLoader.ServerLoadStatus.message = 'users'));
             const docs = await DocServer.GetRefFields(raw.reduce((list, user) => [...list, user.sharingDocumentId, user.linkDatabaseId], [] as string[]));
             raw.map(
                 action((newUser: User) => {
@@ -144,7 +147,7 @@ export class SharingManager extends React.Component<{}> {
                     if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
                         if (!this.users.find(user => user.user.email === newUser.email)) {
                             this.users.push({ user: newUser, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) });
-                            LinkManager.addLinkDB(linkDatabase);
+                            //LinkManager.addLinkDB(linkDatabase);
                         }
                     }
                 })
@@ -525,10 +528,10 @@ export class SharingManager extends React.Component<{}> {
             const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
 
             return !permissions ? null : (
-                <div key={groupKey} className={'container'} style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
+                <div key={groupKey} className={'container'} style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}>
                     <div className={'padding'}>{StrCast(group.title)}</div>
                     &nbsp;
-                    {group instanceof Doc ? <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null}
+                    {group instanceof Doc ? <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={SettingsManager.Instance.userColor} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null}
                     <div className={'edit-actions'}>
                         {admin || this.myDocAcls ? (
                             <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}>
@@ -550,7 +553,7 @@ export class SharingManager extends React.Component<{}> {
                 <div
                     className="sharing-contents"
                     style={{
-                        background: StrCast(Doc.UserDoc().userBackgroundColor),
+                        background: SettingsManager.Instance.userBackgroundColor,
                         color: StrCast(Doc.UserDoc().userColor),
                     }}>
                     <p className="share-title" style={{ color: StrCast(Doc.UserDoc().userColor) }}>
diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx
index e684bd637..7aad0f2b1 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -19,6 +19,7 @@ import { BugType, FileData, Priority, ReportForm, ViewState, bugDropdownItems, d
 import { Filter, FormInput, FormTextArea, IssueCard, IssueView, Tag } from './ReportManagerComponents';
 import { StrCast } from '../../../fields/Types';
 import { MdRefresh } from 'react-icons/md';
+import { SettingsManager } from '../SettingsManager';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -213,11 +214,11 @@ export class ReportManager extends React.Component<{}> {
      * @returns the component that dispays all issues
      */
     private viewIssuesComponent = () => {
-        const darkMode = isDarkMode(StrCast(Doc.UserDoc().userBackgroundColor));
+        const darkMode = isDarkMode(SettingsManager.Instance.userBackgroundColor);
         const colors = darkMode ? darkColors : lightColors;
 
         return (
-            <div className="view-issues" style={{ backgroundColor: StrCast(Doc.UserDoc().userBackgroundColor), color: colors.text }}>
+            <div className="view-issues" style={{ backgroundColor: SettingsManager.Instance.userBackgroundColor, color: colors.text }}>
                 <div className="left" style={{ display: this.rightExpanded ? 'none' : 'flex' }}>
                     <div className="report-header">
                         <h2 style={{ color: colors.text }}>Open Issues</h2>
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index ae55c8ebf..21808d6e0 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -43,7 +43,7 @@ export class DashboardView extends React.Component {
     @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
 
     @observable private newDashboardName: string | undefined = undefined;
-    @observable private newDashboardColor: string | undefined = "#AFAFAF";
+    @observable private newDashboardColor: string | undefined = '#AFAFAF';
     @action abortCreateNewDashboard = () => {
         this.newDashboardName = undefined;
     };
@@ -100,30 +100,15 @@ export class DashboardView extends React.Component {
         const dashboardCount = DocListCast(Doc.MyDashboards.data).length + 1;
         const placeholder = `Dashboard ${dashboardCount}`;
         return (
-            <div className="new-dashboard"
-                style={{ 
+            <div
+                className="new-dashboard"
+                style={{
                     background: StrCast(Doc.UserDoc().userBackgroundColor),
-                    color: StrCast(Doc.UserDoc().userColor)
-                }}
-            >
+                    color: StrCast(Doc.UserDoc().userColor),
+                }}>
                 <div className="header">Create New Dashboard</div>
-                <EditableText 
-                    formLabel='Title'
-                    placeholder={placeholder}
-                    type={Type.SEC}
-                    color={StrCast(Doc.UserDoc().userColor)}
-                    setVal={val => this.setNewDashboardName(val as string)}
-                    fillWidth
-                />
-                <ColorPicker
-                    formLabel='Background'
-                    colorPickerType='github'
-                    type={Type.TERT}
-                    selectedColor={this.newDashboardColor}
-                    setSelectedColor={color => {
-                        this.setNewDashboardColor(color);
-                    }}
-                />
+                <EditableText formLabel="Title" placeholder={placeholder} type={Type.SEC} color={StrCast(Doc.UserDoc().userColor)} setVal={val => this.setNewDashboardName(val as string)} fillWidth />
+                <ColorPicker formLabel="Background" colorPickerType="github" type={Type.TERT} selectedColor={this.newDashboardColor} setSelectedColor={this.setNewDashboardColor} />
                 <div className="button-bar">
                     <Button text="Cancel" color={StrCast(Doc.UserDoc().userColor)} onClick={this.abortCreateNewDashboard} />
                     <Button type={Type.TERT} text="Create" color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.createNewDashboard(this.newDashboardName!, this.newDashboardColor)} />
@@ -165,21 +150,14 @@ export class DashboardView extends React.Component {
     };
 
     render() {
-        const color = StrCast(Doc.UserDoc().userColor)
-        const variant = StrCast(Doc.UserDoc().userVariantColor)
+        const color = StrCast(Doc.UserDoc().userColor);
+        const variant = StrCast(Doc.UserDoc().userVariantColor);
         return (
             <>
                 <div className="dashboard-view">
                     <div className="left-menu">
-                        <Button 
-                            text={'My Dashboards'}
-                            active={this.selectedDashboardGroup === DashboardGroup.MyDashboards}
-                            color={color}
-                            align={'flex-start'}
-                            onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}
-                            fillWidth
-                        />
-                        <Button 
+                        <Button text={'My Dashboards'} active={this.selectedDashboardGroup === DashboardGroup.MyDashboards} color={color} align={'flex-start'} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)} fillWidth />
+                        <Button
                             text={'Shared Dashboards' + ' (' + this.getDashboards(DashboardGroup.SharedDashboards).length + ')'}
                             active={this.selectedDashboardGroup === DashboardGroup.SharedDashboards}
                             color={this.getDashboards(DashboardGroup.SharedDashboards).some(dash => !DocListCast(Doc.MySharedDocs.viewed).includes(dash)) ? 'green' : color}
@@ -196,11 +174,11 @@ export class DashboardView extends React.Component {
                                 .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Guest'].includes(key))
                                 .some(key => dashboard[DocAcl][key] !== AclPrivate);
                             return (
-                                <div 
-                                    className="dashboard-container" 
-                                    key={dashboard[Id]} 
+                                <div
+                                    className="dashboard-container"
+                                    key={dashboard[Id]}
                                     style={{ background: this.isUnviewedSharedDashboard(dashboard) && this.selectedDashboardGroup === DashboardGroup.SharedDashboards ? '#6CB982' : shared ? variant : '' }}
-                                    onContextMenu={e => this.onContextMenu(dashboard, e)} 
+                                    onContextMenu={e => this.onContextMenu(dashboard, e)}
                                     onClick={e => this.clickDashboard(e, dashboard)}>
                                     <img
                                         src={
@@ -208,12 +186,7 @@ export class DashboardView extends React.Component {
                                         }
                                     />
                                     <div className="info">
-                                        <EditableText 
-                                            type={Type.PRIM}
-                                            color={color}
-                                            val={StrCast(dashboard.title)} 
-                                            setVal={val => (Doc.GetProto(dashboard).title = val)}
-                                        />
+                                        <EditableText type={Type.PRIM} color={color} val={StrCast(dashboard.title)} setVal={val => (Doc.GetProto(dashboard).title = val)} />
                                         {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></div>}
                                         <div
                                             className="more"
@@ -229,10 +202,13 @@ export class DashboardView extends React.Component {
                                             <Button size={Size.SMALL} color={color} icon={<FontAwesomeIcon color={color} icon="bars" />} />
                                         </div>
                                     </div>
-                                    <div className={`background`} style={{
-                                        background: StrCast(Doc.UserDoc().userColor),
-                                        filter: 'opacity(0.2)'
-                                    }}/>
+                                    <div
+                                        className={`background`}
+                                        style={{
+                                            background: StrCast(Doc.UserDoc().userColor),
+                                            filter: 'opacity(0.2)',
+                                        }}
+                                    />
                                     <div className={'dashboard-status' + (shared ? '-shared' : '')}>{shared ? 'shared' : ''}</div>
                                 </div>
                             );
@@ -243,10 +219,13 @@ export class DashboardView extends React.Component {
                                 this.setNewDashboardName('');
                             }}>
                             +
-                            <div className={`background`} style={{
-                                background: StrCast(Doc.UserDoc().userColor),
-                                filter: 'opacity(0.2)'
-                            }}/>
+                            <div
+                                className={`background`}
+                                style={{
+                                    background: StrCast(Doc.UserDoc().userColor),
+                                    filter: 'opacity(0.2)',
+                                }}
+                            />
                         </div>
                     </div>
                 </div>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 8fe5c2e01..e076e69ca 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -3,7 +3,7 @@ import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, HierarchyMapping, Opt, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
-import { Cast, StrCast } from '../../fields/Types';
+import { Cast, DocCast, StrCast } from '../../fields/Types';
 import { distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
@@ -149,9 +149,9 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 toRemove.forEach(doc => {
                     leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
                     Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
+                    Doc.RemoveDocFromList(Doc.GetProto(doc), 'proto_embeddings', doc);
                     doc.embedContainer = undefined;
                     if (recent) {
-                        Doc.RemoveDocFromList(recent, 'data', doc);
                         doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
                     }
                 });
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 68d29942b..63bd01b19 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -169,8 +169,6 @@ export class FilterPanel extends React.Component<filterProps> {
 
     render() {
         const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
-        console.log("this is option " + options)
-        console.log("this is alll facets " + this._allFacets)
         return (
             <div className="filterBox-treeView">
                 <div className="filterBox-select">
@@ -192,17 +190,15 @@ export class FilterPanel extends React.Component<filterProps> {
                 <div className="filterBox-tree" key="tree">
                     {Array.from(this.activeFacets.keys()).map(facetHeader => (
                         <div>
-                            <div className  = "filterBox-facetHeader">
-                                <div className = "filterBox-facetHeader-Header"> </div>
+                            <div className="filterBox-facetHeader">
+                                <div className="filterBox-facetHeader-Header"> </div>
                                 {facetHeader.charAt(0).toUpperCase() + facetHeader.slice(1)}
-                                
-                                <div className = "filterBox-facetHeader-collapse"> 
-                                 <AiOutlineMinusSquare/>
-                                 {/* <CiCircleRemove/> */}
-                                  </div>
-                               
+
+                                <div className="filterBox-facetHeader-collapse">
+                                    <AiOutlineMinusSquare />
+                                    {/* <CiCircleRemove/> */}
+                                </div>
                             </div>
-                           
 
                             {this.displayFacetValueFilterUIs(this.activeFacets.get(facetHeader), facetHeader)}
                         </div>
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 286d39943..f79a30ad3 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -313,8 +313,10 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     className="lightboxView-tabBtn"
                     title="open in tab"
                     onClick={e => {
+                        const lightdoc = LightboxView._docTarget || LightboxView._doc!;
                         e.stopPropagation();
-                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none);
+                        Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
+                        CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
                         SelectionManager.DeselectAll();
                         LightboxView.SetLightboxDoc(undefined);
                     }}>
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 2fa42d091..2a73d77ed 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -6,10 +6,7 @@ import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
 import { AssignAllExtensions } from '../../extensions/General/Extensions';
 import { FieldLoader } from '../../fields/FieldLoader';
-import { DocServer } from '../DocServer';
-import { Docs } from '../documents/Documents';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { LinkManager } from '../util/LinkManager'; // this must come before importing Docs and CurrentUserUtils
 import { ReplayMovements } from '../util/ReplayMovements';
 import { TrackMovements } from '../util/TrackMovements';
 import { CollectionView } from './collections/CollectionView';
@@ -20,7 +17,7 @@ import './global/globalScripts';
 dotenv.config();
 
 AssignAllExtensions();
-FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure why this is needed to get the code loaded properly...
+FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; // bcz: not sure why this is needed to get the code loaded properly...
 
 (async () => {
     MainView.Live = window.location.search.includes('live');
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cbaa763f5..dc236b603 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -4,7 +4,7 @@ import * as far from '@fortawesome/free-regular-svg-icons';
 import * as fa from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import 'browndash-components/dist/styles/global.min.css';
-import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
+import { action, computed, configure, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import 'normalize.css';
 import * as React from 'react';
@@ -182,14 +182,6 @@ export class MainView extends React.Component {
                 'currentFrame',
             ]); // 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)
-                )
-        );
 
         const tag = document.createElement('script');
         tag.src = 'https://www.youtube.com/iframe_api';
@@ -527,7 +519,7 @@ export class MainView extends React.Component {
     });
 
     initEventListeners = () => {
-        window.addEventListener('beforeunload', () => DocServer.UPDATE_SERVER_CACHE());
+        window.addEventListener('beforeunload', DocServer.UPDATE_SERVER_CACHE);
         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));
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index b74eabcc3..8cae34d7d 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -375,7 +375,6 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 val: value[1],
             };
         });
-        console.log('click val: ', this.onClickVal);
         return !this.selectedDoc ? null : (
             <Dropdown
                 tooltip={'Choose onClick behavior'}
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 395aa2b61..d157e7b1c 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -38,7 +38,6 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
                 }, new Set<Doc>())
                 .keys()
         );
-        console.log('embeddings ' + embeddings.length);
 
         return doclayouts
             .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document))
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
index ec8043ffe..6fab0168b 100644
--- a/src/client/views/PropertiesSection.tsx
+++ b/src/client/views/PropertiesSection.tsx
@@ -1,19 +1,19 @@
 import React = require('react');
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
-import { action, computed, observable } from "mobx"
-import { observer } from "mobx-react"
-import './PropertiesSection.scss'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import './PropertiesSection.scss';
 import { Doc } from '../../fields/Doc';
 import { StrCast } from '../../fields/Types';
 
 export interface PropertiesSectionProps {
-    title: string, 
-    content?: JSX.Element | string | null, 
-    isOpen: boolean,
-    setIsOpen: (bool: boolean) => any 
-    inSection?: boolean,
-    setInSection?: (bool: boolean) => any
-    onDoubleClick?: () => void
+    title: string;
+    content?: JSX.Element | string | null;
+    isOpen: boolean;
+    setIsOpen: (bool: boolean) => any;
+    inSection?: boolean;
+    setInSection?: (bool: boolean) => any;
+    onDoubleClick?: () => void;
 }
 
 @observer
@@ -33,34 +33,32 @@ export class PropertiesSection extends React.Component<PropertiesSectionProps> {
     @observable isDouble: boolean = false;
 
     render() {
-        console.log(this.props.title, this.props.content)
-        if (this.props.content === undefined || this.props.content === null) return null
-        else return <div className="propertiesView-section" onPointerEnter={action(() => (this.props.setInSection && this.props.setInSection(true)))} onPointerLeave={action(() => (this.props.setInSection && this.props.setInSection(false)))}>
-        <div className="propertiesView-sectionTitle" 
-            onDoubleClick={action((e) => {
-                this.isDouble = true;
-                this.props.onDoubleClick && this.props.onDoubleClick()
-                console.log("open options")
-                this.props.setIsOpen(true)
-                setTimeout(() => this.isDouble = false, 300)
-            })}
-            onClick={action((e) => {
-                this.props.setIsOpen(!this.props.isOpen)
-            })} 
-            style={{ 
-                background: this.props.isOpen ?  this.variantColor : this.backgroundColor,
-                color: this.color
-            }}>
-            {this.props.title}
-            <div className="propertiesView-sectionTitle-icon">
-                <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg"  />
-            </div>
-        </div>
-        {!this.props.isOpen ? null :
-            <div className="propertiesView-content">
-                {this.props.content}
-            </div>
-        }
-    </div>
+        if (this.props.content === undefined || this.props.content === null) return null;
+        else
+            return (
+                <div className="propertiesView-section" onPointerEnter={action(() => this.props.setInSection && this.props.setInSection(true))} onPointerLeave={action(() => this.props.setInSection && this.props.setInSection(false))}>
+                    <div
+                        className="propertiesView-sectionTitle"
+                        onDoubleClick={action(e => {
+                            this.isDouble = true;
+                            this.props.onDoubleClick && this.props.onDoubleClick();
+                            this.props.setIsOpen(true);
+                            setTimeout(() => (this.isDouble = false), 300);
+                        })}
+                        onClick={action(e => {
+                            this.props.setIsOpen(!this.props.isOpen);
+                        })}
+                        style={{
+                            background: this.props.isOpen ? this.variantColor : this.backgroundColor,
+                            color: this.color,
+                        }}>
+                        {this.props.title}
+                        <div className="propertiesView-sectionTitle-icon">
+                            <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" />
+                        </div>
+                    </div>
+                    {!this.props.isOpen ? null : <div className="propertiesView-content">{this.props.content}</div>}
+                </div>
+            );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 27b9c3c7a..872f1c6ab 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -8,7 +8,7 @@ import { concat } from 'lodash';
 import { Lambda, action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import * as Icons from "react-icons/bs"; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import * as Icons from 'react-icons/bs'; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
 import { GrCircleInformation } from 'react-icons/gr';
 import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
 import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
@@ -51,7 +51,6 @@ interface PropertiesViewProps {
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
 }
 
-
 @observer
 export class PropertiesView extends React.Component<PropertiesViewProps> {
     private _widthUndo?: UndoManager.Batch;
@@ -191,7 +190,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             });
 
             rows.push(
-                <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: "white", textAlign: "center" }}>
+                <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px', backgroundColor: 'white', textAlign: 'center' }}>
                     <EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
                 </div>
             );
@@ -249,14 +248,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return !this.selectedDoc ? null : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
-    @computed get contextCount(){
-        console.log("in context count");
-        if (this.selectedDocumentView){ 
-            const target = (this.selectedDocumentView.props.Document)
-            const embeddings = DocListCast(target.proto_embeddings)
-            console.log(embeddings.length -1 );
-            return (embeddings.length - 1)
-        } else{
+    @computed get contextCount() {
+        if (this.selectedDocumentView) {
+            const target = this.selectedDocumentView.props.Document;
+            const embeddings = DocListCast(target.proto_embeddings);
+            return embeddings.length - 1;
+        } else {
             return 0;
         }
     }
@@ -266,13 +263,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return !selAnchor ? null : <PropertiesDocBacklinksSelector Document={selAnchor} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
-    @computed get linkCount(){
-        const selAnchor =  this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
+    @computed get linkCount() {
+        const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
         var counter = 0;
 
-        LinkManager.Links(selAnchor).forEach((l, i) =>
-                counter ++
-            );
+        LinkManager.Links(selAnchor).forEach((l, i) => counter++);
 
         return counter;
     }
@@ -356,7 +351,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return (
             <Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
                 <div className="notify-button">
-                    <FontAwesomeIcon className="notify-button-icon" icon="bell"  size="sm" />
+                    <FontAwesomeIcon className="notify-button-icon" icon="bell" size="sm" />
                 </div>
             </Tooltip>
         );
@@ -367,16 +362,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @computed get expansionIcon() {
         return (
-            <div className="expansion-button" >
-                <IconButton 
-                    icon={<FontAwesomeIcon icon={'ellipsis-h'} />} 
-                    size={Size.XSMALL} 
-                    color={StrCast(Doc.UserDoc().userColor)} 
+            <div className="expansion-button">
+                <IconButton
+                    icon={<FontAwesomeIcon icon={'ellipsis-h'} />}
+                    size={Size.XSMALL}
+                    color={StrCast(Doc.UserDoc().userColor)}
                     onClick={action(() => {
                         if (this.selectedDocumentView || this.selectedDoc) {
                             SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
                         }
-                    })} 
+                    })}
                 />
             </div>
         );
@@ -418,7 +413,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             <div>
                 <div className={'propertiesView-shareDropDown'}>
                     <div className={`propertiesView-shareDropDown${permission}`}>
-                        <div >{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
+                        <div>{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
                     </div>
                 </div>
             </div>
@@ -450,7 +445,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const target = docs[0];
 
         const showAdmin = GetEffectiveAcl(target) == AclAdmin;
-        console.log(GetEffectiveAcl(target), Doc.GetProto(target)[`acl-${normalizeEmail(Doc.CurrentUserEmail)}`])
         const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
@@ -522,7 +516,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 <div>
                     <br></br> Individuals with Access to this Document
                 </div>
-                <div className="propertiesView-sharingTable" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+                <div className="propertiesView-sharingTable" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
                     {<div> {individualTableEntries}</div>}
                 </div>
                 {groupTableEntries.length > 0 ? (
@@ -530,7 +524,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         <div>
                             <br></br> Groups with Access to this Document
                         </div>
-                        <div className="propertiesView-sharingTable" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}>
+                        <div className="propertiesView-sharingTable" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor) }}>
                             {<div> {groupTableEntries}</div>}
                         </div>
                     </div>
@@ -562,68 +556,43 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get editableTitle() {
-
         const titles = new Set<string>();
         const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
         SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
-        return (
-            <EditableText
-                val={title}
-                setVal={this.setTitle}
-                color={this.color}
-                type={Type.SEC}
-                formLabel={"Title"}
-                fillWidth
-            />
-        );
+        return <EditableText val={title} setVal={this.setTitle} color={this.color} type={Type.SEC} formLabel={'Title'} fillWidth />;
     }
 
     @computed get currentType() {
-        // console.log("current type " + this.selectedDoc?.type)
- 
-         const documentType = StrCast(this.selectedDoc?.type)
-         var currentType: string = documentType;
-         var capitalizedDocType = Utils.cleanDocumentType(currentType as DocumentType);
- 
-         return (
-             <div>
-                 <div className = "propertiesView-wordType">Type</div>  
-                 <div className= "currentType"> 
-                 <div className='currentType-icon'>
-                      {this.currentComponent}
-                 </div>
-                
-                   {capitalizedDocType} 
-                
-                 </div>
-                
-             </div>
-           
-         )
-     }
+        const documentType = StrCast(this.selectedDoc?.type);
+        var currentType: string = documentType;
+        var capitalizedDocType = Utils.cleanDocumentType(currentType as DocumentType);
 
-    @computed get currentComponent() {
+        return (
+            <div>
+                <div className="propertiesView-wordType">Type</div>
+                <div className="currentType">
+                    <div className="currentType-icon">{this.currentComponent}</div>
 
-        var iconName = StrCast(this.selectedDoc?.systemIcon)
+                    {capitalizedDocType}
+                </div>
+            </div>
+        );
+    }
 
-        // if (this.selectedDoc?.type === DocumentType.COL){
-        //     console.log("i did it!")
-        // }
-       
+    @computed get currentComponent() {
+        var iconName = StrCast(this.selectedDoc?.systemIcon);
 
-        if (iconName){
+        if (iconName) {
             const Icon = Icons[iconName as keyof typeof Icons];
             return <Icon />;
-        } else{
-          return <Icons.BsFillCollectionFill/>
-
+        } else {
+            return <Icons.BsFillCollectionFill />;
         }
     }
 
     @undoBatch
     @action
     setTitle = (value: string | number) => {
-        console.log(value)
         if (SelectionManager.Views().length > 1) {
             SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true));
         } else if (this.dataDoc) {
@@ -687,7 +656,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         className="inking-button-points"
                         style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? 'black' : '' }}
                         onPointerDown={action(() => (InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton))}>
-                        <FontAwesomeIcon icon="bezier-curve"  size="lg" />
+                        <FontAwesomeIcon icon="bezier-curve" size="lg" />
                     </div>
                 </Tooltip>
             </div>
@@ -706,10 +675,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 <input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} onKeyPress={e => e.stopPropagation()} />
                 <div className="inputBox-button">
                     <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
-                        <FontAwesomeIcon icon="caret-up"  size="sm" />
+                        <FontAwesomeIcon icon="caret-up" size="sm" />
                     </div>
                     <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
-                        <FontAwesomeIcon icon="caret-down"  size="sm" />
+                        <FontAwesomeIcon icon="caret-down" size="sm" />
                     </div>
                 </div>
             </div>
@@ -963,10 +932,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 <input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} />
                 <div className="inputBox-button">
                     <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
-                        <FontAwesomeIcon icon="caret-up"  size="sm" />
+                        <FontAwesomeIcon icon="caret-up" size="sm" />
                     </div>
                     <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
-                        <FontAwesomeIcon icon="caret-down"  size="sm" />
+                        <FontAwesomeIcon icon="caret-down" size="sm" />
                     </div>
                 </div>
             </div>
@@ -983,7 +952,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         this.openSharing = false;
         this.openLayout = false;
         this.openFilters = false;
-    }
+    };
 
     @computed get widthAndDash() {
         return (
@@ -1065,64 +1034,45 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: any) => {
-        return <div>
-            <NumberInput
-                formLabel={label}
-                formLabelPlacement={'left'}
-                type={Type.SEC}
-                unit={unit}
-                fillWidth
-                color={this.color}
-                number={number}
-                setNumber={setNumber} 
-                min={min}
-                max={max}            
-            />
-            <Slider 
-                multithumb={false}
-                color={this.color}
-                size={Size.XSMALL} 
-                min={min} 
-                max={max}
-                unit={unit} 
-                number={number}
-                setNumber={setNumber}
-                fillWidth
-            />
-        </div>
-    }
+        return (
+            <div>
+                <NumberInput formLabel={label} formLabelPlacement={'left'} type={Type.SEC} unit={unit} fillWidth color={this.color} number={number} setNumber={setNumber} min={min} max={max} />
+                <Slider multithumb={false} color={this.color} size={Size.XSMALL} min={min} max={max} unit={unit} number={number} setNumber={setNumber} fillWidth />
+            </div>
+        );
+    };
 
     @computed get transformEditor() {
         return (
             <div className="transform-editor">
                 {this.isInk ? this.controlPointsButton : null}
                 {this.getNumber(
-                    "Height",
-                    " px",
+                    'Height',
+                    ' px',
                     0,
                     1000,
                     Number(this.shapeHgt),
                     undoable((val: string) => !isNaN(Number(val)) && (this.shapeHgt = val), 'set height')
                 )}
                 {this.getNumber(
-                    "Width",
-                    " px",
+                    'Width',
+                    ' px',
                     0,
                     1000,
                     Number(this.shapeWid),
                     undoable((val: string) => !isNaN(Number(val)) && (this.shapeWid = val), 'set width')
                 )}
                 {this.getNumber(
-                    "X Coordinate",
-                    " px",
+                    'X Coordinate',
+                    ' px',
                     -2000,
                     2000,
                     Number(this.shapeXps),
                     undoable((val: string) => !isNaN(Number(val)) && (this.shapeXps = val), 'set x coord')
                 )}
                 {this.getNumber(
-                    "Y Coordinate",
-                    " px",
+                    'Y Coordinate',
+                    ' px',
                     -2000,
                     2000,
                     Number(this.shapeYps),
@@ -1133,38 +1083,44 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get optionsSubMenu() {
-        return <PropertiesSection
-            title="Options"
-            content={<PropertiesButtons />}
-            inSection={this.inOptions}
-            isOpen={this.openOptions}
-            setInSection={(bool) => this.inOptions = bool}
-            setIsOpen={(bool) => this.openOptions = bool}
-            onDoubleClick={() => this.onDoubleClick()}
-        />
+        return (
+            <PropertiesSection
+                title="Options"
+                content={<PropertiesButtons />}
+                inSection={this.inOptions}
+                isOpen={this.openOptions}
+                setInSection={bool => (this.inOptions = bool)}
+                setIsOpen={bool => (this.openOptions = bool)}
+                onDoubleClick={() => this.onDoubleClick()}
+            />
+        );
     }
 
     @computed get sharingSubMenu() {
-        return <PropertiesSection
-            title="Sharing & Permissions"
-            content={<>
-            {/* <div className="propertiesView-buttonContainer"> */}
-                    <div className="propertiesView-acls-checkbox">
-                        Layout Permissions
-                        <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
-                    </div>
-                    {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+        return (
+            <PropertiesSection
+                title="Sharing & Permissions"
+                content={
+                    <>
+                        {/* <div className="propertiesView-buttonContainer"> */}
+                        <div className="propertiesView-acls-checkbox">
+                            Layout Permissions
+                            <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
+                        </div>
+                        {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
                         <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
                             <FontAwesomeIcon icon="redo-alt"  size="1x" />
                         </button>
                     </Tooltip> */}
-                {/* </div> */}
-                {this.sharingTable}
-            </>}
-            isOpen={this.openSharing}
-            setIsOpen={(bool) => this.openSharing = bool}
-            onDoubleClick={() => this.onDoubleClick()}
-        />
+                        {/* </div> */}
+                        {this.sharingTable}
+                    </>
+                }
+                isOpen={this.openSharing}
+                setIsOpen={bool => (this.openSharing = bool)}
+                onDoubleClick={() => this.onDoubleClick()}
+            />
+        );
     }
 
     /**
@@ -1192,15 +1148,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     };
 
     @computed get filtersSubMenu() {
-        return <PropertiesSection 
-            title="Filters"
-            content={<div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
-                <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
-            </div>}
-            isOpen={this.openFilters}
-            setIsOpen={(bool) => this.openFilters = bool}
-             onDoubleClick={() => this.onDoubleClick()}
-        />
+        return (
+            <PropertiesSection
+                title="Filters"
+                content={
+                    <div className="propertiesView-content filters" style={{ position: 'relative', height: 'auto' }}>
+                        <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
+                    </div>
+                }
+                isOpen={this.openFilters}
+                setIsOpen={bool => (this.openFilters = bool)}
+                onDoubleClick={() => this.onDoubleClick()}
+            />
+        );
     }
 
     @computed get inkSubMenu() {
@@ -1208,68 +1168,42 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         return (
             <>
-                <PropertiesSection
-                    title="Appearance"
-                    content={this.isInk ? this.appearanceEditor : null}
-                    isOpen={this.openAppearance}
-                    setIsOpen={(bool) => this.openAppearance = bool}
-                     onDoubleClick={() => this.onDoubleClick()}
-                />
-                <PropertiesSection
-                    title="Transform"
-                    content={this.transformEditor}
-                    isOpen={this.openTransform}
-                    setIsOpen={(bool) => this.openTransform = bool}
-                    onDoubleClick={() => this.onDoubleClick()}
-                />
+                <PropertiesSection title="Appearance" content={this.isInk ? this.appearanceEditor : null} isOpen={this.openAppearance} setIsOpen={bool => (this.openAppearance = bool)} onDoubleClick={() => this.onDoubleClick()} />
+                <PropertiesSection title="Transform" content={this.transformEditor} isOpen={this.openTransform} setIsOpen={bool => (this.openTransform = bool)} onDoubleClick={() => this.onDoubleClick()} />
             </>
         );
     }
 
     @computed get fieldsSubMenu() {
-        return <PropertiesSection
-            title="Fields & Tags"
-            content={<div className="propertiesView-content fields">{
-                Doc.noviceMode ? this.noviceFields : this.expandedField}
-            </div>}
-            isOpen={this.openFields}
-            setIsOpen={(bool) => this.openFields = bool}
-            onDoubleClick={() => this.onDoubleClick()}
-        />
+        return (
+            <PropertiesSection
+                title="Fields & Tags"
+                content={<div className="propertiesView-content fields">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>}
+                isOpen={this.openFields}
+                setIsOpen={bool => (this.openFields = bool)}
+                onDoubleClick={() => this.onDoubleClick()}
+            />
+        );
     }
 
     @computed get contextsSubMenu() {
-        return <PropertiesSection
-            title="Other Contexts"
-            content={this.contextCount > 0 ? this.contexts : "There are no other contexts."}
-            isOpen={this.openContexts}
-            setIsOpen={(bool) => this.openContexts = bool}
-            onDoubleClick={() => this.onDoubleClick()}
-        />
+        return (
+            <PropertiesSection
+                title="Other Contexts"
+                content={this.contextCount > 0 ? this.contexts : 'There are no other contexts.'}
+                isOpen={this.openContexts}
+                setIsOpen={bool => (this.openContexts = bool)}
+                onDoubleClick={() => this.onDoubleClick()}
+            />
+        );
     }
 
-
-
-
-
     @computed get linksSubMenu() {
-        return <PropertiesSection
-            title="Linked To"
-            content={this.linkCount > 0 ? this.links : "There are no current links." }
-            isOpen={this.openLinks}
-            setIsOpen={(bool) => this.openLinks = bool}
-            onDoubleClick={() => this.onDoubleClick()}
-        />
+        return <PropertiesSection title="Linked To" content={this.linkCount > 0 ? this.links : 'There are no current links.'} isOpen={this.openLinks} setIsOpen={bool => (this.openLinks = bool)} onDoubleClick={() => this.onDoubleClick()} />;
     }
 
     @computed get layoutSubMenu() {
-        return <PropertiesSection
-            title="Layout"
-            content={this.layoutPreview}
-            isOpen={this.openLayout}
-            setIsOpen={(bool) => this.openLayout = bool}
-            onDoubleClick={() => this.onDoubleClick()}
-        />
+        return <PropertiesSection title="Layout" content={this.layoutPreview} isOpen={this.openLayout} setIsOpen={bool => (this.openLayout = bool)} onDoubleClick={() => this.onDoubleClick()} />;
     }
 
     @computed get description() {
@@ -1470,224 +1404,226 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (scale > 1) scale = 1;
         this.sourceAnchor && (this.sourceAnchor.followLinkZoomScale = scale);
     };
-    
+
     @computed get linkProperties() {
         const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3));
         const targZoom = this.sourceAnchor?.followLinkZoom;
         const indent = 30;
         const hasSelectedAnchor = LinkManager.Links(this.sourceAnchor).includes(LinkManager.currentLink!);
 
-        return <>
-        <div className="propertiesView-section" style={{ background: 'darkgray' }}>
-            <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
-                <p>Relationship</p>
-                {this.editRelationship}
-            </div>
-            <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
-                <p>Description</p>
-                {this.editDescription}
-            </div>
-            <div className="propertiesView-input inline">
-                <p>Show link</p>
-                <button
-                    style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
-                    onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
-                    onClick={e => e.stopPropagation()}
-                    className="propertiesButton">
-                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                </button>
-            </div>
-            <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
-                <p>Auto-move anchors</p>
-                <button
-                    style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
-                    onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
-                    onClick={e => e.stopPropagation()}
-                    className="propertiesButton">
-                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                </button>
-            </div>
-            <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
-                <p>Display arrow</p>
-                <button
-                    style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
-                    onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
-                    onClick={e => e.stopPropagation()}
-                    className="propertiesButton">
-                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                </button>
-            </div>
-        </div>
-        {!hasSelectedAnchor ? null : (
-            <div className="propertiesView-section">
-                <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
-                    <p>Follow by</p>
-                    <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
-                        <option value={undefined}>Default</option>
-                        <option value={OpenWhere.addLeft}>Opening in new left pane</option>
-                        <option value={OpenWhere.addRight}>Opening in new right pane</option>
-                        <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
-                        <option value={OpenWhere.replaceRight}>Replacing right tab</option>
-                        <option value={OpenWhere.lightbox}>Opening in lightbox</option>
-                        <option value={OpenWhere.add}>Opening in new tab</option>
-                        <option value={OpenWhere.replace}>Replacing current tab</option>
-                        <option value={OpenWhere.inParent}>Opening in same collection</option>
-                        {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
-                    </select>
-                </div>
-                <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
-                    <p>Animation</p>
-                    <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
-                        <option value="default">Default</option>
-                        {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
-                            <option key={effect.toString()} value={effect.toString()}>
-                                {effect.toString()}
-                            </option>
-                        ))}
-                    </select>
-                    <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
-                        {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
-                        {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
-                        {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
-                        {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
-                        {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
+        return (
+            <>
+                <div className="propertiesView-section" style={{ background: 'darkgray' }}>
+                    <div className="propertiesView-input first" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
+                        <p>Relationship</p>
+                        {this.editRelationship}
+                    </div>
+                    <div className="propertiesView-input" style={{ display: 'grid', gridTemplateColumns: '84px auto' }}>
+                        <p>Description</p>
+                        {this.editDescription}
+                    </div>
+                    <div className="propertiesView-input inline">
+                        <p>Show link</p>
+                        <button
+                            style={{ background: !LinkManager.currentLink?.link_displayLine ? '' : '#4476f7', borderRadius: 3 }}
+                            onPointerDown={e => this.toggleLinkProp(e, 'link_displayLine')}
+                            onClick={e => e.stopPropagation()}
+                            className="propertiesButton">
+                            <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                        </button>
+                    </div>
+                    <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
+                        <p>Auto-move anchors</p>
+                        <button
+                            style={{ background: !LinkManager.currentLink?.link_autoMoveAnchors ? '' : '#4476f7', borderRadius: 3 }}
+                            onPointerDown={e => this.toggleLinkProp(e, 'link_autoMoveAnchors')}
+                            onClick={e => e.stopPropagation()}
+                            className="propertiesButton">
+                            <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                        </button>
+                    </div>
+                    <div className="propertiesView-input inline" style={{ marginLeft: 10 }}>
+                        <p>Display arrow</p>
+                        <button
+                            style={{ background: !LinkManager.currentLink?.link_displayArrow ? '' : '#4476f7', borderRadius: 3 }}
+                            onPointerDown={e => this.toggleLinkProp(e, 'link_displayArrow')}
+                            onClick={e => e.stopPropagation()}
+                            className="propertiesButton">
+                            <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                        </button>
                     </div>
                 </div>
-                {PresBox.inputter(
-                    '0.1',
-                    '0.1',
-                    '10',
-                    NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
-                    true,
-                    (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
-                    indent
-                )}{' '}
-                <div
-                    className={'slider-headers'}
-                    style={{
-                        display: 'grid',
-                        justifyContent: 'space-between',
-                        width: `calc(100% - ${indent * 2}px)`,
-                        marginLeft: indent,
-                        marginRight: indent,
-                        gridTemplateColumns: 'auto auto',
-                        borderTop: 'solid',
-                    }}>
-                    <div className="slider-text">Fast</div>
-                    <div className="slider-text">Slow</div>
-                </div>{' '}
-                <div className="propertiesView-input inline">
-                    <p>Play Target Audio</p>
-                    <button
-                        style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
-                        onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
-                        onClick={e => e.stopPropagation()}
-                        className="propertiesButton">
-                        <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                    </button>
-                </div>
-                <div className="propertiesView-input inline">
-                    <p>Zoom Text Selections</p>
-                    <button
-                        style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
-                        onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
-                        onClick={e => e.stopPropagation()}
-                        className="propertiesButton">
-                        <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                    </button>
-                </div>
-                <div className="propertiesView-input inline">
-                    <p>Toggle Follow to Outer Context</p>
-                    <button
-                        style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
-                        onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
-                        onClick={e => e.stopPropagation()}
-                        className="propertiesButton">
-                        <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
-                    </button>
-                </div>
-                <div className="propertiesView-input inline">
-                    <p>Toggle Target (Show/Hide)</p>
-                    <button
-                        style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
-                        onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
-                        onClick={e => e.stopPropagation()}
-                        className="propertiesButton">
-                        <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                    </button>
-                </div>
-                <div className="propertiesView-input inline">
-                    <p>Ease Transitions</p>
-                    <button
-                        style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
-                        onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
-                        onClick={e => e.stopPropagation()}
-                        className="propertiesButton">
-                        <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                    </button>
-                </div>
-                <div className="propertiesView-input inline">
-                    <p>Capture Offset to Target</p>
-                    <button
-                        style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
-                        onPointerDown={e => {
-                            this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
-                            this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
-                        }}
-                        onClick={e => e.stopPropagation()}
-                        className="propertiesButton">
-                        <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                    </button>
-                </div>
-                <div className="propertiesView-input inline">
-                    <p>Center Target (no zoom)</p>
-                    <button
-                        style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
-                        onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
-                        onClick={e => e.stopPropagation()}
-                        className="propertiesButton">
-                        <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                    </button>
-                </div>
-                <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
-                    <p>Zoom %</p>
-                    <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
-                        <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
-                        <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
-                            <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
-                                <FontAwesomeIcon icon={'caret-up'} />
+                {!hasSelectedAnchor ? null : (
+                    <div className="propertiesView-section">
+                        <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 84px)' }}>
+                            <p>Follow by</p>
+                            <select onChange={e => this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}>
+                                <option value={undefined}>Default</option>
+                                <option value={OpenWhere.addLeft}>Opening in new left pane</option>
+                                <option value={OpenWhere.addRight}>Opening in new right pane</option>
+                                <option value={OpenWhere.replaceLeft}>Replacing left tab</option>
+                                <option value={OpenWhere.replaceRight}>Replacing right tab</option>
+                                <option value={OpenWhere.lightbox}>Opening in lightbox</option>
+                                <option value={OpenWhere.add}>Opening in new tab</option>
+                                <option value={OpenWhere.replace}>Replacing current tab</option>
+                                <option value={OpenWhere.inParent}>Opening in same collection</option>
+                                {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
+                            </select>
+                        </div>
+                        <div className="propertiesView-input inline first" style={{ display: 'grid', gridTemplateColumns: '84px calc(100% - 134px) 50px' }}>
+                            <p>Animation</p>
+                            <select style={{ width: '100%', gridColumn: 2 }} onChange={e => this.changeAnimationBehavior(e.currentTarget.value)} value={StrCast(this.sourceAnchor?.followLinkAnimEffect, 'default')}>
+                                <option value="default">Default</option>
+                                {[PresEffect.None, PresEffect.Zoom, PresEffect.Lightspeed, PresEffect.Fade, PresEffect.Flip, PresEffect.Rotate, PresEffect.Bounce, PresEffect.Roll].map(effect => (
+                                    <option key={effect.toString()} value={effect.toString()}>
+                                        {effect.toString()}
+                                    </option>
+                                ))}
+                            </select>
+                            <div className="effectDirection" style={{ marginLeft: '10px', display: 'grid', width: 40, height: 36, gridColumn: 3, gridTemplateRows: '12px 12px 12px' }}>
+                                {this.animationDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
+                                {this.animationDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
+                                {this.animationDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
+                                {this.animationDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
+                                {this.animationDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
                             </div>
-                            <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
-                                <FontAwesomeIcon icon={'caret-down'} />
+                        </div>
+                        {PresBox.inputter(
+                            '0.1',
+                            '0.1',
+                            '10',
+                            NumCast(this.sourceAnchor?.followLinkTransitionTime) / 1000,
+                            true,
+                            (val: string) => PresBox.SetTransitionTime(val, (timeInMS: number) => this.sourceAnchor && (this.sourceAnchor.followLinkTransitionTime = timeInMS)),
+                            indent
+                        )}{' '}
+                        <div
+                            className={'slider-headers'}
+                            style={{
+                                display: 'grid',
+                                justifyContent: 'space-between',
+                                width: `calc(100% - ${indent * 2}px)`,
+                                marginLeft: indent,
+                                marginRight: indent,
+                                gridTemplateColumns: 'auto auto',
+                                borderTop: 'solid',
+                            }}>
+                            <div className="slider-text">Fast</div>
+                            <div className="slider-text">Slow</div>
+                        </div>{' '}
+                        <div className="propertiesView-input inline">
+                            <p>Play Target Audio</p>
+                            <button
+                                style={{ background: !this.sourceAnchor?.followLinkAudio ? '' : '#4476f7', borderRadius: 3 }}
+                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkAudio', this.sourceAnchor)}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                            </button>
+                        </div>
+                        <div className="propertiesView-input inline">
+                            <p>Zoom Text Selections</p>
+                            <button
+                                style={{ background: !this.sourceAnchor?.followLinkZoomText ? '' : '#4476f7', borderRadius: 3 }}
+                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoomText', this.sourceAnchor)}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                            </button>
+                        </div>
+                        <div className="propertiesView-input inline">
+                            <p>Toggle Follow to Outer Context</p>
+                            <button
+                                style={{ background: !this.sourceAnchor?.followLinkToOuterContext ? '' : '#4476f7', borderRadius: 3 }}
+                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToOuterContext', this.sourceAnchor)}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faWindowMaximize as IconLookup} size="lg" />
+                            </button>
+                        </div>
+                        <div className="propertiesView-input inline">
+                            <p>Toggle Target (Show/Hide)</p>
+                            <button
+                                style={{ background: !this.sourceAnchor?.followLinkToggle ? '' : '#4476f7', borderRadius: 3 }}
+                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkToggle', this.sourceAnchor)}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                            </button>
+                        </div>
+                        <div className="propertiesView-input inline">
+                            <p>Ease Transitions</p>
+                            <button
+                                style={{ background: this.sourceAnchor?.followLinkEase === 'linear' ? '' : '#4476f7', borderRadius: 3 }}
+                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkEase', this.sourceAnchor, 'ease', 'linear')}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                            </button>
+                        </div>
+                        <div className="propertiesView-input inline">
+                            <p>Capture Offset to Target</p>
+                            <button
+                                style={{ background: this.sourceAnchor?.followLinkXoffset === undefined ? '' : '#4476f7', borderRadius: 3 }}
+                                onPointerDown={e => {
+                                    this.toggleAnchorProp(e, 'followLinkXoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.x) - NumCast(this.sourceAnchor?.x), undefined);
+                                    this.toggleAnchorProp(e, 'followLinkYoffset', this.sourceAnchor, NumCast(this.destinationAnchor?.y) - NumCast(this.sourceAnchor?.y), undefined);
+                                }}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                            </button>
+                        </div>
+                        <div className="propertiesView-input inline">
+                            <p>Center Target (no zoom)</p>
+                            <button
+                                style={{ background: this.sourceAnchor?.followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                            </button>
+                        </div>
+                        <div className="propertiesView-input inline" style={{ display: 'grid', gridTemplateColumns: '78px calc(100% - 108px) 50px' }}>
+                            <p>Zoom %</p>
+                            <div className="ribbon-property" style={{ display: !targZoom ? 'none' : 'inline-flex' }}>
+                                <input className="presBox-input" style={{ width: '100%' }} readOnly={true} type="number" value={zoom} />
+                                <div className="ribbon-propertyUpDown" style={{ display: 'flex', flexDirection: 'column' }}>
+                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
+                                        <FontAwesomeIcon icon={'caret-up'} />
+                                    </div>
+                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
+                                        <FontAwesomeIcon icon={'caret-down'} />
+                                    </div>
+                                </div>
                             </div>
+                            <button
+                                style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
+                                onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
+                                onClick={e => e.stopPropagation()}
+                                className="propertiesButton">
+                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                            </button>
                         </div>
+                        {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
+                        <div
+                            className={'slider-headers'}
+                            style={{
+                                display: !targZoom ? 'none' : 'grid',
+                                justifyContent: 'space-between',
+                                width: `calc(100% - ${indent * 2}px)`,
+                                marginLeft: indent,
+                                marginRight: indent,
+                                gridTemplateColumns: 'auto auto',
+                                borderTop: 'solid',
+                            }}>
+                            <div className="slider-text">0%</div>
+                            <div className="slider-text">100%</div>
+                        </div>{' '}
                     </div>
-                    <button
-                        style={{ background: !targZoom || this.sourceAnchor?.followLinkZoomScale === 0 ? '' : '#4476f7', borderRadius: 3, gridColumn: 3 }}
-                        onPointerDown={e => this.toggleAnchorProp(e, 'followLinkZoom', this.sourceAnchor)}
-                        onClick={e => e.stopPropagation()}
-                        className="propertiesButton">
-                        <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                    </button>
-                </div>
-                {!targZoom ? null : PresBox.inputter('0', '1', '100', zoom, true, this.setZoom, 30)}
-                <div
-                    className={'slider-headers'}
-                    style={{
-                        display: !targZoom ? 'none' : 'grid',
-                        justifyContent: 'space-between',
-                        width: `calc(100% - ${indent * 2}px)`,
-                        marginLeft: indent,
-                        marginRight: indent,
-                        gridTemplateColumns: 'auto auto',
-                        borderTop: 'solid',
-                    }}>
-                    <div className="slider-text">0%</div>
-                    <div className="slider-text">100%</div>
-                </div>{' '}
-            </div>
-        )}
-    </>
+                )}
+            </>
+        );
     }
 
     /**
@@ -1723,23 +1659,20 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             width: this.props.width,
                             minWidth: this.props.width,
                         }}>
-                        <div className = "propertiesView-propAndInfoGrouping">
+                        <div className="propertiesView-propAndInfoGrouping">
                             <div className="propertiesView-title" style={{ width: this.props.width }}>
                                 Properties
                             </div>
-                            <div className = "propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/')}>
-                                 <GrCircleInformation/> </div>
-
+                            <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/')}>
+                                <GrCircleInformation />{' '}
+                            </div>
                         </div>
 
-                        
                         <div className="propertiesView-name">{this.editableTitle}</div>
-                        <div className = "propertiesView-type"> {this.currentType} </div>
+                        <div className="propertiesView-type"> {this.currentType} </div>
                         {this.contextsSubMenu}
                         {this.linksSubMenu}
-                        {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : (
-                            this.linkProperties
-                        )}
+                        {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : this.linkProperties}
                         {this.inkSubMenu}
                         {this.optionsSubMenu}
                         {this.fieldsSubMenu}
@@ -1760,7 +1693,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             Presentation
                         </div>
                         <div className="propertiesView-name" style={{ borderBottom: 0 }}>
-                         
                             {this.editableTitle}
                             <div className="propertiesView-presSelected">
                                 <div className="propertiesView-selectedCount">{PresBox.Instance.selectedArray.size} selected</div>
@@ -1772,7 +1704,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                 <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Transitions
                                     <div className="propertiesView-presTrails-title-icon">
-                                        <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg"  />
+                                        <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
                                 {this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
@@ -1786,7 +1718,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                     style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Visibilty
                                     <div className="propertiesView-presTrails-title-icon">
-                                        <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg"  />
+                                        <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
                                 {this.openPresVisibilityAndDuration ? <div className="propertiesView-presTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
@@ -1797,7 +1729,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                 <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openPresProgressivize = !this.openPresProgressivize))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Progressivize
                                     <div className="propertiesView-presTrails-title-icon">
-                                        <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg"  />
+                                        <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
                                 {this.openPresProgressivize ? <div className="propertiesView-presTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
@@ -1808,7 +1740,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                 <div className="propertiesView-presTrails-title" onPointerDown={action(() => (this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
                                     &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
                                     <div className="propertiesView-presTrails-title-icon">
-                                        <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg"  />
+                                        <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" />
                                     </div>
                                 </div>
                                 {this.openSlideOptions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index bbbad3690..94748e884 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -22,7 +22,7 @@ import { DocumentViewProps } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
 import { KeyValueBox } from './nodes/KeyValueBox';
 import { SliderBox } from './nodes/SliderBox';
-import { BsArrowDown, BsArrowUp, BsArrowDownUp } from 'react-icons/bs'
+import { BsArrowDown, BsArrowUp, BsArrowDownUp } from 'react-icons/bs';
 import './StyleProvider.scss';
 import React = require('react');
 
@@ -161,7 +161,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 ''
             );
         case StyleProp.Color:
-            if (MainView.Instance.LastButton === doc) return Doc.UserDoc().userBackgroundColor;
+            if (MainView.Instance.LastButton === doc) return SettingsManager.Instance.userBackgroundColor;
             if (Doc.IsSystem(doc!)) return StrCast(Doc.UserDoc().userColor)
             if (doc?.type === DocumentType.FONTICON) return Doc.UserDoc().userColor;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color));
@@ -208,12 +208,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.PRES:        docColor = docColor || (darkScheme() ? 'transparent' : 'transparent'); break;
                 case DocumentType.FONTICON:    docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
                 case DocumentType.RTF:         docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
-                case DocumentType.FILTER:      docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : 'rgba(105, 105, 105, 0.432)'); break;
                 case DocumentType.INK:         docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
                 case DocumentType.EQUATION:    docColor = docColor || 'transparent'; break;
                 case DocumentType.LABEL:       docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
                 case DocumentType.BUTTON:      docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
-                case DocumentType.LINKANCHOR:  docColor = isAnchor ? Colors.LIGHT_BLUE : 'transparent'; break;
                 case DocumentType.LINK:        docColor = (isAnchor ? docColor : '') || 'transparent'; break;
                 case DocumentType.IMG:
                 case DocumentType.WEB:
@@ -224,7 +222,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 case DocumentType.COL:
                     if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
                     docColor = docColor || (Doc.IsSystem(doc)
-                            ? StrCast(Doc.UserDoc().userBackgroundColor)
+                            ? SettingsManager.Instance.userBackgroundColor
                             : doc.annotationOn
                             ? '#00000010' // faint interior for collections on PDFs, images, etc
                             : doc?._isGroup
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index caf04cc1b..a551e5332 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -7,6 +7,7 @@ import { StrCast } from '../../fields/Types';
 import { Doc } from '../../fields/Doc';
 import { Popup, Type, isDark } from 'browndash-components';
 import { Colors } from './global/globalEnums';
+import { SettingsManager } from '../util/SettingsManager';
 
 interface UndoStackProps {
     width?: number;
@@ -18,35 +19,37 @@ export class UndoStack extends React.Component<UndoStackProps> {
     @observable static HideInline: boolean;
     @observable static Expand: boolean;
     render() {
-        const background = UndoManager.batchCounter.get() ? 'yellow' : StrCast(Doc.UserDoc().userBackgroundColor)
+        const background = UndoManager.batchCounter.get() ? 'yellow' : SettingsManager.Instance.userBackgroundColor;
         return this.props.inline && UndoStack.HideInline ? null : (
             <div className="undoStack-outerContainer">
-                <Popup 
+                <Popup
                     text={'Undo/Redo Stack'}
                     color={UndoManager.batchCounter.get() ? 'yellow' : StrCast(Doc.UserDoc().userVariantColor)}
                     placement={`top-start`}
                     type={Type.TERT}
                     popup={
-                    <div className="undoStack-commandsContainer" ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })} 
-                        style={{ 
-                            background: background,
-                            color: isDark(background) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY
-                        }}>
-                        {UndoManager.undoStackNames.map((name, i) => (
-                            <div className="undoStack-resultContainer" key={i}>
-                                <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
-                            </div>
-                        ))}
-                        {Array.from(UndoManager.redoStackNames)
-                            .reverse()
-                            .map((name, i) => (
+                        <div
+                            className="undoStack-commandsContainer"
+                            ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })}
+                            style={{
+                                background: background,
+                                color: isDark(background) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY,
+                            }}>
+                            {UndoManager.undoStackNames.map((name, i) => (
                                 <div className="undoStack-resultContainer" key={i}>
-                                    <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
-                                        {name.replace(/[^\.]*\./, '')}
-                                    </div>
+                                    <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
                                 </div>
-                        ))}
-                    </div>    
+                            ))}
+                            {Array.from(UndoManager.redoStackNames)
+                                .reverse()
+                                .map((name, i) => (
+                                    <div className="undoStack-resultContainer" key={i}>
+                                        <div className="undoStack-commandString" style={{ fontWeight: 'bold', color: 'red' }}>
+                                            {name.replace(/[^\.]*\./, '')}
+                                        </div>
+                                    </div>
+                                ))}
+                        </div>
                     }
                 />
             </div>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 16982595d..0052c4196 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -164,7 +164,7 @@ export class CollectionDockingView extends CollectionSubView() {
     public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
         if (document?._type_collection === CollectionViewType.Docking && !keyValue) return DashboardView.openDashboard(document);
         if (!CollectionDockingView.Instance) return false;
-        const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !keyValue);
+        const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !tab.contentItem.config.props.keyValue && !keyValue);
         if (tab) {
             tab.header.parent.setActiveContentItem(tab.contentItem);
             return true;
@@ -466,7 +466,10 @@ export class CollectionDockingView extends CollectionSubView() {
         this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
         if (tab.DashDoc && ![DocumentType.PRES].includes(tab.DashDoc?.type) && !tab.contentItem.config.props.keyValue) {
             Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
-            Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
+            // if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
+            if (tab.DashDoc.embedContainer === this.rootDoc) tab.DashDoc.embedContainer = undefined;
+            if (!tab.DashDoc.embedContainer) Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
+            Doc.RemoveDocFromList(Doc.GetProto(tab.DashDoc), 'proto_embeddings', tab.DashDoc);
         }
         if (CollectionDockingView.Instance) {
             const dview = CollectionDockingView.Instance.props.Document;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 5135cfb57..f65e8698f 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -181,7 +181,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
             <div
                 className="collectionMenu-container"
                 style={{
-                    background: StrCast(Doc.UserDoc().userBackgroundColor),
+                    background: SettingsManager.Instance.userBackgroundColor,
                     // borderColor: StrCast(Doc.UserDoc().userColor)
                 }}>
                 {this.contMenuButtons}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a5c276125..056204ad3 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -669,7 +669,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         if (menuDoc) {
             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
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 00137736d..ea3b5065f 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -150,6 +150,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                 console.log('WHAAAT');
             }
             dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction;
+            e.stopPropagation();
         }
     };
 
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 67b7b39dd..d787f5262 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -396,7 +396,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         return NumCast(Cast(PresBox.Instance.activeItem.presentationTargetDoc, Doc, null)._currentFrame);
     };
     static Activate = (tabDoc: Doc) => {
-        const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc);
+        const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc && !tab.contentItem.config.props.keyValue);
         tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
         return tab !== undefined;
     };
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index fb23fc7f1..6e0ccd4be 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -127,7 +127,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             : this.props.treeView.fileSysMode
             ? this.doc.isFolder
                 ? this.fieldKey
-                : 'embeddings' // for displaying
+                : 'data' // file system folders display their contents (data).  used to be they displayed their embeddings but now its a tree structure and not a flat list
             : this.props.treeView.outlineMode || this.childDocs
             ? this.fieldKey
             : Doc.noviceMode
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 090cf356c..7c53bfdbe 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -377,7 +377,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             : ((doc: Doc) => {
                   Doc.GetProto(doc).data = new List<Doc>(selected);
                   Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
-                  !this.props.isAnnotationOverlay && Doc.AddFileOrphan(Doc.GetProto(doc));
                   doc._freeform_panX = doc._freeform_panY = 0;
                   return doc;
               })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 79842047b..256377758 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -197,7 +197,6 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
 });
 
 export function checkInksToGroup() {
-    // console.log("getting here to inks group");
     if (Doc.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
diff --git a/src/client/views/newlightbox/ExploreView/ExploreView.tsx b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
index 44a0bcd5f..a1d6375c4 100644
--- a/src/client/views/newlightbox/ExploreView/ExploreView.tsx
+++ b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
@@ -12,14 +12,11 @@ export const ExploreView = (props: IExploreView) => {
         <div className={`exploreView-container`}>
             {recs &&
                 recs.map(rec => {
-                    console.log(rec.embedding, bounds);
                     const x_bound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x));
                     const y_bound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y));
-                    console.log(x_bound, y_bound);
                     if (rec.embedding) {
                         const x = (rec.embedding.x / x_bound) * 50;
                         const y = (rec.embedding.y / y_bound) * 50;
-                        console.log(x, y);
                         return (
                             <div className={`exploreView-doc`} onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}>
                                 {rec.title}
diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
index b9d05c531..2c2f04b9f 100644
--- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
+++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
@@ -22,7 +22,6 @@ export const Recommendation = (props: IRecommendation) => {
                         doc = docView.rootDoc;
                     }
                 } else if (data) {
-                    console.log(data, type);
                     switch (type) {
                         case 'YouTube':
                             console.log('create ', type, 'document');
diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
index e1ff6f8eb..10bfb0c64 100644
--- a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
+++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
@@ -34,7 +34,6 @@ export const createLineGenerator = (xScale: d3.ScaleLinear<number, number, never
 };
 
 export const xAxisCreator = (g: d3.Selection<SVGGElement, unknown, null, undefined>, height: number, xScale: d3.ScaleLinear<number, number, never>) => {
-    console.log('x axis creator being called');
     g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15));
 };
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 70d2f95ea..d379b2b52 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,5 +1,5 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
@@ -47,7 +47,6 @@ import { DocumentLinksButton } from './DocumentLinksButton';
 import './DocumentView.scss';
 import { FieldViewProps } from './FieldView';
 import { FormattedTextBox } from './formattedText/FormattedTextBox';
-import { KeyValueBox } from './KeyValueBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { PresEffect, PresEffectDirection } from './trails';
 import { PinProps, PresBox } from './trails/PresBox';
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 86191de63..d69009415 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import wiki from 'wikijs';
 import { Doc, DocCastAsync, Opt } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
+import { DirectLinks, Height, Width } from '../../../fields/DocSymbols';
 import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
 import { DocServer } from '../../DocServer';
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index de0b57fd7..4919ee94c 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -61,7 +61,6 @@ const script = document.createElement('script');
 script.defer = true;
 script.async = true;
 script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
-console.log(script.src);
 document.head.appendChild(script);
 
 /**
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 312b3c619..bbf56bdf9 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -224,7 +224,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             const aud_chunks: any = [];
             this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
             this._audioRec.onstop = async (e: any) => {
-                const [{ result }] = await Networking.UploadFilesToServer(aud_chunks.map((file: any) => ({file})));
+                const [{ result }] = await Networking.UploadFilesToServer(aud_chunks.map((file: any) => ({ file })));
                 if (!(result instanceof Error)) {
                     this.dataDoc[this.props.fieldKey + '-audio'] = new AudioField(result.accessPaths.agnostic.client);
                 }
@@ -235,9 +235,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
             this._videoRec.onstart = () => (this.dataDoc[this.props.fieldKey + '-recordingStart'] = new DateField(new Date()));
             this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
             this._videoRec.onstop = async (e: any) => {
-                console.log('screenshotbox: upload');
                 const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
-                const [{ result }] = await Networking.UploadFilesToServer({file});
+                const [{ result }] = await Networking.UploadFilesToServer({ file });
                 this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
                 if (!(result instanceof Error)) {
                     // convert this screenshotBox into normal videoBox
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 3ad3c911d..7c8a1849e 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -610,7 +610,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
                         ' ': {
                             dataProvider: (token: any) => this.handleToken(token),
                             component: (blob: any) => {
-                                console.log('Blob', blob);
                                 return this.renderFuncListElement(blob.entity);
                             },
                             output: (item: any, trigger: any) => {
@@ -621,7 +620,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
                         '.': {
                             dataProvider: (token: any) => this.handleToken(token),
                             component: (blob: any) => {
-                                console.log('Blob', blob);
                                 return this.renderFuncListElement(blob.entity);
                             },
                             output: (item: any, trigger: any) => {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 895bb80f0..1dcc445e8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -96,6 +96,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _editorView: Opt<EditorView>;
     public _applyingChange: string = '';
+    private _finishingLink = false;
     private _searchIndex = 0;
     private _lastTimedMark: Mark | undefined = undefined;
     private _cachedLinks: Doc[] = [];
@@ -245,7 +246,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         if (!pinProps && this._editorView?.state.selection.empty) return this.rootDoc;
         const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.rootDoc.title), annotationOn: this.rootDoc });
         this.addDocument(anchor);
+        this._finishingLink = true;
         this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
+        this._finishingLink = false;
         PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true } }, this.rootDoc);
         return anchor;
     };
@@ -326,7 +329,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
                     if ((!prevData && !protoData) || newText || (!newText && !templateData)) {
                         // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
-                        if (this.props.isContentActive() && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
+                        if ((this._finishingLink || this.props.isContentActive()) && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
                             const numstring = NumCast(dataDoc[this.fieldKey], null);
                             dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText);
                             dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited
@@ -457,8 +460,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
                             alink =
                                 alink ??
-                                (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)) ||
-                                    DocUtils.MakeLink(this.props.Document, target, { link_relationship: LinkManager.AutoKeywords })!);
+                                (LinkManager.Links(this.rootDoc).find(
+                                    link =>
+                                        Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && //
+                                        Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)
+                                ) ||
+                                    DocUtils.MakeLink(this.rootDoc, target, { link_relationship: LinkManager.AutoKeywords })!);
                             newAutoLinks.add(alink);
                             const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
                             allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
@@ -840,7 +847,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
         const appearance = cm.findByDescription('Appearance...');
         const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
-        appearanceItems.push({ description: 'Change Perspective...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
+        appearanceItems.push({ description: 'Change Style...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
         // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
 
         !Doc.noviceMode &&
@@ -1996,12 +2003,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     }
     cycleAlternateText = () => {
         if (this.layoutDoc._layout_enableAltContentUI) {
-            const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+            const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
             this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
         }
     };
     @computed get overlayAlternateIcon() {
-        const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+        const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
         return (
             <Tooltip
                 title={
@@ -2080,7 +2087,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         ? {}
                         : {
                               transform: `scale(${scale})`,
-                              transformOrigin: 'top left',
                               width: `${100 / scale}%`,
                               height: `${100 / scale}%`,
                           }),
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index ac1e7ce5d..8bafc2cef 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -243,13 +243,13 @@ export class RichTextRules {
 
             // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
             // [[<fieldKey> : <Doc>]]
-            // [[:Doc]] => hyperlink
+            // [[:docTitle]] => hyperlink
             // [[fieldKey]] => show field
             // [[fieldKey=value]] => show field and also set its value
-            // [[fieldKey:Doc]] => show field of doc
+            // [[fieldKey:docTitle]] => show field of doc
             new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
                 const fieldKey = match[1];
-                const docId = match[3]?.replace(':', '');
+                const docTitle = match[3]?.replace(':', '');
                 const value = match[2]?.substring(1);
                 const linkToDoc = (target: Doc) => {
                     const rstate = this.TextBox.EditorView?.state;
@@ -266,12 +266,12 @@ export class RichTextRules {
                     }
                 };
                 if (!fieldKey) {
-                    if (docId) {
-                        const target = DocServer.QUERY_SERVER_CACHE(docId);
-                        if (target) setTimeout(() => linkToDoc(target));
-                        else DocServer.GetRefField(docId).then(docx => linkToDoc((docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docId + '(auto)', _width: 500, _height: 500 }, docId)));
-
-                        return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+                    if (docTitle) {
+                        const target = DocServer.FindDocByTitle(docTitle);
+                        if (target) {
+                            setTimeout(() => linkToDoc(target));
+                            return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+                        }
                     }
                     return state.tr;
                 }
@@ -279,8 +279,12 @@ export class RichTextRules {
                     const num = value.match(/^[0-9.]$/);
                     this.Document[DocData][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
                 }
-                const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId, hideKey: false });
-                return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
+                const target = DocServer.FindDocByTitle(docTitle);
+                if (target) {
+                    const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target[Id], hideKey: false });
+                    return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
+                }
+                return state.tr;
             }),
 
             // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index b877cc36a..e35e011e2 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -17,6 +17,7 @@ import { EditorView } from 'prosemirror-view';
 import './AnchorMenu.scss';
 import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
 import { StrCast } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
 
 @observer
 export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -262,7 +263,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                     colorPicker={this.highlightColor}
                     color={StrCast(Doc.UserDoc().userColor)}
                 />
-                <ColorPicker colorPickerType={'github'} selectedColor={this.highlightColor} setSelectedColor={color => this.changeHighlightColor(color)} size={Size.XSMALL} />
+                <ColorPicker selectedColor={this.highlightColor} setSelectedColor={this.changeHighlightColor} size={Size.XSMALL} />
             </Group>
         );
     }
@@ -287,7 +288,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     canSummarize = (): boolean => {
         const docs = SelectionManager.Docs();
         if (docs.length > 0) {
-            return docs.some(doc => doc.type === 'pdf' || doc.type === 'web');
+            return docs.some(doc => doc.type === DocumentType.PDF || doc.type === DocumentType.WEB);
         }
         return false;
     };
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index e911bd283..a28561107 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -18,6 +18,7 @@ import './SearchBox.scss';
 import { fetchRecommendations } from '../newlightbox/utils';
 import { IRecommendation, Recommendation } from '../newlightbox/components';
 import { Colors } from '../global/globalEnums';
+import { SettingsManager } from '../../util/SettingsManager';
 
 const DAMPENING_FACTOR = 0.9;
 const MAX_ITERATIONS = 25;
@@ -218,7 +219,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     }
     @action
     static staticSearchCollection(rootDoc: Opt<Doc>, query: string) {
-        const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
+        const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.SEARCH, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
         const blockedKeys = [
             'x',
             'y',
@@ -398,22 +399,21 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         if (query) {
             this.searchCollection(query);
-            const response = await fetchRecommendations('', query, [], true)
-            const recs = response.recommendations
-            const recommendations:IRecommendation[] = []
+            const response = await fetchRecommendations('', query, [], true);
+            const recs = response.recommendations;
+            const recommendations: IRecommendation[] = [];
             for (const key in recs) {
                 const title = recs[key].title;
-                console.log(title);
-                const url = recs[key].url
-                const type = recs[key].type
-                const text = recs[key].text
-                const transcript = recs[key].transcript
-                const previewUrl = recs[key].previewUrl
-                const embedding = recs[key].embedding
-                const distance = recs[key].distance
-                const source = recs[key].source
-                const related_concepts = recs[key].related_concepts
-                const docId = recs[key].doc_id
+                const url = recs[key].url;
+                const type = recs[key].type;
+                const text = recs[key].text;
+                const transcript = recs[key].transcript;
+                const previewUrl = recs[key].previewUrl;
+                const embedding = recs[key].embedding;
+                const distance = recs[key].distance;
+                const source = recs[key].source;
+                const related_concepts = recs[key].related_concepts;
+                const docId = recs[key].doc_id;
                 recommendations.push({
                     title: title,
                     data: url,
@@ -425,11 +425,11 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                     distance: Math.round(distance * 100) / 100,
                     source: source,
                     related_concepts: related_concepts,
-                    docId: docId
-                })
+                    docId: docId,
+                });
             }
-            const setRecommendations = action(() => this._recommendations = recommendations)
-            setRecommendations()
+            const setRecommendations = action(() => (this._recommendations = recommendations));
+            setRecommendations();
         }
     };
 
@@ -461,7 +461,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      */
     @computed
     public get selectOptions() {
-        const selectValues = ['all', 'rtf', 'image', 'pdf', 'web', 'video', 'audio', 'collection'];
+        const selectValues = ['all', DocumentType.RTF, DocumentType.IMG, DocumentType.PDF, DocumentType.WEB, DocumentType.VID, DocumentType.AUDIO, DocumentType.COL];
 
         return selectValues.map(value => (
             <option key={value} value={value}>
@@ -517,19 +517,19 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                             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 className="searchBox-result-keys" style={{ color: SettingsManager.Instance.userVariantColor }}>
+                                {result[1].join(', ')}
+                            </div>
                         </div>
                     </Tooltip>
                 );
             }
         });
 
-        const recommendationsJSX: JSX.Element[] = this._recommendations.map((props) => (
-            <Recommendation {...props}/>
-        ))
+        const recommendationsJSX: JSX.Element[] = this._recommendations.map(props => <Recommendation {...props} />);
 
         return (
-            <div className="searchBox-container" style={{pointerEvents: 'all', background: StrCast(Doc.UserDoc().userBackgroundColor)}}>
+            <div className="searchBox-container" style={{ pointerEvents: 'all', color: SettingsManager.Instance.userColor, background: SettingsManager.Instance.userBackgroundColor }}>
                 <div className="searchBox-bar">
                     {isLinkSearch ? null : (
                         <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
@@ -552,20 +552,24 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                         ref={this._inputRef}
                     />
                 </div>
-                {resultsJSX.length > 0 && <div className="searchBox-results-container">
-                    <div className="section-header" style={{background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}}>
-                        <div className="section-title">Results</div>
-                        <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+                {resultsJSX.length > 0 && (
+                    <div className="searchBox-results-container">
+                        <div className="section-header" style={{ background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE) }}>
+                            <div className="section-title">Results</div>
+                            <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+                        </div>
+                        <div className="searchBox-results-view">{resultsJSX}</div>
                     </div>
-                    <div className="searchBox-results-view">{resultsJSX}</div>
-                </div>}
-                {recommendationsJSX.length > 0 && <div className="searchBox-recommendations-container">
-                    <div className="section-header" style={{background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}}>
-                        <div className="section-title">Recommendations</div>
-                        <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+                )}
+                {recommendationsJSX.length > 0 && (
+                    <div className="searchBox-recommendations-container">
+                        <div className="section-header" style={{ background: StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE) }}>
+                            <div className="section-title">Recommendations</div>
+                            <div className="section-subtitle">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+                        </div>
+                        <div className="searchBox-recommendations-view">{recommendationsJSX}</div>
                     </div>
-                    <div className="searchBox-recommendations-view">{recommendationsJSX}</div>
-                </div>}
+                )}
             </div>
         );
     }
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index cb8eda9de..8caba930d 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -35,10 +35,14 @@ export class TopBar extends React.Component {
         });
     };
 
-    @computed get color() { return StrCast(Doc.UserDoc().userColor, Colors.LIGHT_GRAY); }
-    @computed get variantColor() { return StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE); }
+    @computed get color() {
+        return StrCast(Doc.UserDoc().userColor, Colors.LIGHT_GRAY);
+    }
+    @computed get variantColor() {
+        return StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE);
+    }
     @computed get backgroundColor() {
-        return PingManager.Instance.IsBeating ? StrCast(Doc.UserDoc().userBackgroundColor, Colors.DARK_GRAY) : Colors.MEDIUM_GRAY;
+        return PingManager.Instance.IsBeating ? SettingsManager.Instance.userBackgroundColor : Colors.MEDIUM_GRAY;
     }
 
     @observable happyHeart: boolean = PingManager.Instance.IsBeating;
@@ -102,7 +106,7 @@ export class TopBar extends React.Component {
                     tooltip="Open Dashboards"
                     size={Size.SMALL}
                     color={this.color}
-                    style={{fontWeight: 700, fontSize: '1rem'}}
+                    style={{ fontWeight: 700, fontSize: '1rem' }}
                     onClick={(e: React.MouseEvent) => {
                         const dashView = Doc.ActiveDashboard && DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard);
                         ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' });
@@ -134,7 +138,7 @@ export class TopBar extends React.Component {
     @computed get topbarRight() {
         return (
             <div className="topbar-right">
-                {Doc.ActiveDashboard ? 
+                {Doc.ActiveDashboard ? (
                     <Button
                         text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'}
                         type={Type.TERT}
@@ -143,15 +147,15 @@ export class TopBar extends React.Component {
                             SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
                         }}
                     />
-                : null }
-                <IconButton tooltip={"Issue Reporter ⌘I"} size={Size.SMALL} color={this.color} onClick={ReportManager.Instance.open} icon={<FaBug />} />
-                <IconButton tooltip={"Documentation ⌘D"} size={Size.SMALL} color={this.color} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
-                <IconButton tooltip={"Settings ⌘⇧S"} size={Size.SMALL} color={this.color} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} />
+                ) : null}
+                <IconButton tooltip={'Issue Reporter ⌘I'} size={Size.SMALL} color={this.color} onClick={ReportManager.Instance.open} icon={<FaBug />} />
+                <IconButton tooltip={'Documentation ⌘D'} size={Size.SMALL} color={this.color} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
+                <IconButton tooltip={'Settings ⌘⇧S'} size={Size.SMALL} color={this.color} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} />
                 <IconButton
                     size={Size.SMALL}
                     onClick={ServerStats.Instance.open}
                     type={Type.TERT}
-                    tooltip={'Server is ' + (PingManager.Instance.IsBeating ? '' : 'NOT ') + 'running'}
+                    tooltip={'Server is ' + (PingManager.Instance.IsBeating ? '' : 'NOT ') + 'running ' + SettingsManager.Version}
                     color={this.happyHeart ? Colors.LIGHT_BLUE : Colors.ERROR_RED}
                     icon={<FontAwesomeIcon icon={this.happyHeart ? 'heart' : 'heart-broken'} />}
                 />
@@ -163,12 +167,14 @@ export class TopBar extends React.Component {
     render() {
         return (
             //TODO:glr Add support for light / dark mode
-            <div style={{ 
-                pointerEvents: 'all',
-                color: this.color,
-                background: this.backgroundColor,
-                // borderColor: this.color
-            }} className="topbar-container">
+            <div
+                style={{
+                    pointerEvents: 'all',
+                    color: this.color,
+                    background: this.backgroundColor,
+                    // borderColor: this.color
+                }}
+                className="topbar-container">
                 <div className="topbar-inner-container">
                     {this.topbarLeft}
                     {this.topbarCenter}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5a411a201..eb52cff88 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -238,22 +238,6 @@ export class Doc extends RefField {
     public static get MyFilesystem() {
         return DocCast(Doc.UserDoc().myFilesystem);
     }
-    public static get MyFileOrphans() {
-        return DocCast(Doc.UserDoc().myFileOrphans);
-    }
-    public static AddFileOrphan(doc: Doc) {
-        if (
-            doc &&
-            Doc.MyFileOrphans instanceof Doc &&
-            Doc.IsDataProto(doc) &&
-            !Doc.IsSystem(doc) &&
-            ![DocumentType.CONFIG, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(doc.type as any) &&
-            !doc.isFolder &&
-            !doc.annotationOn
-        ) {
-            Doc.AddDocToList(Doc.MyFileOrphans, undefined, doc);
-        }
-    }
     public static get MyTools() {
         return DocCast(Doc.UserDoc().myTools);
     }
@@ -388,7 +372,7 @@ export class Doc extends RefField {
             } else {
                 return Cast(layoutField, Doc, null);
             }
-            return Cast(self[renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
+            return Cast(self[renderFieldKey + '_layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
         }
         return undefined;
     }
@@ -701,7 +685,8 @@ export namespace Doc {
     }
 
     export function BestEmbedding(doc: Doc) {
-        const bestEmbedding = Doc.GetProto(doc) ? DocListCast(doc.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+        const bestEmbedding = Doc.GetProto(doc) ? [doc, ...DocListCast(doc.proto_embeddings)].find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail) : doc;
+        bestEmbedding && Doc.AddDocToList(Doc.GetProto(doc), 'protoEmbeddings', doc);
         return bestEmbedding ?? Doc.MakeEmbedding(doc);
     }
 
@@ -782,7 +767,6 @@ export namespace Doc {
         copy.cloneOf = doc;
         cloneMap.set(doc[Id], copy);
 
-        Doc.AddFileOrphan(copy);
         return copy;
     }
     export function repairClone(clone: Doc, cloneMap: Map<string, Doc>, visited: Set<Doc>) {
@@ -917,7 +901,7 @@ export namespace Doc {
         // If it doesn't find the expanded layout, then it makes a delegate of the template layout and
         // saves it on the data doc indexed by the template layout's id.
         //
-        const expandedLayoutFieldKey = templateField + '-layout[' + templateLayoutDoc[Id] + ']';
+        const expandedLayoutFieldKey = templateField + '_layout[' + templateLayoutDoc[Id] + ']';
         let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
 
         if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
@@ -985,6 +969,59 @@ export namespace Doc {
         return overwrite;
     }
 
+    export function FindReferences(infield: Doc | List<any>, references: Set<Doc>, system: boolean | undefined) {
+        if (infield instanceof List<any>) {
+            infield.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
+            return;
+        }
+        const doc = infield as Doc;
+        if (references.has(doc)) {
+            references.add(doc);
+            return;
+        }
+        const excludeLists = doc.title === 'My Recently Closed' || doc.title === 'My Header Bar' || doc.title === 'My Dashboards';
+        if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) return;
+        references.add(doc);
+        Object.keys(doc).forEach(key => {
+            if (key === 'proto') {
+                if (doc.proto instanceof Doc) {
+                    Doc.FindReferences(doc.proto, references, system);
+                }
+            } else {
+                const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+                const field = key === 'author' ? Doc.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]);
+                if (field instanceof RefField) {
+                    if (field instanceof Doc) {
+                        if (key === 'myLinkDatabase') {
+                            field instanceof Doc && references.add(field);
+                            // skip docs that have been closed and are scheduled for garbage collection
+                        } else {
+                            Doc.FindReferences(field, references, system);
+                        }
+                    }
+                } else if (cfield instanceof ComputedField) {
+                } else if (field instanceof ObjectField) {
+                    if (field instanceof Doc) {
+                        Doc.FindReferences(field, references, system);
+                    } else if (field instanceof List) {
+                        !excludeLists && Doc.FindReferences(field, references, system);
+                    } else if (field instanceof ProxyField) {
+                        if (key === 'myLinkDatabase') {
+                            field instanceof Doc && references.add(field);
+                            // skip docs that have been closed and are scheduled for garbage collection
+                        } else {
+                            Doc.FindReferences(field.value, references, system);
+                        }
+                    } else if (field instanceof PrefetchProxy) {
+                        Doc.FindReferences(field.value, references, system);
+                    }
+                } else if (field instanceof Promise) {
+                    debugger; //This shouldn't happend...
+                }
+            }
+        });
+    }
+
     export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string, retitle = false): Doc {
         const copy = new Doc(copyProtoId, true);
         updateCachedAcls(copy);
@@ -1025,7 +1062,6 @@ export namespace Doc {
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
         }
-        Doc.AddFileOrphan(copy);
         return copy;
     }
 
@@ -1044,7 +1080,6 @@ export namespace Doc {
             if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DocData], 'proto_embeddings', delegate);
             title && (delegate.title = title);
             delegate[Initializing] = false;
-            Doc.AddFileOrphan(delegate);
             return delegate;
         }
         return undefined;
@@ -1177,7 +1212,7 @@ export namespace Doc {
     // the document containing the view layout information - will be the Document itself unless the Document has
     // a layout field or 'layout' is given.
     export function Layout(doc: Doc, layout?: Doc): Doc {
-        const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}-layout[` + layout[Id] + ']'], Doc, null);
+        const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}_layout[` + layout[Id] + ']'], Doc, null);
         return overrideLayout || doc[DocLayout] || doc;
     }
     export function SetLayout(doc: Doc, layout: Doc | string) {
diff --git a/src/fields/FieldLoader.tsx b/src/fields/FieldLoader.tsx
index 2a7b936f7..a5a71833c 100644
--- a/src/fields/FieldLoader.tsx
+++ b/src/fields/FieldLoader.tsx
@@ -6,10 +6,9 @@ import './FieldLoader.scss';
 
 @observer
 export class FieldLoader extends React.Component {
-    @observable public static ServerLoadStatus = { requested: 0, retrieved: 0 };
-    public static active = false;
+    @observable public static ServerLoadStatus = { requested: 0, retrieved: 0, message: '' };
 
     render() {
-        return <div className="fieldLoader">{`Requested: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>;
+        return <div className="fieldLoader">{`${FieldLoader.ServerLoadStatus.message} request: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>;
     }
 }
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 6764f2727..337bb812f 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -143,7 +143,6 @@ export namespace DashUploadUtils {
 
     export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> {
         return new Promise<Upload.FileResponse<Upload.FileInformation>>((res, rej) => {
-            console.log('Uploading YouTube video: ' + videoId);
             const name = videoId;
             const path = name.replace(/^-/, '__') + '.mp4';
             const finalPath = serverPathToFile(Directory.videos, path);
@@ -523,7 +522,7 @@ export namespace DashUploadUtils {
             try {
                 const response = await axios.post(RESIZE_FUNCTION_URL, {
                     url: requestable,
-                    filename: resolved
+                    filename: resolved,
                 });
                 writtenFiles = response.data.writtenFiles;
             } catch (err) {
-- 
cgit v1.2.3-70-g09d2


From d48eb2da82ff88d52d42f6f5c4c553fa614e7f02 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 10 Aug 2023 09:25:48 -0400
Subject: merged /getUserDocumentIds into /getCurrentUser to avoid extra server
 round trip

---
 src/client/util/CurrentUserUtils.ts   | 39 ++++++++++++++++-------------------
 src/client/views/Main.tsx             |  6 +++++-
 src/server/ApiManagers/UserManager.ts |  3 ++-
 3 files changed, 25 insertions(+), 23 deletions(-)

(limited to 'src/client/views/Main.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 017fb6061..f8fc2b531 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -917,7 +917,7 @@ export class CurrentUserUtils {
     public static async loadCurrentUser() {
         return rp.get(Utils.prepend("/getCurrentUser")).then(async response => {
             if (response) {
-                const result: { id: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response);
+                const result: { userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string,  email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response);
                 Doc.CurrentUserEmail = result.email;
                 resolvedPorts = result.resolvedPorts as any;
                 DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email);
@@ -936,27 +936,24 @@ export class CurrentUserUtils {
         });
     }
 
-    public static async loadUserDocument(id: string) {
-        await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
-            const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids);
-            if (userDocumentId) {
-                return DocServer.GetRefField(userDocumentId).then(async field => {
-                    Docs.newAccount = !(field instanceof Doc);
-                    await Docs.Prototypes.initialize();
-                    const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
-                    this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
-                    if (Docs.newAccount) {
-                        if (Doc.CurrentUserEmail === "guest") {
-                            DashboardView.createNewDashboard(undefined, "guest dashboard");
-                        } else {
-                            userDoc.activePage = "home";
-                        }
-                    }
-                    return userDoc;
-                });
-            } else {
-                throw new Error("There should be a user id! Why does Dash think there isn't one?");
+    public static async loadUserDocument(info:{
+        userDocumentId: string;
+        sharingDocumentId: string;
+        linkDatabaseId: string;
+    }) {
+        return DocServer.GetRefField(info.userDocumentId).then(async field => {
+            Docs.newAccount = !(field instanceof Doc);
+            await Docs.Prototypes.initialize();
+            const userDoc = Docs.newAccount ? new Doc(info.userDocumentId, true) : field as Doc;
+            this.updateUserDocument(Doc.SetUserDoc(userDoc), info.sharingDocumentId, info.linkDatabaseId);
+            if (Docs.newAccount) {
+                if (Doc.CurrentUserEmail === "guest") {
+                    DashboardView.createNewDashboard(undefined, "guest dashboard");
+                } else {
+                    userDoc.activePage = "home";
+                }
             }
+            return userDoc;
         });
     }
 
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 2a73d77ed..6dd1d53ee 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -26,7 +26,11 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
     window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
     const info = await CurrentUserUtils.loadCurrentUser();
     // if (info.email === 'guest') DocServer.Control.makeReadOnly();
-    await CurrentUserUtils.loadUserDocument(info.id);
+    if (!info.userDocumentId) {
+        alert('Fatal Error: user not found in database');
+        return;
+    }
+    await CurrentUserUtils.loadUserDocument(info);
     setTimeout(() => {
         document.getElementById('root')!.addEventListener(
             'wheel',
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index a00a7ca5d..c4555fa8d 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -68,7 +68,8 @@ export default class UserManager extends ApiManager {
         register({
             method: Method.GET,
             subscription: '/getCurrentUser',
-            secureHandler: ({ res, user: { _id, email, cacheDocumentIds } }) => res.send(JSON.stringify({ id: _id, email, cacheDocumentIds, resolvedPorts })),
+            secureHandler: ({ res, user }) =>
+                res.send(JSON.stringify({ userDocumentId: user.userDocumentId, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId, email: user.email, cacheDocumentIds: user.cacheDocumentIds, resolvedPorts })),
             publicHandler: ({ res }) => res.send(JSON.stringify({ id: '__guest__', email: 'guest' })),
         });
 
-- 
cgit v1.2.3-70-g09d2

