From 62666034718378f4d970331665019b23659c1f3d Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 22 Mar 2023 17:01:17 -0400
Subject: visual changes to collaboration on doc and in PropertiesView

---
 src/client/views/PropertiesView.tsx | 96 +++++++++++++++++++++++++++++--------
 1 file changed, 76 insertions(+), 20 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index e750dc43a..c34760d13 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -7,7 +7,7 @@ import { intersection } from 'lodash';
 import { action, autorun, computed, Lambda, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { AclAdmin, AclSym, HierarchyMapping, DataSym, Doc, DocListCast, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+import { AclAdmin, AclSym, HierarchyMapping, DataSym, Doc, DocListCast, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym, DocCastAsync } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -364,8 +364,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     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]));
-        SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
+        if (user=="Public"){
+            this.selectedDoc!['acl-' +user] = e.currentTarget.value as SharingPermissions;
+        }
+        else{
+            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]));
+            SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
+        }
     };
 
     /**
@@ -376,11 +381,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (permission === '-multiple-') dropdownValues.unshift(permission);
         if (user !== 'Override') dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return (
-            <select className="permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
+            <select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
                 {dropdownValues
                     .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
                     .map(permission => (
-                        <option key={permission} value={permission}>
+                        <option className="propertiesView-permisssions-select" key={permission} value={permission}>
                             {' '}
                             {permission}{' '}
                         </option>
@@ -433,8 +438,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)}
             >
                 <div className="propertiesView-sharingTable-item-name" style={{ width: name !== 'Me' ? '85px' : '80px' }}>
-                    {' '}
-                    {name}{' '}
+                    {' '}{name}{' '}
                 </div>
                 {/* {name !== "Me" ? this.notifyIcon : null} */}
                 <div className="propertiesView-sharingTable-item-permission">
@@ -445,13 +449,52 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    publicACLDropDown(admin: boolean, permission: string, showExpansionIcon?: boolean){
+        var dropDownText = "";
+        switch(StrCast(permission)){
+            case ("Edit"):
+                dropDownText = "⬢ ";
+                break;
+            case ("Self-Edit"):
+                dropDownText = "⬢ ";
+                break;
+            case ("Augment"):
+                dropDownText = "⬟ ";
+                break;
+            case ("View"):
+                dropDownText = "♦ ";
+                break;
+            case ("Not-Shared"):
+                dropDownText = "▲ ";
+                break;
+        }
+        return (
+            <div>
+                <div className={'propertiesView-shareDropDown'}>
+                    <div className={`propertiesView-shareDropDown${permission}`}>
+                        <div className="propertiesView-shareDropDown">
+                            {' '}{dropDownText}{' '}
+                            {admin && permission !== 'Owner' ? this.getPermissionsSelect("Public", permission) : permission}
+                            {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null}
+                        </div>
+                    </div>
+                </div>
+            </div>
+        );
+    }
+
     /**
      * @returns the sharing and permissions panel.
      */
     @computed get sharingTable() {
+        // const docToUse = this.selectedDocumentView?.rootDoc;
+        const docToUse = this.selectedDoc;
+        if (!docToUse){
+            return null;
+        }
         // all selected docs
         const docs =
-            SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]));
+            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];
 
@@ -479,17 +522,27 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // shifts the current user, owner, public to the top of the doc.
         // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
         if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
-        tableEntries.unshift(this.sharingItem('Public', showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-'));
-        tableEntries.unshift(
-            this.sharingItem(
-                'Me',
-                showAdmin,
-                docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-',
-                !ownerSame
-            )
-        );
 
-        return <div className="propertiesView-sharingTable">{tableEntries}</div>;
+        // tableEntries.unshift(this.sharingItem(target.author, showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-'));
+        // tableEntries.unshift(
+        //     this.sharingItem(
+        //         'Me',
+        //         showAdmin,
+        //         docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-',
+        //         !ownerSame
+        //     )
+        // );
+        docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, doc['acl-Public'], true)));
+
+        return (
+            <div> Sharing Mode
+                <div>{this.publicACLDropDown(true, StrCast(docToUse['acl-Public']), false)}</div>
+            <div>  <br></br> Who has access to the Dashboard? </div>
+            <div className="propertiesView-sharingTable">{
+                <div> {tableEntries}</div>
+                }</div>
+            </div>
+        );
     }
 
     @computed get fieldsCheckbox() {
@@ -1116,12 +1169,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 {!this.openSharing ? null : (
                     <div className="propertiesView-sharing-content">
                         <div className="propertiesView-buttonContainer">
-                            {!Doc.noviceMode ? (
+
+                        
+                            {/* {!Doc.noviceMode ? ( // what is the layout checkbox for?
                                 <div className="propertiesView-acls-checkbox">
                                     <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
                                     <div className="propertiesView-acls-checkbox-text">Layout</div>
                                 </div>
-                            ) : null}
+                            ) : null} */}
+
                             {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
                                         <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
                                             <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
-- 
cgit v1.2.3-70-g09d2


From 4265feca9e63cad6067055497ecabd354ead84f4 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 22 Mar 2023 17:45:32 -0400
Subject: collaboration icons

---
 src/client/views/DocumentDecorations.tsx |  3 +++
 src/client/views/PropertiesView.tsx      | 26 +++++++++++++-------------
 2 files changed, 16 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d87c13ce6..b2b18b245 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -756,6 +756,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             case ("Not-Shared"):
                 shareSymbolIcon = "▲ ";
                 break;
+            default:
+                shareSymbolIcon = "";
+                break;
         }
 
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index c34760d13..e58558aea 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -504,25 +504,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // 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 tableEntries = [];
 
         // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
-        if (commonKeys.length) {
-            for (const key of commonKeys) {
-                const name = denormalizeEmail(key.substring(4));
-                const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
-                if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
-                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
-                }
-            }
-        }
-
+        // if (commonKeys.length) {
+        //     for (const key of commonKeys) {
+        //         const name = denormalizeEmail(key.substring(4));
+        //         const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
+        //         if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
+        //             tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
+        //         }
+        //     }
+        // }
         const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
         // shifts the current user, owner, public to the top of the doc.
         // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
         if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
-
         // tableEntries.unshift(this.sharingItem(target.author, showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-'));
         // tableEntries.unshift(
         //     this.sharingItem(
@@ -532,7 +529,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         //         !ownerSame
         //     )
         // );
-        docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, doc['acl-Public'], true)));
+
+        SharingManager.Instance.users.forEach(eachUser => tableEntries.unshift(this.sharingItem(eachUser.user.email, false, "test", false)));
+
+        docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, 'Owner', true)));
 
         return (
             <div> Sharing Mode
-- 
cgit v1.2.3-70-g09d2


From d1e4d4a9daae4f037cf8b22e1011d97680a01e27 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Tue, 2 May 2023 17:44:00 -0400
Subject: update

properties view, document decorations, and sharing manager show + use correct acl
---
 package-lock.json                                  |   6 -
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/SharingManager.scss                |   8 +-
 src/client/util/SharingManager.tsx                 | 126 ++++++++++++++++++---
 src/client/views/DocumentDecorations.tsx           |  60 ++++++----
 src/client/views/PropertiesView.scss               |   7 +-
 src/client/views/PropertiesView.tsx                | 124 ++++++++++++++------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  18 +--
 src/fields/Doc.ts                                  |   7 +-
 src/fields/util.ts                                 |  47 ++++++--
 10 files changed, 292 insertions(+), 113 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 42a7babb6..d74073e7f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22164,12 +22164,6 @@
             "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
             "dev": true
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 3e89c8347..f5510d7e1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -12,7 +12,7 @@ import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
 import { ComputedField, ScriptField } from '../../fields/ScriptField';
 import { Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types';
 import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from '../../fields/URLField';
-import { inheritParentAcls, SharingPermissions } from '../../fields/util';
+import { inheritParentAcls, normalizeEmail, SharingPermissions } from '../../fields/util';
 import { Upload } from '../../server/SharedMediaTypes';
 import { aggregateBounds, OmitKeys, Utils } from '../../Utils';
 import { YoutubeBox } from '../apis/youtube/YoutubeBox';
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 932e94664..f47c48805 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -6,7 +6,7 @@
         transform: translate(-20px, -20px);
     }
 
-    select {
+    .select {
         text-align: justify;
         text-align-last: end
     }
@@ -212,9 +212,9 @@
             cursor: pointer;
         }
 
-        &:hover .padding {
-            white-space: unset;
-        }
+        // &:hover .padding {
+        //     white-space: unset;
+        // }
 
         .padding {
             padding: 0 10px 0 0;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3c05af4bb..5e61f6d3c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -12,6 +12,7 @@ import { Cast, NumCast, PromiseValue, StrCast } from '../../fields/Types';
 import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
 import { Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
+import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
 import { CollectionView } from '../views/collections/CollectionView';
 import { DictationOverlay } from '../views/DictationOverlay';
 import { MainViewModal } from '../views/MainViewModal';
@@ -157,21 +158,42 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${normalizeEmail(user.email)}`;
         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;
+        // }
 
         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]))
             .map(doc => {
                 doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
+                
                 if (permission === SharingPermissions.None) {
                     if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
                 } else {
                     if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
                 }
+                doc = targetDoc
 
                 distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-
                 this.setDashboardBackground(doc, permission as SharingPermissions);
                 if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
                 else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
@@ -190,8 +212,18 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${key}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
-        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
+        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;
+        }
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
@@ -334,13 +366,20 @@ export class SharingManager extends React.Component<{}> {
     //     targetDoc["acl-" + PublicKey] = permission;
     // }
 
-    // private get sharingUrl() {
-    //     if (!this.targetDoc) {
-    //         return undefined;
-    //     }
-    //     const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
-    //     return `${baseUrl}?sharing=true`;
-    // }
+    private get sharingUrl() {
+        if (!this.targetDoc) {
+            return undefined;
+        }
+        const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
+        return `${baseUrl}?sharing=true`;
+    }
+
+    /**
+     * Copies the Public sharing url to the user's clipboard.
+     */
+    private copyURL = (e: any) => {
+        Utils.CopyText(this.sharingUrl!)
+    }
 
     // copy = action(() => {
     //     if (this.sharingUrl) {
@@ -357,7 +396,7 @@ export class SharingManager extends React.Component<{}> {
         if (!uniform) dropdownValues.unshift('-multiple-');
         if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return dropdownValues
-            .filter(permission => !Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any))
+            .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)))
             .map(permission => (
                 <option key={permission} value={permission}>
                     {permission}
@@ -422,6 +461,7 @@ export class SharingManager extends React.Component<{}> {
             this.selectedUsers.forEach(user => {
                 if (user.value.includes(indType)) {
                     this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
+
                 } else {
                     this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
                 }
@@ -466,6 +506,7 @@ export class SharingManager extends React.Component<{}> {
         if (!this.targetDoc) return null;
         TraceMobx();
         const groupList = GroupManager.Instance?.allGroups || [];
+
         const sortedUsers = this.users
             .slice()
             .sort(this.sortUsers)
@@ -505,13 +546,48 @@ 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
-            .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+            // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+            .filter(({ user }) => (docs[0]?.author !== user.email))
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
+                const dashboardList = SelectionManager.Views().length < 2 ? [targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+                // const dashboard = dashboardList[0]
+                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)}`;
                 const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).every(doc => doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey]);
-                const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                const permissions = uniform ? StrCast(docToUse?.[userKey]) : '-multiple-';
 
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
@@ -551,7 +627,7 @@ export class SharingManager extends React.Component<{}> {
                 </div>
             ) : null
         );
-
+    
         // the list of groups shared with
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
@@ -560,11 +636,27 @@ export class SharingManager extends React.Component<{}> {
             const uniform = docs
                 .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
                 .every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
-            const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
+            const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
 
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
-                    <div className={'padding'}>{StrCast(group.title)}</div>
+                    <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' }} />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b2b18b245..3e0a1f7a4 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -6,12 +6,12 @@ import { action, computed, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
 import { DateField } from '../../fields/DateField';
-import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
 import { Document } from '../../fields/documentSchemas';
 import { InkField } from '../../fields/InkField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, SharingPermissions } from '../../fields/util';
+import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util';
 import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -157,27 +157,35 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     };
 
     @action onContainerDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.onBackgroundMove(true, e),
-            e => {},
-            emptyFunction
-        );
+        const first = SelectionManager.Views()[0];
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc)
+        if (effectiveAcl==AclAdmin || effectiveAcl==AclEdit || effectiveAcl==AclAugment){
+            setupMoveUpEvents(
+                this,
+                e,
+                e => this.onBackgroundMove(true, e),
+                e => {},
+                emptyFunction
+            );
+        }
     };
 
     @action onTitleDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.onBackgroundMove(true, e),
-            e => {},
-            action(e => {
-                !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString);
-                this._editingTitle = true;
-                this._keyinput.current && setTimeout(this._keyinput.current.focus);
-            })
-        );
+        const first = SelectionManager.Views()[0];
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc)
+        if (effectiveAcl==AclAdmin || effectiveAcl==AclEdit || effectiveAcl==AclAugment){
+            setupMoveUpEvents(
+                this,
+                e,
+                e => this.onBackgroundMove(true, e),
+                e => {},
+                action(e => {
+                    !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString);
+                    this._editingTitle = true;
+                    this._keyinput.current && setTimeout(this._keyinput.current.focus);
+                })
+            );
+        }
     };
 
     onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
@@ -485,6 +493,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
         const first = SelectionManager.Views()[0];
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc)
+        if (!(effectiveAcl==AclAdmin || effectiveAcl==AclEdit || effectiveAcl==AclAugment)) return false;
         if (!first) return false;
         let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
         var fixedAspect = Doc.NativeAspect(first.layoutDoc);
@@ -737,7 +747,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
 
         // sharing
-        const docShareMode = seldocview.rootDoc["acl-Public"];
+        const docShareMode = Doc.GetProto(seldocview.rootDoc)["acl-Public"];
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch(shareMode){
@@ -781,10 +791,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.props.hideDeleteButton ||
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
-                const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
-                return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                // 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);
             });
-
         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">
                 <div
@@ -852,7 +863,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             <div className={'documentDecorations-share'}>
                 <div className={`documentDecorations-share${shareMode}`}>
                 <span>{shareSymbolIcon + " " + shareMode}</span>
-                {/* <span>{shareMode}</span> */}
                 </div>
             </div> ) : (<div/>
         );
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 3edc7fea8..50bf6ce74 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -384,17 +384,14 @@
         padding: 5px; // remove when adding buttons
         border-radius: 6px; // remove when adding buttons
         margin-right: 10px; // remove when adding buttons
-        // width: 100%;
-        // display: inline-table;
         background-color: #ececec;
         max-height: 130px;
         width: 92%;
 
         .propertiesView-sharingTable-item {
             display: flex;
-            // padding: 5px;
             padding: 3px;
-            align-items: center;
+            align-items: right;
             border-bottom: 0.5px solid grey;
 
             &:hover .propertiesView-sharingTable-item-name {
@@ -429,7 +426,7 @@
         background: inherit;
         border: none;
         background: inherit;
-        width: 87px;
+        // 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 dfc43e6c8..a6a5347ad 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -13,7 +13,7 @@ import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { ComputedField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from '../../fields/util';
+import { denormalizeEmail, GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
@@ -34,6 +34,7 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+import { SourceMapDevToolPlugin } from 'webpack';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -366,11 +367,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     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"){
-            this.selectedDoc!['acl-' +user] = e.currentTarget.value as SharingPermissions;
+            docs[0]['acl-' +user] = e.currentTarget.value as SharingPermissions;
         }
         else{
-            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]));
+
             SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
         }
     };
@@ -381,15 +383,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     getPermissionsSelect(user: string, permission: string) {
         const dropdownValues: string[] = Object.values(SharingPermissions);
         if (permission === '-multiple-') dropdownValues.unshift(permission);
-        if (user !== 'Override') dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
+        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 => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
+                    .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)))
                     .map(permission => (
                         <option className="propertiesView-permisssions-select" key={permission} value={permission}>
-                            {' '}
-                            {permission}{' '}
+                            {' '}{permission}{' '}
                         </option>
                     ))}
             </select>
@@ -432,6 +436,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns a row of the permissions panel
      */
     sharingItem(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
+        if (name==Doc.CurrentUserEmail){
+            name = 'Me';
+        }
         return (
             <div
                 className="propertiesView-sharingTable-item"
@@ -445,7 +452,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 {/* {name !== "Me" ? this.notifyIcon : null} */}
                 <div className="propertiesView-sharingTable-item-permission">
                     {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
-                    {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null}
+                    {(permission === 'Owner' && name=='Me')|| showExpansionIcon ? this.expansionIcon : null}
                 </div>
             </div>
         );
@@ -470,6 +477,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 dropDownText = "▲ ";
                 break;
         }
+
         return (
             <div>
                 <div className={'propertiesView-shareDropDown'}>
@@ -490,7 +498,24 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @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;
         }
@@ -501,44 +526,69 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const target = docs[0];
 
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
-        const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
-        const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
+        // 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 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)}`])
+            if(Doc.ActiveDashboard){
+                if(Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]==undefined){
+                    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
+                usersAdded.push(eachUser.user.email);
+            }
+        });
 
-        // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
-        // if (commonKeys.length) {
-        //     for (const key of commonKeys) {
-        //         const name = denormalizeEmail(key.substring(4));
-        //         const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
-        //         if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
-        //             tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
+        // commonKeys.forEach(user => {
+        //     const userEmail = user.slice(4)
+        //     var userOnDashboard = true;
+        //     if(Doc.ActiveDashboard){
+        //         if(Doc.ActiveDashboard[user]=='' || Doc.ActiveDashboard[user]==undefined){
+        //             userOnDashboard = false;
         //         }
         //     }
-        // }
-        const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
-        // shifts the current user, owner, public to the top of the doc.
-        // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
-        if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
-        // tableEntries.unshift(this.sharingItem(target.author, showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-'));
-        // tableEntries.unshift(
-        //     this.sharingItem(
-        //         'Me',
-        //         showAdmin,
-        //         docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-',
-        //         !ownerSame
-        //     )
-        // );
-
-        SharingManager.Instance.users.forEach(eachUser => tableEntries.unshift(this.sharingItem(eachUser.user.email, false, "test", false)));
-
-        docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, 'Owner', true)));
+        //     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));
+        }
+
+        // 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
 
         return (
-            <div> Sharing Mode
-                <div>{this.publicACLDropDown(true, StrCast(docToUse['acl-Public']), false)}</div>
+            <div > Sharing Mode
+                <div>{
+                    this.publicACLDropDown(true, StrCast(target['acl-Public']), false)}
+                </div>
             <div>  <br></br> Who has access to the Dashboard? </div>
             <div className="propertiesView-sharingTable">{
                 <div> {tableEntries}</div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a2c1195cb..0e4330fa5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -19,7 +19,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
+import { GetEffectiveAcl, normalizeEmail, TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from '../../../DocServer';
@@ -295,11 +295,12 @@ 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(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].includes(effectiveAcl)) {
+            
+            if ([AclEdit, AclAdmin, AclSelfEdit, 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('#')) {
@@ -1689,7 +1690,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) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
+            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
                 e.preventDefault();
             }
         }
@@ -1708,7 +1709,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             default:
                 if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
             case ' ':
-                [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) &&
+                if (e.code == "Space"){
+                    break;
+                }
+                [AclEdit, AclAugment, AclSelfEdit, 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/fields/Doc.ts b/src/fields/Doc.ts
index 4d82551db..5e2e40979 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -135,7 +135,6 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }>
 // this recursively updates all protos as well.
 export function updateCachedAcls(doc: Doc) {
     if (!doc) return;
-
     const target = (doc as any)?.__fields ?? doc;
     const permissions: { [key: string]: symbol } = !target.author || target.author === Doc.CurrentUserEmail ? { 'acl-Me': AclAdmin } : {};
     Object.keys(target).filter(key => key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl));
@@ -1117,7 +1116,7 @@ export namespace Doc {
                 target[targetKey] = new PrefetchProxy(templateDoc);
             } else {
                 titleTarget && (Doc.GetProto(target).title = titleTarget);
-                const setDoc = [AclAdmin, AclEdit].includes(GetEffectiveAcl(Doc.GetProto(target))) ? Doc.GetProto(target) : target;
+                const setDoc = [AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(Doc.GetProto(target))) ? Doc.GetProto(target) : target;
                 setDoc[targetKey] = new PrefetchProxy(templateDoc);
             }
         }
@@ -1295,7 +1294,9 @@ export namespace Doc {
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
-        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) {
+            return DocBrushStatus.unbrushed;
+        }
         const status = brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
         if (status === DocBrushStatus.unbrushed) {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
diff --git a/src/fields/util.ts b/src/fields/util.ts
index eca4d1351..c1e1a7111 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -9,9 +9,11 @@ import { returnZero } from '../Utils';
 import CursorField from './CursorField';
 import {
     AclAdmin,
+    AclAugment,
     AclEdit,
     aclLevel,
     AclPrivate,
+    AclReadonly,
     AclSelfEdit,
     AclSym,
     DataSym,
@@ -37,6 +39,8 @@ import { RichTextField } from './RichTextField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
+import { SharingManager } from '../client/util/SharingManager';
+import { PropertiesView } from '../client/views/PropertiesView';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -82,8 +86,8 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     const writeMode = DocServer.getFieldWriteMode(prop as string);
     const fromServer = target[UpdatingFromServer];
     const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
-    const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
-    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
+    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();
 
     if (writeToDoc) {
         if (value === undefined) {
@@ -139,13 +143,13 @@ export function denormalizeEmail(email: string) {
  * Copies parent's acl fields to the child
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
-    return;
     const dataDoc = parent[DataSym];
     for (const key of Object.keys(dataDoc)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
         const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
         key.startsWith('acl') && distributeAcls(key, permission, child);
     }
+    return;
 }
 
 /**
@@ -182,6 +186,7 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
  * Calculates the effective access right to a document for the current user.
  */
 export function GetEffectiveAcl(target: any, user?: string): symbol {
+    target = Doc.GetProto(target)
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
     return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
@@ -205,8 +210,24 @@ export function SetCachedGroups(groups: string[]) {
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
     const targetAcls = target[AclSym];
-    if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
-
+    if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName(SharingPermissions.Admin)) return AclAdmin;
+    if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)]){
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Admin){
+            return AclAdmin
+        }
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Edit){
+            return AclEdit
+        }
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Augment){
+            return AclAugment
+        }
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.View){
+            return AclReadonly
+        }
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.None){
+            return AclPrivate
+        }
+    }
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
     if (targetAcls && Object.keys(targetAcls).length) {
         let effectiveAcl = AclPrivate;
@@ -225,13 +246,22 @@ function getEffectiveAcl(target: any, user?: string): symbol {
         //const override = targetAcls['acl-Override'];
         // if (override !== AclUnset && override !== undefined) effectiveAcl = override;
 
-        // if we're in playground mode, return AclEdit (or AclAdmin if that's the user's effectiveAcl)
+
         return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
     }
     // authored documents are private until an ACL is set.
     const targetAuthor = target.__fields?.author || target.author; // target may be a Doc of Proxy, so check __fields.author and .author
     if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
     return AclAdmin;
+    let acl = AclPrivate
+    if (user){
+        acl = target['acl-'+user]
+    }
+    else{
+        acl = target['acl-'+normalizeEmail(Doc.CurrentUserEmail)]         
+    }
+    console.log(target['acl-'+normalizeEmail(Doc.CurrentUserEmail)])
+    return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(acl)!.level < aclLevel.editable ? AclEdit : acl;
 }
 /**
  * Recursively distributes the access right for a user across the children of a document and its annotations.
@@ -247,9 +277,11 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 
     if ((target._viewType === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
         target[key] = acl;
+        Doc.GetProto(target)[key] = acl
         if (target !== Doc.GetProto(target)) {
             //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
             updateCachedAcls(target);
+
         }
         return;
     }
@@ -260,7 +292,6 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
         target[key] = acl;
         layoutDocChanged = true;
-
         if (isDashboard) {
             DocListCastAsync(target[Doc.LayoutFieldKey(target)]).then(docs => {
                 docs?.forEach(d => distributeAcls(key, acl, d, inheritingFromCollection, visited));
@@ -299,7 +330,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 !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true;
+    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) 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 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/PropertiesView.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 abcf1167340f9f411e7712d11f2110625b0938d8 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 4 May 2023 15:45:50 -0400
Subject: simple checkbox so that users can choose to override more private acl
 docs

---
 src/client/util/SharingManager.scss |  2 +-
 src/client/util/SharingManager.tsx  | 24 +++++++++++++++---------
 src/client/views/PropertiesView.tsx |  9 +--------
 3 files changed, 17 insertions(+), 18 deletions(-)

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

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 3cab77816..aa1e11ef5 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -85,11 +85,11 @@
                 .layoutDoc-acls,
                 .myDocs-acls {
                     flex-direction: column;
-                    margin-right: 12;
 
                     label {
                         font-weight: normal;
                         font-style: italic;
+                        padding-right: 12;
                     }
 
                     input {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 40a282db0..aa9fc4433 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -82,6 +82,7 @@ export class SharingManager extends React.Component<{}> {
     @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
     private populating: boolean = false; // whether the list of users is populating or not
     @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
+    @observable private overridePrivate: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
     @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
 
     // private get linkVisible() {
@@ -161,10 +162,12 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
         
         // 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)
-        })
+        if (this.overridePrivate){
+            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);
 
@@ -200,10 +203,12 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
         
         // 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)
-        })
+        if (this.overridePrivate){
+            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);
 
@@ -275,7 +280,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 (this.overridePrivate) 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);
             });
@@ -648,6 +653,7 @@ export class SharingManager extends React.Component<{}> {
                             <div className="acl-container">
                                 {Doc.noviceMode ? null : (
                                     <div className="layoutDoc-acls">
+                                        <input type="checkbox" onChange={action(() => (this.overridePrivate = !this.overridePrivate))} checked={this.overridePrivate} /> <label>Override Private </label>
                                         <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
                                     </div>
                                 )}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 984c2dc04..94a6d5639 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -368,14 +368,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @undoBatch
     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;
-            SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
-        }
-        else{
-
-            SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
-        }
+        SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
     };
 
     /**
-- 
cgit v1.2.3-70-g09d2


From c48b9015da3f68e83179fea9cb03cfe9fe7d1331 Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Tue, 6 Jun 2023 17:03:25 -0400
Subject: Context and link message is back

---
 package-lock.json                   | 39 -------------------------------------
 src/client/views/PropertiesView.tsx | 31 ++++++++++++++++++++++++++---
 2 files changed, 28 insertions(+), 42 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 895dec389..70f86a6b9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5955,16 +5955,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3": {
             "version": "7.8.4",
             "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
@@ -7345,28 +7335,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.62",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
-            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -7378,7 +7346,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -23051,12 +23018,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5c3ed39a4..0007ac84f 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -7,7 +7,7 @@ import { intersection } from 'lodash';
 import { action, computed, Lambda, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+import { AclAdmin, AclSym, DataSym, Doc, DocListCast, Field, FieldResult, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -46,6 +46,7 @@ interface PropertiesViewProps {
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
 }
 
+
 @observer
 export class PropertiesView extends React.Component<PropertiesViewProps> {
     private _widthUndo?: UndoManager.Batch;
@@ -244,11 +245,33 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return !this.selectedDoc ? null : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
+    @computed get contextCount(){
+        if (this.selectedDoc != undefined){
+            const aliases = DocListCast(this.selectedDoc.aliases)
+            return aliases.length   
+        } else{
+            return 0;
+        }
+    }
+
     @computed get links() {
+        console.log("this is selected doc" + this.selectedDoc)
+        // console.log("this is selected doc" + this.selectedDoc)
         const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
+        console.log("this is selAnchor" + selAnchor)
         return !selAnchor ? null : <PropertiesDocBacklinksSelector Document={selAnchor} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
+    @computed get linkCount(){
+        const selAnchor =  this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
+        if (selAnchor != undefined){
+            const links = DocListCast(selAnchor.links)
+            return links.length
+        } else{
+            return 0
+        }
+    }
+
     @computed get layoutPreview() {
         if (SelectionManager.Views().length > 1) {
             return '-- multiple selected --';
@@ -1118,21 +1141,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         <FontAwesomeIcon icon={this.openContexts ? 'caret-down' : 'caret-right'} size="lg" color="white" />
                     </div>
                 </div>
-                {this.openContexts ? <div className="propertiesView-contexts-content">{this.contexts}</div> : null}
+                {!this.openContexts ? null : this.contextCount > 0 ? <div className="propertiesView-contexts-content">{this.contexts}</div> : <div className="propertiesView-contexts-content">There are no other contexts.</div>}
             </div>
         );
     }
 
     @computed get linksSubMenu() {
+        
         return (
             <div className="propertiesView-contexts">
+            
                 <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
                     Linked To
                     <div className="propertiesView-contexts-title-icon">
                         <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
                     </div>
                 </div>
-                {this.openLinks ? <div className="propertiesView-contexts-content">{this.links}</div> : null}
+                {!this.openLinks ? null : this.linkCount > 0 ? <div className="propertiesView-contexts-content">{this.links}</div> : <div className="propertiesView-contexts-content">There are no current links.</div>}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 7ba42015e6cc197393254205459d94d681e26f61 Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Tue, 6 Jun 2023 17:28:13 -0400
Subject: started changing top section of prop menu

---
 src/client/views/PropertiesView.scss | 17 +++++++++++++----
 src/client/views/PropertiesView.tsx  |  9 +++++++--
 2 files changed, 20 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 897be9a32..5e6bd85e6 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -15,13 +15,14 @@
     overflow-y: auto;
 
     .propertiesView-title {
-        text-align: center;
+        text-align: left;
         padding-top: 12px;
-        padding-bottom: 12px;
+        // padding-bottom: 12px;
+        padding-left: 10px;
         display: flex;
-        font-size: 18px;
+        font-size: 25px;
         font-weight: bold;
-        justify-content: center;
+        // justify-content: center;
 
         .propertiesView-title-icon {
             width: 20px;
@@ -830,8 +831,16 @@
     }
 }
 
+.propertiesView-wordTitle{
+    color:silver;
+    font-weight:200;
+}
+
 .editable-title {
     border: solid 1px #323232;
+    padding-left: 5px;
+    padding-top: 4px;
+    border-radius: 4px;
     height: fit-content;
 
     &:hover {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 0007ac84f..3c473498e 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -466,9 +466,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
         const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
         return (
-            <div className="editable-title">
-                <EditableView key="editableView" contents={title} height={25} fontSize={14} GetValue={() => title} SetValue={this.setTitle} />
+            <div>
+                <div className="propertiesView-wordTitle">Title</div> 
+                <div className="editable-title">
+                    <EditableView key="editableView" contents={title} height={25} fontSize={14} GetValue={() => title} SetValue={this.setTitle} />
+                </div>
             </div>
+            
         );
     }
 
@@ -1659,6 +1663,7 @@ 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>
-- 
cgit v1.2.3-70-g09d2


From 6707f6ad559b463e514bdbbd634ded25bae4a5f7 Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Wed, 7 Jun 2023 13:35:24 -0400
Subject: got types but trying to get icon

---
 src/client/documents/DocumentTypes.ts |  2 +-
 src/client/documents/Documents.ts     |  3 +-
 src/client/views/PropertiesView.scss  | 27 ++++++++++++++++-
 src/client/views/PropertiesView.tsx   | 55 ++++++++++++++++++++++++++++++++---
 4 files changed, 80 insertions(+), 7 deletions(-)

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

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index cdc8c275c..bd71281fa 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -2,7 +2,7 @@ export enum DocumentType {
     NONE = 'none',
 
     // core data types
-    RTF = 'rtf',
+    RTF = 'rich text',
     IMG = 'image',
     WEB = 'web',
     COL = 'collection',
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 515a870b5..f877f2598 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -430,7 +430,8 @@ export namespace Docs {
                         nativeHeightUnfrozen: true,
                         layout_forceReflow: true,
                         defaultDoubleClick: 'ignore',
-                    },
+                        systemIcon: 'AiFillFileText'
+                    }, 
                 },
             ],
             [
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 5e6bd85e6..b3d16d82a 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -41,7 +41,7 @@
     }
 
     .propertiesView-name {
-        border-bottom: 1px solid black;
+       // border-bottom: 1px solid black;
         padding: 8.5px;
         font-size: 12.5px;
 
@@ -50,6 +50,11 @@
         }
     }
 
+    .propertiesView-type{
+        padding: 8.5px;
+        font-size: 12.5px;
+    }
+
     .propertiesView-settings {
         //border-bottom: 1px solid black;
         //padding: 8.5px;
@@ -848,6 +853,26 @@
     }
 }
 
+.propertiesView-wordType{
+    color:silver;
+    font-weight:200;
+}
+
+.currentType{
+    text-decoration: underline;
+    display: flex;
+    align-items:center;
+    // border: solid 1px #323232;
+    // padding-left: 5px;
+    // padding-top: 4px;
+    // border-radius: 4px;
+    // height: fit-content;
+}
+
+.currentType-icon{
+    margin-right:5px;
+}
+
 .properties-flyout {
     grid-column: 2/4;
 }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 3c473498e..c5dd0eaf5 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -2,7 +2,7 @@ import React = require('react');
 import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Checkbox, Tooltip } from '@material-ui/core';
+import { Checkbox, Icon, Tooltip } from '@material-ui/core';
 import { intersection } from 'lodash';
 import { action, computed, Lambda, observable } from 'mobx';
 import { observer } from 'mobx-react';
@@ -34,6 +34,13 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+import { RichTextField } from '../../fields/RichTextField';
+import { AiFillFileText } from "react-icons/ai"
+import {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import { CgBrowser} from "react-icons/cg"
+import { ImageField, VideoField, WebField } from '../../fields/URLField';
+import { FaFileVideo } from 'react-icons/fa';
+import { IconButton } from 'browndash-components';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -255,10 +262,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get links() {
-        console.log("this is selected doc" + this.selectedDoc)
-        // console.log("this is selected doc" + this.selectedDoc)
         const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
-        console.log("this is selAnchor" + selAnchor)
         return !selAnchor ? null : <PropertiesDocBacklinksSelector Document={selAnchor} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
@@ -462,6 +466,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     };
 
     @computed get editableTitle() {
+
         const titles = new Set<string>();
         SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
         const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
@@ -476,6 +481,45 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    @computed get currentType() {
+       // const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)];
+        const documentType = StrCast(this.selectedDoc?.type)
+        //console.log("this is tyoe " + typezzz)
+        var currentType: string = "";
+        var currentTypeComponent =  <BsFillFileEarmarkImageFill/>;
+    
+            currentType = documentType
+           // currentTypeComponent = //c
+         //}// else if (layoutField instanceof ImageField){
+        //     currentType = 'Image'
+        //     currentTypeComponent = <BsFillFileEarmarkImageFill/>
+        // } else if(layoutField instanceof WebField){
+        //     currentType = 'Website'
+        //     currentTypeComponent = <CgBrowser/>
+        // } else if (layoutField instanceof VideoField){
+        //     currentType = 'Video'
+        //     currentTypeComponent = <FaFileVideo/>
+        // }
+        
+        // else{
+        // currentType = 'Collection'
+        // currentTypeComponent = <BsCollectionFill/>
+        // }
+
+        return (
+            <div>
+                <div className = "propertiesView-wordType">Type</div>
+                
+                <div className= "currentType"> 
+                {/* <Icon iconName = "AiFillFileText"></Icon> */}
+                {/* <IconButton icon={StrCast(this.selectedDoc?.systemIcon)}></IconButton> */}
+                {currentType} 
+                </div>
+            </div>
+          
+        )
+    }
+
     @undoBatch
     @action
     setTitle = (value: string) => {
@@ -1420,6 +1464,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         </div>
                         <div className="propertiesView-name">{this.editableTitle}</div>
 
+                        <div className = "propertiesView-type"> {this.currentType} </div>
+                        
+
                         {this.contextsSubMenu}
 
                         {this.linksSubMenu}
-- 
cgit v1.2.3-70-g09d2


From ae956f7e5c3d4f95a7a59ce21c79ec0ce665069b Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Thu, 8 Jun 2023 10:11:13 -0400
Subject: staring on thursday

---
 src/client/views/PropertiesButtons.tsx | 435 +++++++++++++++++----------------
 src/client/views/PropertiesView.tsx    |   4 +-
 2 files changed, 224 insertions(+), 215 deletions(-)

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

diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 76828a576..2c88eacc6 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
+import { Icon, Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
@@ -21,6 +21,9 @@ import { DocumentView, OpenWhere } from './nodes/DocumentView';
 import { pasteImageBitmap } from './nodes/WebBoxRenderer';
 import './PropertiesButtons.scss';
 import React = require('react');
+import { JsxElement } from 'typescript';
+import { FaThumbtack } from 'react-icons/fa';
+import { AiOutlineApple } from 'react-icons/ai';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -30,6 +33,7 @@ enum UtilityButtonState {
     OpenRight,
     OpenExternally,
 }
+
 @observer
 export class PropertiesButtons extends React.Component<{}, {}> {
     @observable public static Instance: PropertiesButtons;
@@ -41,7 +45,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return !SelectionManager.SelectedSchemaDoc() && SelectionManager.Views().lastElement()?.topMost;
     }
 
-    propertyToggleBtn = (label: string, property: string, tooltip: (on?: any) => string, icon: (on: boolean) => string, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
+    propertyToggleBtn = (label: string, property: string, tooltip: (on?: any) => string, icon: (on: boolean) => any, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
         const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedDoc;
         const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => ((dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true);
         return !targetDoc ? null : (
@@ -55,7 +59,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                                 SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property));
                             } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
                         }, property)}>
-                        <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon={icon(BoolCast(targetDoc?.[property])) as any} />
+                        {icon((BoolCast(targetDoc?.[property])) as any)}
+                    
+                        {/* <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon={icon(BoolCast(targetDoc?.[property])) as any} /> */}
                     </div>
                     <div className="propertiesButtons-title">{label}</div>
                 </div>
@@ -67,216 +73,217 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             'No\xA0Drag',
             '_lockedPosition',
             on => `${on ? 'Unlock' : 'Lock'} position to prevent dragging`,
-            on => 'thumbtack'
-        );
-    }
-    @computed get maskButton() {
-        return this.propertyToggleBtn(
-            'Mask',
-            'stroke_isInkMask',
-            on => (on ? 'Make plain ink' : 'Make highlight mask'),
-            on => 'paint-brush',
-            (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
-        );
-    }
-    @computed get hideImageButton() {
-        return this.propertyToggleBtn(
-            'Background',
-            '_hideImage',
-            on => (on ? 'Show Image' : 'Show Background'),
-            on => 'portrait'
-        );
-    }
-    @computed get clustersButton() {
-        return this.propertyToggleBtn(
-            'Clusters',
-            '_freeform_useClusters',
-            on => `${on ? 'Hide' : 'Show'} clusters`,
-            on => 'braille'
-        );
-    }
-    @computed get panButton() {
-        return this.propertyToggleBtn(
-            'Lock\xA0View',
-            '_lockedTransform',
-            on => `${on ? 'Unlock' : 'Lock'} panning of view`,
-            on => 'lock'
-        );
-    }
-    @computed get forceActiveButton() {
-        return this.propertyToggleBtn(
-            'Active',
-            '_forceActive',
-            on => `${on ? 'Select to activate' : 'Contents always active'} `,
-            on => 'eye'
-        );
-    }
-    @computed get fitContentButton() {
-        return this.propertyToggleBtn(
-            'View All',
-            '_freeform_fitContentsToBox',
-            on => `${on ? "Don't" : 'Do'} fit content to container visible area`,
-            on => 'object-group'
-        );
-    }
-    // this implments a container pattern by marking the targetDoc (collection) as a lightbox
-    // that always fits its contents to its container and that hides all other documents when
-    // a link is followed that targets a 'lightbox' destination
-    @computed get isLightboxButton() {
-        return this.propertyToggleBtn(
-            'Lightbox',
-            'isLightbox',
-            on => `${on ? 'Set' : 'Remove'} lightbox flag`,
-            on => 'window-restore',
-            onClick => {
-                SelectionManager.Views().forEach(dv => {
-                    const containerDoc = dv.rootDoc;
-                    //containerDoc.followAllLinks =
-                    // containerDoc.noShadow =
-                    // containerDoc.disableDocBrushing =
-                    // containerDoc._forceActive =
-                    //containerDoc._freeform_fitContentsToBox =
-                    containerDoc._isLightbox = !containerDoc._isLightbox;
-                    //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
-                    const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
-                    //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
-                    containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false)));
-                });
-            }
-        );
-    }
-    @computed get layout_fitWidthButton() {
-        return this.propertyToggleBtn(
-            'Fit\xA0Width',
-            '_layout_fitWidth',
-            on => `${on ? "Don't" : 'Do'} fit content to width of container`,
-            on => 'arrows-alt-h'
-        );
-    }
-    @computed get captionButton() {
-        return this.propertyToggleBtn(
-            'Caption',
-            '_layout_showCaption',
-            on => `${on ? 'Hide' : 'Show'} caption footer`,
-            on => 'closed-captioning',
-            (dv, doc) => ((dv?.rootDoc || doc)._layout_showCaption = (dv?.rootDoc || doc)._layout_showCaption === undefined ? 'caption' : undefined)
-        );
-    }
-    @computed get chromeButton() {
-        return this.propertyToggleBtn(
-            'Controls',
-            '_chromeHidden',
-            on => `${on ? 'Show' : 'Hide'} editing UI`,
-            on => 'edit',
-            (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden)
-        );
-    }
-    @computed get titleButton() {
-        return this.propertyToggleBtn(
-            'Title',
-            '_layout_showTitle',
-            on => 'Switch between title styles',
-            on => 'text-width',
-            (dv, doc) => {
-                const tdoc = dv?.rootDoc || doc;
-                const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : '';
-                tdoc._layout_showTitle = newtitle;
-            }
-        );
-    }
-    @computed get layout_autoHeightButton() {
-        return this.propertyToggleBtn(
-            'Auto\xA0Size',
-            '_layout_autoHeight',
-            on => `Automatical vertical sizing to show all content`,
-            on => 'arrows-alt-v'
-        );
-    }
-    @computed get gridButton() {
-        return this.propertyToggleBtn(
-            'Grid',
-            '_freeform_backgroundGrid',
-            on => `Display background grid in collection`,
-            on => 'border-all'
-        );
-    }
-    @computed get groupButton() {
-        return this.propertyToggleBtn(
-            'Group',
-            'isGroup',
-            on => `Display collection as a Group`,
-            on => 'object-group',
-            (dv, doc) => {
-                doc.isGroup = !doc.isGroup;
-                doc.forceActive = doc.isGroup;
-            }
-        );
-    }
-    @computed get freezeThumb() {
-        return this.propertyToggleBtn(
-            'FreezeThumb',
-            '_thumb-frozen',
-            on => `${on ? 'Freeze' : 'Unfreeze'} thumbnail`,
-            on => 'snowflake',
-            (dv, doc) => {
-                if (doc['thumb-frozen']) doc['thumb-frozen'] = undefined;
-                else {
-                    document.body.focus(); // so that we can access the clipboard without an error
-                    setTimeout(() =>
-                        pasteImageBitmap((data_url: any, error: any) => {
-                            error && console.log(error);
-                            data_url && Utils.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename)));
-                        })
-                    );
-                }
-            }
-        );
-    }
-    @computed get snapButton() {
-        return this.propertyToggleBtn(
-            'Snap\xA0Lines',
-            'freeform_snapLines',
-            on => `Display snapping lines when objects are dragged`,
-            on => 'th',
-            undefined,
-            true
+            on => <AiOutlineApple/>
+           // on => 'thumbtack'
         );
     }
+    // @computed get maskButton() {
+    //     return this.propertyToggleBtn(
+    //         'Mask',
+    //         'stroke_isInkMask',
+    //         on => (on ? 'Make plain ink' : 'Make highlight mask'),
+    //         on => 'paint-brush',
+    //         (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
+    //     );
+    // }
+    // @computed get hideImageButton() {
+    //     return this.propertyToggleBtn(
+    //         'Background',
+    //         '_hideImage',
+    //         on => (on ? 'Show Image' : 'Show Background'),
+    //         on => 'portrait'
+    //     );
+    // }
+    // @computed get clustersButton() {
+    //     return this.propertyToggleBtn(
+    //         'Clusters',
+    //         '_freeform_useClusters',
+    //         on => `${on ? 'Hide' : 'Show'} clusters`,
+    //         on => 'braille'
+    //     );
+    // }
+    // @computed get panButton() {
+    //     return this.propertyToggleBtn(
+    //         'Lock\xA0View',
+    //         '_lockedTransform',
+    //         on => `${on ? 'Unlock' : 'Lock'} panning of view`,
+    //         on => 'lock'
+    //     );
+    // }
+    // @computed get forceActiveButton() {
+    //     return this.propertyToggleBtn(
+    //         'Active',
+    //         '_forceActive',
+    //         on => `${on ? 'Select to activate' : 'Contents always active'} `,
+    //         on => 'eye'
+    //     );
+    // }
+    // @computed get fitContentButton() {
+    //     return this.propertyToggleBtn(
+    //         'View All',
+    //         '_freeform_fitContentsToBox',
+    //         on => `${on ? "Don't" : 'Do'} fit content to container visible area`,
+    //         on => 'object-group'
+    //     );
+    // }
+    // // this implments a container pattern by marking the targetDoc (collection) as a lightbox
+    // // that always fits its contents to its container and that hides all other documents when
+    // // a link is followed that targets a 'lightbox' destination
+    // @computed get isLightboxButton() {
+    //     return this.propertyToggleBtn(
+    //         'Lightbox',
+    //         'isLightbox',
+    //         on => `${on ? 'Set' : 'Remove'} lightbox flag`,
+    //         on => 'window-restore',
+    //         onClick => {
+    //             SelectionManager.Views().forEach(dv => {
+    //                 const containerDoc = dv.rootDoc;
+    //                 //containerDoc.followAllLinks =
+    //                 // containerDoc.noShadow =
+    //                 // containerDoc.disableDocBrushing =
+    //                 // containerDoc._forceActive =
+    //                 //containerDoc._freeform_fitContentsToBox =
+    //                 containerDoc._isLightbox = !containerDoc._isLightbox;
+    //                 //containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
+    //                 const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
+    //                 //dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
+    //                 containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.layout_linkDisplay = false)));
+    //             });
+    //         }
+    //     );
+    // }
+    // @computed get layout_fitWidthButton() {
+    //     return this.propertyToggleBtn(
+    //         'Fit\xA0Width',
+    //         '_layout_fitWidth',
+    //         on => `${on ? "Don't" : 'Do'} fit content to width of container`,
+    //         on => 'arrows-alt-h'
+    //     );
+    // }
+    // @computed get captionButton() {
+    //     return this.propertyToggleBtn(
+    //         'Caption',
+    //         '_layout_showCaption',
+    //         on => `${on ? 'Hide' : 'Show'} caption footer`,
+    //         on => 'closed-captioning',
+    //         (dv, doc) => ((dv?.rootDoc || doc)._layout_showCaption = (dv?.rootDoc || doc)._layout_showCaption === undefined ? 'caption' : undefined)
+    //     );
+    // }
+    // @computed get chromeButton() {
+    //     return this.propertyToggleBtn(
+    //         'Controls',
+    //         '_chromeHidden',
+    //         on => `${on ? 'Show' : 'Hide'} editing UI`,
+    //         on => 'edit',
+    //         (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden)
+    //     );
+    // }
+    // @computed get titleButton() {
+    //     return this.propertyToggleBtn(
+    //         'Title',
+    //         '_layout_showTitle',
+    //         on => 'Switch between title styles',
+    //         on => 'text-width',
+    //         (dv, doc) => {
+    //             const tdoc = dv?.rootDoc || doc;
+    //             const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : '';
+    //             tdoc._layout_showTitle = newtitle;
+    //         }
+    //     );
+    // }
+    // @computed get layout_autoHeightButton() {
+    //     return this.propertyToggleBtn(
+    //         'Auto\xA0Size',
+    //         '_layout_autoHeight',
+    //         on => `Automatical vertical sizing to show all content`,
+    //         on => 'arrows-alt-v'
+    //     );
+    // }
+    // @computed get gridButton() {
+    //     return this.propertyToggleBtn(
+    //         'Grid',
+    //         '_freeform_backgroundGrid',
+    //         on => `Display background grid in collection`,
+    //         on => 'border-all'
+    //     );
+    // }
+    // @computed get groupButton() {
+    //     return this.propertyToggleBtn(
+    //         'Group',
+    //         'isGroup',
+    //         on => `Display collection as a Group`,
+    //         on => 'object-group',
+    //         (dv, doc) => {
+    //             doc.isGroup = !doc.isGroup;
+    //             doc.forceActive = doc.isGroup;
+    //         }
+    //     );
+    // }
+    // @computed get freezeThumb() {
+    //     return this.propertyToggleBtn(
+    //         'FreezeThumb',
+    //         '_thumb-frozen',
+    //         on => `${on ? 'Freeze' : 'Unfreeze'} thumbnail`,
+    //         on => 'snowflake',
+    //         (dv, doc) => {
+    //             if (doc['thumb-frozen']) doc['thumb-frozen'] = undefined;
+    //             else {
+    //                 document.body.focus(); // so that we can access the clipboard without an error
+    //                 setTimeout(() =>
+    //                     pasteImageBitmap((data_url: any, error: any) => {
+    //                         error && console.log(error);
+    //                         data_url && Utils.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename)));
+    //                     })
+    //                 );
+    //             }
+    //         }
+    //     );
+    // }
+    // @computed get snapButton() {
+    //     return this.propertyToggleBtn(
+    //         'Snap\xA0Lines',
+    //         'freeform_snapLines',
+    //         on => `Display snapping lines when objects are dragged`,
+    //         on => 'th',
+    //         undefined,
+    //         true
+    //     );
+    // }
 
-    @computed
-    get onClickButton() {
-        return !this.selectedDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">Choose onClick behavior</div>} placement="top">
-                <div>
-                    <div className="propertiesButtons-linkFlyout">
-                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onClickFlyout}>
-                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
-                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                            </div>
-                        </Flyout>
-                    </div>
-                    <div className="propertiesButtons-title"> onclick </div>
-                </div>
-            </Tooltip>
-        );
-    }
-    @computed
-    get perspectiveButton() {
-        return !this.selectedDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
-                <div>
-                    <div className="propertiesButtons-linkFlyout">
-                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
-                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
-                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                            </div>
-                        </Flyout>
-                    </div>
-                    <div className="propertiesButtons-title"> Perspective </div>
-                </div>
-            </Tooltip>
-        );
-    }
+    // @computed
+    // get onClickButton() {
+    //     return !this.selectedDoc ? null : (
+    //         <Tooltip title={<div className="dash-tooltip">Choose onClick behavior</div>} placement="top">
+    //             <div>
+    //                 <div className="propertiesButtons-linkFlyout">
+    //                     <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onClickFlyout}>
+    //                         <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
+    //                             <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
+    //                         </div>
+    //                     </Flyout>
+    //                 </div>
+    //                 <div className="propertiesButtons-title"> onclick </div>
+    //             </div>
+    //         </Tooltip>
+    //     );
+    // }
+    // @computed
+    // get perspectiveButton() {
+    //     return !this.selectedDoc ? null : (
+    //         <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
+    //             <div>
+    //                 <div className="propertiesButtons-linkFlyout">
+    //                     <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
+    //                         <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
+    //                             <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
+    //                         </div>
+    //                     </Flyout>
+    //                 </div>
+    //                 <div className="propertiesButtons-title"> Perspective </div>
+    //             </div>
+    //         </Tooltip>
+    //     );
+    // }
 
     @undoBatch
     handlePerspectiveChange = (e: any) => {
@@ -407,10 +414,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isNovice = Doc.noviceMode;
         return !this.selectedDoc ? null : (
             <div className="propertiesButtons">
-                {toggle(this.titleButton)}
-                {toggle(this.captionButton)}
+                {/* {toggle(this.titleButton)}
+                {toggle(this.captionButton)} */}
                 {toggle(this.lockButton)}
-                {toggle(this.onClickButton)}
+                {/* {toggle(this.onClickButton)}
                 {toggle(this.layout_fitWidthButton)}
                 {toggle(this.freezeThumb)}
                 {toggle(this.forceActiveButton)}
@@ -425,7 +432,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.snapButton, { display: !isCollection ? 'none' : '' })}
                 {toggle(this.clustersButton, { display: !isFreeForm ? 'none' : '' })}
                 {toggle(this.panButton, { display: !isFreeForm ? 'none' : '' })}
-                {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? 'none' : '' })}
+                {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? 'none' : '' })} */}
             </div>
         );
     }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index c5dd0eaf5..5ba71b815 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -486,7 +486,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const documentType = StrCast(this.selectedDoc?.type)
         //console.log("this is tyoe " + typezzz)
         var currentType: string = "";
-        var currentTypeComponent =  <BsFillFileEarmarkImageFill/>;
+        var currentTypeComponent = <BsFillFileEarmarkImageFill/>;
     
             currentType = documentType
            // currentTypeComponent = //c
@@ -511,8 +511,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 <div className = "propertiesView-wordType">Type</div>
                 
                 <div className= "currentType"> 
+
                 {/* <Icon iconName = "AiFillFileText"></Icon> */}
                 {/* <IconButton icon={StrCast(this.selectedDoc?.systemIcon)}></IconButton> */}
+                {/* {currentTypeComponent} */}
                 {currentType} 
                 </div>
             </div>
-- 
cgit v1.2.3-70-g09d2


From baf6955c9c4d4dc79df072778449f882aa63d68e Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Thu, 8 Jun 2023 13:37:37 -0400
Subject: got icon through string working but broke something in links

---
 src/client/documents/Documents.ts   | 18 ++++-----
 src/client/views/PropertiesView.tsx | 78 +++++++++++++++++++++----------------
 2 files changed, 54 insertions(+), 42 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f877f2598..e5168c882 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -430,7 +430,7 @@ export namespace Docs {
                         nativeHeightUnfrozen: true,
                         layout_forceReflow: true,
                         defaultDoubleClick: 'ignore',
-                        systemIcon: 'AiFillFileText'
+                        systemIcon : 'BsFileEarmarkTextFill'
                     }, 
                 },
             ],
@@ -452,21 +452,21 @@ export namespace Docs {
                 DocumentType.IMG,
                 {
                     layout: { view: ImageBox, dataField: defaultDataKey },
-                    options: { freeform: '' },
+                    options: { freeform: '', systemIcon: 'BsFileEarmarkImageFill' },
                 },
             ],
             [
                 DocumentType.WEB,
                 {
                     layout: { view: WebBox, dataField: defaultDataKey },
-                    options: { _height: 300, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' },
+                    options: { _height: 300, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' },
                 },
             ],
             [
                 DocumentType.COL,
                 {
                     layout: { view: CollectionView, dataField: defaultDataKey },
-                    options: { _layout_fitWidth: true, freeform: '', _freeform_panX: 0, _freeform_panY: 0, _freeform_scale: 1 },
+                    options: { _layout_fitWidth: true, freeform: '', _freeform_panX: 0, _freeform_panY: 0, _freeform_scale: 1, systemIcon: 'BsFillCollectionFill' },
                 },
             ],
             [
@@ -480,35 +480,35 @@ export namespace Docs {
                 DocumentType.VID,
                 {
                     layout: { view: VideoBox, dataField: defaultDataKey },
-                    options: { _layout_currentTimecode: 0 },
+                    options: { _layout_currentTimecode: 0, systemIcon: 'BsFileEarmarkPlayFill' },
                 },
             ],
             [
                 DocumentType.AUDIO,
                 {
                     layout: { view: AudioBox, dataField: defaultDataKey },
-                    options: { _height: 100, layout_fitWidth: true, layout_forceReflow: true, nativeDimModifiable: true },
+                    options: { _height: 100, layout_fitWidth: true, layout_forceReflow: true, nativeDimModifiable: true, systemIcon: 'BsFillVolumeUpFill' },
                 },
             ],
             [
                 DocumentType.REC,
                 {
                     layout: { view: VideoBox, dataField: defaultDataKey },
-                    options: { _height: 100, backgroundColor: 'pink' },
+                    options: { _height: 100, backgroundColor: 'pink', systemIcon: 'BsFillMicFill' },
                 },
             ],
             [
                 DocumentType.PDF,
                 {
                     layout: { view: PDFBox, dataField: defaultDataKey },
-                    options: { _layout_curPage: 1, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true },
+                    options: { _layout_curPage: 1, _layout_fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, systemIcon: 'BsFileEarmarkPdfFill' },
                 },
             ],
             [
                 DocumentType.MAP,
                 {
                     layout: { view: MapBox, dataField: defaultDataKey },
-                    options: { _height: 600, _width: 800, nativeDimModifiable: true },
+                    options: { _height: 600, _width: 800, nativeDimModifiable: true, systemIcon: 'BsFillPinMapFill' },
                 },
             ],
             [
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5ba71b815..a6c94d0d9 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -35,12 +35,14 @@ import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
 import { RichTextField } from '../../fields/RichTextField';
-import { AiFillFileText } from "react-icons/ai"
-import {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import { AiFillFileText } from "react-icons/ai" //* as Icons from "react-icons/ai" //
+import * as Icons from "react-icons/bs" //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
 import { CgBrowser} from "react-icons/cg"
 import { ImageField, VideoField, WebField } from '../../fields/URLField';
-import { FaFileVideo } from 'react-icons/fa';
+import { FaFileVideo } from 'react-icons/fa'; //* as Icons from "react-icons/fa"; //
 import { IconButton } from 'browndash-components';
+import { IconBase } from 'react-icons';
+import { MdOutlineMedicalServices } from 'react-icons/md';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -268,8 +270,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @computed get linkCount(){
         const selAnchor =  this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
+      
+        if (this.links === null){
+            console.log("this is where i want to be")
+        }
+        console.log("this is based on links() " + this.links?.key)
+
         if (selAnchor != undefined){
+            console.log("thsi si selAnchr " + selAnchor.links)
             const links = DocListCast(selAnchor.links)
+            console.log("we linkin yo " + links)
             return links.length
         } else{
             return 0
@@ -481,42 +491,43 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    @computed get currentComponent() {
+
+        var iconName = StrCast(this.selectedDoc?.systemIcon)
+
+        // if (this.selectedDoc?.type === DocumentType.COL){
+        //     console.log("i did it!")
+        // }
+       
+
+        if (iconName){
+            const Icon = Icons[iconName as keyof typeof Icons];
+            return <Icon />;
+        } else{
+          return <Icons.BsFillCollectionFill/>
+
+        }
+    }
+
     @computed get currentType() {
-       // const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)];
+        console.log("current type " + this.selectedDoc?.type)
+
         const documentType = StrCast(this.selectedDoc?.type)
-        //console.log("this is tyoe " + typezzz)
-        var currentType: string = "";
-        var currentTypeComponent = <BsFillFileEarmarkImageFill/>;
-    
-            currentType = documentType
-           // currentTypeComponent = //c
-         //}// else if (layoutField instanceof ImageField){
-        //     currentType = 'Image'
-        //     currentTypeComponent = <BsFillFileEarmarkImageFill/>
-        // } else if(layoutField instanceof WebField){
-        //     currentType = 'Website'
-        //     currentTypeComponent = <CgBrowser/>
-        // } else if (layoutField instanceof VideoField){
-        //     currentType = 'Video'
-        //     currentTypeComponent = <FaFileVideo/>
-        // }
-        
-        // else{
-        // currentType = 'Collection'
-        // currentTypeComponent = <BsCollectionFill/>
-        // }
+        var currentType: string = documentType;
+        var capitalizedDocType = currentType.charAt(0).toUpperCase() + currentType.slice(1)
 
         return (
             <div>
-                <div className = "propertiesView-wordType">Type</div>
-                
+                <div className = "propertiesView-wordType">Type</div>  
                 <div className= "currentType"> 
-
-                {/* <Icon iconName = "AiFillFileText"></Icon> */}
-                {/* <IconButton icon={StrCast(this.selectedDoc?.systemIcon)}></IconButton> */}
-                {/* {currentTypeComponent} */}
-                {currentType} 
+                <div className='currentType-icon'>
+                     {this.currentComponent}
+                </div>
+               
+                  {capitalizedDocType} 
+               
                 </div>
+               
             </div>
           
         )
@@ -1207,7 +1218,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
                     </div>
                 </div>
-                {!this.openLinks ? null : this.linkCount > 0 ? <div className="propertiesView-contexts-content">{this.links}</div> : <div className="propertiesView-contexts-content">There are no current links.</div>}
+                {/* {!this.openLinks ? null : this.linkCount > 0 ? <div className="propertiesView-contexts-content">{this.links}</div> : <div className="propertiesView-contexts-content">There are no current links.</div>} */}
+                {!this.openLinks ? null : <div className="propertiesView-contexts-content">{this.links}</div>}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From aae37436b520e03c4d403f22bd61c9849c1938ec Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Thu, 8 Jun 2023 16:05:45 -0400
Subject: fixed linking bug; added more icons

- cant add icon for simulation and equation; look at more types to add icons
---
 src/client/documents/Documents.ts   |  6 +++---
 src/client/util/CaptureManager.tsx  |  1 -
 src/client/views/PropertiesView.tsx | 25 ++++++++++---------------
 3 files changed, 13 insertions(+), 19 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e5168c882..8f62a484f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -575,7 +575,7 @@ export namespace Docs {
                 DocumentType.EQUATION,
                 {
                     layout: { view: EquationBox, dataField: 'text' },
-                    options: { nativeDimModifiable: true, fontSize: '14px', layout_hideResizeHandles: true, layout_hideDecorationTitle: true },
+                    options: { nativeDimModifiable: true, fontSize: '14px', layout_hideResizeHandles: true, layout_hideDecorationTitle: true},
                 },
             ],
             [
@@ -639,7 +639,7 @@ export namespace Docs {
                 {
                     // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
                     layout: { view: InkingStroke, dataField: 'stroke' },
-                    options: {},
+                    options: { systemIcon: 'BsFillPencilFill'},
                 },
             ],
             [
@@ -691,7 +691,7 @@ export namespace Docs {
                 {
                     data: '',
                     layout: { view: PhysicsSimulationBox, dataField: defaultDataKey, _width: 1000, _height: 800 },
-                    options: { _height: 100, layout_forceReflow: true, nativeHeightUnfrozen: true, mass1: '', mass2: '', nativeDimModifiable: true, position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: '' },
+                    options: { _height: 100, layout_forceReflow: true, nativeHeightUnfrozen: true, mass1: '', mass2: '', nativeDimModifiable: true, position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: ''},
                 },
             ],
         ]);
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index d68761ba7..f42336ee7 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -58,7 +58,6 @@ export class CaptureManager extends React.Component<{}> {
                 )
             );
         }
-
         return (
             <div className="capture-block">
                 <div className="capture-block-title">Links</div>
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index a6c94d0d9..d956c874c 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -270,20 +270,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @computed get linkCount(){
         const selAnchor =  this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc;
-      
-        if (this.links === null){
-            console.log("this is where i want to be")
-        }
-        console.log("this is based on links() " + this.links?.key)
+        var counter = 0;
 
-        if (selAnchor != undefined){
-            console.log("thsi si selAnchr " + selAnchor.links)
-            const links = DocListCast(selAnchor.links)
-            console.log("we linkin yo " + links)
-            return links.length
-        } else{
-            return 0
-        }
+        LinkManager.Links(selAnchor).forEach((l, i) =>
+                counter ++
+            );
+
+        return counter;
     }
 
     @computed get layoutPreview() {
@@ -1208,6 +1201,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get linksSubMenu() {
+    //     this.linkCount
+    //    this.links
         
         return (
             <div className="propertiesView-contexts">
@@ -1218,8 +1213,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
                     </div>
                 </div>
-                {/* {!this.openLinks ? null : this.linkCount > 0 ? <div className="propertiesView-contexts-content">{this.links}</div> : <div className="propertiesView-contexts-content">There are no current links.</div>} */}
-                {!this.openLinks ? null : <div className="propertiesView-contexts-content">{this.links}</div>}
+                {!this.openLinks ? null : this.linkCount > 0 ? <div className="propertiesView-contexts-content">{this.links}</div> : <div className="propertiesView-contexts-content">There are no current links.</div>}
+                {/* {!this.openLinks ? null : <div className="propertiesView-contexts-content">{this.links}</div>} */}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 31311af1d7a10883f2f9e0fb84cb279da6a318be Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Wed, 14 Jun 2023 12:59:02 -0400
Subject: basics double clicking, fixed customizable options for icons and text

---
 src/client/views/PropertiesButtons.tsx | 102 ++++++++++++++++++---------------
 src/client/views/PropertiesView.tsx    |   5 +-
 2 files changed, 59 insertions(+), 48 deletions(-)

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

diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 247368685..3d61ee273 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -22,8 +22,10 @@ import { pasteImageBitmap } from './nodes/WebBoxRenderer';
 import './PropertiesButtons.scss';
 import React = require('react');
 import { JsxElement } from 'typescript';
-import { FaBraille, FaThumbtack } from 'react-icons/fa';
-import { AiOutlineApple } from 'react-icons/ai';
+import { FaBraille, FaHighlighter, FaThumbtack } from 'react-icons/fa';
+import { AiOutlineApple, AiOutlinePicture } from 'react-icons/ai';
+import { MdSubtitles, MdSubtitlesOff } from 'react-icons/md';
+import {TbHighlight, TbHighlightOff} from 'react-icons/tb'
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -45,9 +47,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return !SelectionManager.SelectedSchemaDoc() && SelectionManager.Views().lastElement()?.topMost;
     }
 
-    propertyToggleBtn = (label: string, property: string, tooltip: (on?: any) => string, icon: (on: boolean) => any, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
+    propertyToggleBtn = (label: (on?: any) => string, property: string, tooltip: (on?: any) => string, icon: (on?: any) => any, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
         const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedDoc;
         const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => ((dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true);
+
+        console.log("current icon " + icon((targetDoc?.[property])))
+
         return !targetDoc ? null : (
             <Tooltip title={<div className={`dash-tooltip`}>{tooltip(targetDoc?.[property])} </div>} placement="top">
                 <div className = 'propertiesButtons-iconAndText' >
@@ -59,9 +64,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                                 SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property));
                             } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
                         }, property)}>
-                            <div className = "propertiesButtons-icon">  {icon((BoolCast(targetDoc?.[property])) as any)} </div>
+                            <div className = "propertiesButtons-icon">  {icon((targetDoc?.[property]) as any)} </div>
                       
-                          <div className="propertiesButtons-label">  {label}</div>
+                          <div className="propertiesButtons-label">  {label(targetDoc?.[property])}</div>
                         {/* <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon={icon(BoolCast(targetDoc?.[property])) as any} /> */}
                     </div>
                   
@@ -70,35 +75,54 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             </Tooltip>
         );
     };
-    @computed get lockButton() {
+    
+
+
+    @computed get titleButton() {
         return this.propertyToggleBtn(
-            'No\xA0Drag',
-            '_lockedPosition',
-            on => `${on ? 'Unlock' : 'Lock'} position to prevent dragging`,
-            on => <AiOutlineApple/>
-           // on => 'thumbtack'
+            on => !on ?  'SHOW TITLE' : (this.selectedDoc?.['_layout_showTitle']) === 'title:hover' ? 'REMOVE TITLE' : 'HOVER TITLE' ,
+            '_layout_showTitle',
+            on => 'Switch between title styles',
+            on =>   on ?  <MdSubtitlesOff/> : <MdSubtitles/>, // {currentIcon}, //(on ?  <MdSubtitles/> :)  , //,'text-width', on ?  <MdSubtitles/> : <MdSubtitlesOff/>,
+            (dv, doc) => {
+                const tdoc = dv?.rootDoc || doc;
+                const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : '';
+                tdoc._layout_showTitle = newtitle;
+            }
         );
     }
-    // @computed get maskButton() {
-    //     return this.propertyToggleBtn(
-    //         'Mask',
-    //         'stroke_isInkMask',
-    //         on => (on ? 'Make plain ink' : 'Make highlight mask'),
-    //         on => 'paint-brush',
-    //         (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
-    //     );
-    // }
-    // @computed get hideImageButton() {
+
+    // @computed get lockButton() {
     //     return this.propertyToggleBtn(
-    //         'Background',
-    //         '_hideImage',
-    //         on => (on ? 'Show Image' : 'Show Background'),
-    //         on => 'portrait'
+    //         'No\xA0Drag',
+    //         '_lockedPosition',
+    //         on => `${on ? 'Unlock' : 'Lock'} position to prevent dragging`,
+    //         on => <AiOutlineApple/>
+    //        // on => 'thumbtack'
     //     );
     // }
+    @computed get maskButton() { //highlight text while going down and reading through
+        return this.propertyToggleBtn(
+            on => on ? 'PLAIN INK':  'HIGHLIGHTER MASK',
+            'stroke_isInkMask',
+            on => (on ? 'Make plain ink' : 'Make highlight mask'),
+            on => on ? <TbHighlightOff/> : <TbHighlight/>, // <FaHighlighter/>,// 'paint-brush',
+            (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
+        );
+    }
+ 
+    @computed get hideImageButton() { // put in developer -- can trace on top of object and drawing is still there
+        return this.propertyToggleBtn(
+            on => 'Background',
+            '_hideImage',
+            on => (on ? 'Show Image' : 'Show Background'),
+            on => <AiOutlinePicture/> //'portrait'
+        );
+    }
+
     @computed get clustersButton() {
         return this.propertyToggleBtn(
-            'Clusters',
+            on =>'Clusters',
             '_freeform_useClusters',
             on => `${on ? 'Hide' : 'Show'} clusters`,
             on => <FaBraille/>
@@ -180,19 +204,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     //         (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden)
     //     );
     // }
-    // @computed get titleButton() {
-    //     return this.propertyToggleBtn(
-    //         'Title',
-    //         '_layout_showTitle',
-    //         on => 'Switch between title styles',
-    //         on => 'text-width',
-    //         (dv, doc) => {
-    //             const tdoc = dv?.rootDoc || doc;
-    //             const newtitle = !tdoc._layout_showTitle ? 'title' : tdoc._layout_showTitle === 'title' ? 'title:hover' : '';
-    //             tdoc._layout_showTitle = newtitle;
-    //         }
-    //     );
-    // }
+
     // @computed get layout_autoHeightButton() {
     //     return this.propertyToggleBtn(
     //         'Auto\xA0Size',
@@ -416,19 +428,19 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isNovice = Doc.noviceMode;
         return !this.selectedDoc ? null : (
             <div className="propertiesButtons">
-                {/* {toggle(this.titleButton)}
-                {toggle(this.captionButton)} */}
-                 {toggle(this.lockButton)}
+                 {toggle(this.titleButton)}
+                   {/* {toggle(this.captionButton)} */}
+                 {/* {toggle(this.lockButton)} */}
                 {/*{toggle(this.onClickButton)}
                 {toggle(this.layout_fitWidthButton)}
                 {toggle(this.freezeThumb)}
                 {toggle(this.forceActiveButton)}
                 {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
-                {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
+                {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}*/}
                 {toggle(this.maskButton, { display: !isInk ? 'none' : '' })}
-                {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })}
-                {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })}
+               {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })}
+                {/* {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })}
                 {toggle(this.gridButton, { display: !isCollection ? 'none' : '' })}
                 {toggle(this.groupButton, { display: isTabView || !isCollection ? 'none' : '' })}
                 {toggle(this.snapButton, { display: !isCollection ? 'none' : '' })} */}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index d956c874c..8d496e992 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1201,13 +1201,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get linksSubMenu() {
-    //     this.linkCount
-    //    this.links
+    //     onPointerDown={action(() => (this.openLinks = !this.openLinks))}
         
         return (
             <div className="propertiesView-contexts">
             
-                <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
+                <div className="propertiesView-contexts-title" onDoubleClick={action(() => (this.openContexts = !this.openContexts, this.openOptions = !this.openOptions, this.openTransform = !this.openTransform))} onClick={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
                     Linked To
                     <div className="propertiesView-contexts-title-icon">
                         <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-- 
cgit v1.2.3-70-g09d2


From 20d217d825891cf29a432a048d1f8e7bc04d062a Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 14 Jun 2023 15:02:39 -0400
Subject: PropertiesView changes - colors on all permissions and fixed bug with
 when the drop down arrow is displayed

---
 src/client/util/SharingManager.tsx        |  1 -
 src/client/views/DocumentDecorations.scss |  6 +++---
 src/client/views/DocumentDecorations.tsx  |  9 +++++---
 src/client/views/PropertiesView.scss      | 35 +++++++++++++++++++------------
 src/client/views/PropertiesView.tsx       | 30 ++++++++++++++------------
 src/fields/util.ts                        |  1 +
 6 files changed, 49 insertions(+), 33 deletions(-)

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d7cc352b7..d9ab0cb5f 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -179,7 +179,6 @@ export class SharingManager extends React.Component<{}> {
                 } else {
                     if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
                 }
-
                 distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
                 this.setDashboardBackground(doc, permission as SharingPermissions);
                 if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 32bcc872a..a5ebe9d58 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -230,14 +230,14 @@ $resizeHandler: 8px;
         &:hover {
             opacity: 1;
         }
-        .documentDecorations-shareNone,
-        .documentDecorations-shareAdmin{
+        .documentDecorations-shareNone{
             width: calc(100% + 10px);
             background: grey;
             color: rgb(71, 71, 71);
             border-color: rgb(71, 71, 71);
         }
-        .documentDecorations-shareEdit{
+        .documentDecorations-shareEdit,
+        .documentDecorations-shareAdmin{
             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 9aca8c339..a6b2db8f3 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -11,7 +11,7 @@ import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl } from '../../fields/util';
+import { GetEffectiveAcl, normalizeEmail } from '../../fields/util';
 import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -650,7 +650,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     } else {
                         const rotCtr = [docwidth / 2, docheight / 2];
                         const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
-
                         const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
                         dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
@@ -756,9 +755,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // sharing
         const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
+        // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-'+normalizeEmail(Doc.CurrentUserEmail)];
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch (shareMode) {
+            case 'Admin':
+                shareSymbolIcon = '⬢ ';
+                break;
             case 'Edit':
                 shareSymbolIcon = '⬢ ';
                 break;
@@ -799,7 +802,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 // const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[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 && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && GetEffectiveAcl(docView.rootDoc) !== AclAdmin && effectiveAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclEdit);
             });
         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/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 71107a5f9..3cf7f2546 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -164,23 +164,29 @@
         margin-right: 10px;
         width: 92%;
 
-        & .propertiesView-shareDropDownNone,
-        .propertiesView-shareDropDownAdmin{
-            padding: 5px;
+        & .propertiesView-shareDropDownNone{
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: grey;
             color: rgb(71, 71, 71);
             border-radius: 6px;
             border: 1px solid rgb(71, 71, 71);
         }
-        & .propertiesView-shareDropDownEdit{
-            padding: 5px;
+        & .propertiesView-shareDropDownEdit,
+        .propertiesView-shareDropDownAdmin{
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: rgb(235, 235, 145);
             color: rgb(75, 75, 5);
             border-radius: 6px;
             border: 1px solid rgb(75, 75, 5);
         }
         & .propertiesView-shareDropDownAugment{
-            padding: 5px;
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: rgb(160, 230, 160);
             color:rgb(19, 80, 19);
             border-radius: 6px;
@@ -188,14 +194,18 @@
 
         }
         & .propertiesView-shareDropDownView{
-            padding: 5px;
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: rgb(161, 161, 238);
             color: rgb(25, 25, 101);
             border-radius: 6px;
             border: 1px solid rgb(25, 25, 101);
         }
         & .propertiesView-shareDropDownNot-Shared{
-            padding: 5px;
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: rgb(210, 143, 143);
             color: rgb(138, 47, 47);
             border-radius: 6px;
@@ -360,13 +370,13 @@
     }
 
     .expansion-button {
-        margin-left: -20;
+        margin-left: 0;
 
         .expansion-button-icon {
             width: 11px;
             height: 11px;
             color: black;
-            margin-left: 27px;
+            margin-left: -15px;
 
             &:hover {
                 color: rgb(131, 131, 131);
@@ -410,6 +420,8 @@
 
             .propertiesView-sharingTable-item-permission {
                 display: flex;
+                position: fixed;
+                right: 30px;
                 align-items: flex-end;
                 margin-left: auto;
             }
@@ -427,9 +439,6 @@
         background: inherit;
         text-align: justify; // for Edge
         text-align-last: end;
-        &:hover {
-            cursor: pointer;
-        }
     }
 
     .propertiesView-fields {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index a5ac58f75..fb21c2db2 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -319,8 +319,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     .filter(permission => !Doc.noviceMode || ![SharingPermissions.View].includes(permission as any))
                     .map(permission => (
                         <option className="propertiesView-permisssions-select" key={permission} value={permission}>
-                            {' '}
-                            {permission}{' '}
+                            {permission}
                         </option>
                     ))}
             </select>
@@ -379,16 +378,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 </div>
                 {/* {name !== "Me" ? this.notifyIcon : null} */}
                 <div className="propertiesView-sharingTable-item-permission">
-                    {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
+                    {this.colorACLDropDown(name, admin, permission, showExpansionIcon)}
                     {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null}
                 </div>
             </div>
         );
     }
 
-    publicACLDropDown(admin: boolean, permission: string, showExpansionIcon?: boolean) {
+    colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
         var dropDownText = '';
         switch (StrCast(permission)) {
+            case 'Admin':
+                dropDownText = '⬢ ';
+                break;
             case 'Edit':
                 dropDownText = '⬢ ';
                 break;
@@ -402,15 +404,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 dropDownText = '▲ ';
                 break;
         }
-
         return (
             <div>
                 <div className={'propertiesView-shareDropDown'}>
                     <div className={`propertiesView-shareDropDown${permission}`}>
                         <div className="propertiesView-shareDropDown">
                             {' '}
-                            {dropDownText} {admin && permission !== 'Owner' ? this.getPermissionsSelect('Public', permission) : permission}
-                            {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null}
+                            {dropDownText}
+                            {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
+                            {/* {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null} */}
                         </div>
                     </div>
                 </div>
@@ -437,9 +439,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         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];
 
-        const effectiveAcls = GetEffectiveAcl(target);
-        const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
-
+        // const effectiveAcls = GetEffectiveAcl(target);
+        // const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
+        const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)]
+        const showAdmin = curUserAcl == 'Admin' || curUserAcl == '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 ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
@@ -480,11 +484,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return (
             <div>
                 {' '}
-                Sharing Mode
-                <div>{this.publicACLDropDown(showAdmin, StrCast(target['acl-Public']), false)}</div>
+                Sharing Mode for Public / Guest Users
+                <div>{this.colorACLDropDown('Public', showAdmin, StrCast(target['acl-Public']), false)}</div>
                 <div>
                     {' '}
-                    <br></br> Who has access to the Dashboard?{' '}
+                    <br></br> Sharing Modes for Individual Users and Groups with Access to the Dashboard{' '}
                 </div>
                 <div className="propertiesView-sharingTable">{<div> {tableEntries}</div>}</div>
             </div>
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 3860decc9..5e925cd7e 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -37,6 +37,7 @@ import { RefField } from './RefField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
+import { convertCompilerOptionsFromJson } from 'typescript';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
-- 
cgit v1.2.3-70-g09d2


From 96abb67130a29c472209eca74d90844090034640 Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Wed, 14 Jun 2023 15:42:51 -0400
Subject: trying to fix double click

---
 src/client/views/PropertiesView.tsx | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8d496e992..9c327b3f1 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -43,6 +43,7 @@ import { FaFileVideo } from 'react-icons/fa'; //* as Icons from "react-icons/fa"
 import { IconButton } from 'browndash-components';
 import { IconBase } from 'react-icons';
 import { MdOutlineMedicalServices } from 'react-icons/md';
+import { createPromiseCapability } from 'pdfjs-dist';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -1200,13 +1201,37 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    @computed get doubleClickClose() {
+        //action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = !this.openTransform))
+        //console.log(this.openContexts + " hello " + this.openOptions + this.openTransform + this.openLinks + this.openFields)
+        if (this.openContexts || this.openOptions || this.openTransform || this.openLinks || this.openFields){
+            this.openContexts = false;
+            this.openOptions = false;
+            this.openTransform = false;
+            this.openLinks = false;
+            this.openFields = false;
+            
+        } else if (!this.openContexts && !this.openOptions && !this.openTransform && !this.openLinks && !this.openFields){
+            this.openContexts = true;
+            this.openOptions = true;
+            this.openTransform = true;
+            this.openLinks = true;
+            this.openFields = true;
+
+        }
+        
+        return (
+            console.log("made")
+        )
+    }
+
     @computed get linksSubMenu() {
     //     onPointerDown={action(() => (this.openLinks = !this.openLinks))}
         
         return (
             <div className="propertiesView-contexts">
             
-                <div className="propertiesView-contexts-title" onDoubleClick={action(() => (this.openContexts = !this.openContexts, this.openOptions = !this.openOptions, this.openTransform = !this.openTransform))} onClick={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
+                <div className="propertiesView-contexts-title" onDoubleClick={action(() => ( this.doubleClickClose, this.openLinks = true))} onClick={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
                     Linked To
                     <div className="propertiesView-contexts-title-icon">
                         <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-- 
cgit v1.2.3-70-g09d2


From 540b8310170f599c93811291ce6d3992c323cd42 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 10:14:16 -0400
Subject: properties view

---
 package-lock.json                        |  38 +++++++++++------------
 src/.DS_Store                            | Bin 10244 -> 10244 bytes
 src/client/util/SharingManager.scss      |   3 +-
 src/client/util/SharingManager.tsx       |  31 +++++++++++--------
 src/client/views/DocumentDecorations.tsx |   4 +--
 src/client/views/PropertiesView.tsx      |  50 +++++++++++++++++++++++++++++--
 6 files changed, 90 insertions(+), 36 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 0347984a4..c55ba91eb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3170,7 +3170,7 @@
                 "textarea-caret": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.0.2.tgz",
-                    "integrity": "sha1-82DEhpmqGr9xhoCkOjGoUGZcLK8="
+                    "integrity": "sha512-gRzeti2YS4did7UJnPQ47wrjD+vp+CJIe9zbsu0bJ987d8QVLvLNG9757rqiQTIy4hGIeFauTTJt5Xkn51UkXg=="
                 }
             }
         },
@@ -3269,7 +3269,7 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
         },
         "agent-base": {
             "version": "6.0.2",
@@ -4166,7 +4166,7 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
         },
         "bail": {
             "version": "2.0.2",
@@ -4236,7 +4236,7 @@
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
+            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
         },
         "base64-js": {
             "version": "1.5.1",
@@ -5217,7 +5217,7 @@
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -5227,7 +5227,7 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
         },
         "compress-commons": {
             "version": "2.1.1",
@@ -5947,7 +5947,7 @@
         "custom-event": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-            "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
+            "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
         },
         "cyclist": {
             "version": "1.0.1",
@@ -7161,7 +7161,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -10351,14 +10351,14 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
         },
         "has-flag": {
             "version": "3.0.0",
@@ -10934,7 +10934,7 @@
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
         },
         "inflight": {
             "version": "1.0.6",
@@ -12468,7 +12468,7 @@
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
@@ -12729,7 +12729,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
+            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -12737,7 +12737,7 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
+                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
                 }
             }
         },
@@ -21782,7 +21782,7 @@
         "sparse-bitfield": {
             "version": "3.0.3",
             "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
-            "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+            "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
             "optional": true,
             "requires": {
                 "memory-pager": "^1.0.2"
@@ -22543,7 +22543,7 @@
         "to-array": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
-            "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+            "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
         },
         "to-fast-properties": {
             "version": "2.0.0",
@@ -24172,7 +24172,7 @@
                 "resolve-cwd": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-                    "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
+                    "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
                     "dev": true,
                     "requires": {
                         "resolve-from": "^3.0.0"
@@ -24181,7 +24181,7 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
                     "dev": true
                 },
                 "semver": {
@@ -24733,7 +24733,7 @@
         "yeast": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
-            "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+            "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
         },
         "yn": {
             "version": "3.1.1",
diff --git a/src/.DS_Store b/src/.DS_Store
index be99aa5af..06389d6ae 100644
Binary files a/src/.DS_Store and b/src/.DS_Store differ
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index aa1e11ef5..80d5807b4 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -107,6 +107,7 @@
             .group-container {
                 width: 50%;
                 display: flex;
+                top:0;
                 flex-direction: column;
 
                 .user-sort {
@@ -199,7 +200,7 @@
         display: flex;
         position: relative;
         margin-top: 5px;
-        margin-bottom: 10px;
+        // margin-bottom: 10px;
         font-size: 22px;
         -webkit-user-select: none;
         -moz-user-select: none;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6112b5b69..78e502e6e 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -526,7 +526,7 @@ export class SharingManager extends React.Component<{}> {
                 docToUse = Doc.GetProto(this.targetDoc!);
 
                 const userKey = `acl-${normalizeEmail(user.email)}`;
-                const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).every(doc => doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey]);
+                const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
 
                 return !permissions ? null : (
@@ -574,8 +574,8 @@ export class SharingManager extends React.Component<{}> {
         const groupListContents = groupListMap.map(group => {
             const groupKey = `acl-${StrCast(group.title)}`;
             const uniform = docs
-                .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
-                .every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
+                .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
+                .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey]));
             const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
 
             return !permissions ? null : (
@@ -670,14 +670,21 @@ export class SharingManager extends React.Component<{}> {
                         </div>
                         <div className={'group-container'}>
                             <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
-                                Groups{' '}
-                                {this.groupSort === 'ascending' ? (
-                                    <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
-                                ) : this.groupSort === 'descending' ? (
-                                    <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
-                                ) : (
-                                    <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
-                                )}
+                                <div className="container">
+                                    Groups
+                                    &nbsp;
+                                    <div className="group-info" onClick={action(() => (GroupManager.Instance?.open()))}>
+                                        <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
+                                    </div>
+                                    &nbsp;
+                                    {this.groupSort === 'ascending' ? (
+                                        <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
+                                    ) : this.groupSort === 'descending' ? (
+                                        <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
+                                    ) : (
+                                        <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
+                                    )}
+                                </div>
                             </div>
                             <div className={'groups-list'}>{groupListContents}</div>
                         </div>
@@ -690,4 +697,4 @@ export class SharingManager extends React.Component<{}> {
     render() {
         return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
     }
-}
+}
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 74cf58c7b..0cd92e2cf 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -760,8 +760,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
 
         // sharing
-        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
-        // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-'+normalizeEmail(Doc.CurrentUserEmail)];
+        // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
+        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-'+normalizeEmail(Doc.CurrentUserEmail)];
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch (shareMode) {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index b6699c3bf..ca27e244d 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -6,7 +6,11 @@ import { Checkbox, Tooltip } from '@material-ui/core';
 import { action, computed, Lambda, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
+<<<<<<< Updated upstream
 import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+=======
+import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+>>>>>>> Stashed changes
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -33,6 +37,8 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+import { GroupManager } from '../util/GroupManager';
+import { intersection } from 'lodash';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -384,6 +390,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    /**
+     * @returns a colored dropdown bar reflective of the permission
+     */
     colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
         var dropDownText = '';
         switch (StrCast(permission)) {
@@ -411,7 +420,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             {' '}
                             {dropDownText}
                             {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
-                            {/* {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null} */}
                         </div>
                     </div>
                 </div>
@@ -426,6 +434,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return u1 > u2 ? -1 : u1 === u2 ? 0 : 1;
     };
 
+    /**
+     * Sorting algorithm to sort groups.
+     */
+    sortGroups = (g1: Doc, g2: Doc) => {
+        return g1 > g2 ? -1 : g1 === g2 ? 0 : 1;
+    };
+
     /**
      * @returns the sharing and permissions panel.
      */
@@ -451,6 +466,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
+<<<<<<< Updated upstream
         // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
         if (commonKeys.length) {
             for (const key of commonKeys) {
@@ -458,6 +474,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
                 if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
                     tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
+=======
+        // adds each user to usersAdded
+        SharingManager.Instance.users.forEach(eachUser => {
+            var userOnDashboard = true;
+            var permission = StrCast(target[`acl-${normalizeEmail(eachUser.user.email)}`]);
+            if (Doc.ActiveDashboard) {
+                if (Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == '' || Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == undefined) {
+                    userOnDashboard = false;
+>>>>>>> Stashed changes
                 }
             }
             if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email != 'Public' && eachUser.user.email != target.author) {
@@ -466,6 +491,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             }
         });
 
+<<<<<<< Updated upstream
         const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
         // shifts the current user, owner, public to the top of the doc.
         // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
@@ -479,8 +505,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 !ownerSame
             )
         );
+=======
+        // adds each user to the table
+        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
+        });
+>>>>>>> Stashed changes
 
-        // add current user
+        // adds current user
         var userEmail = Doc.CurrentUserEmail;
         if (userEmail == 'guest') userEmail = 'Public';
         if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
@@ -488,6 +522,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             usersAdded.push(userEmail);
         }
 
+        // adds groups
+        const groupList = GroupManager.Instance?.allGroups || [];
+        const groups = groupList.slice().sort(this.sortGroups);
+        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
+        const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
+        groupListMap.map(group => {
+            if (group.title != 'Public'){
+                const permission = StrCast(target[`acl-${StrCast(group.title)}`]);
+                tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+            }
+        })
+
         // shift owner to top
         tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
 
-- 
cgit v1.2.3-70-g09d2


From 35793fc1da7633da7c474317b0df83b4368a9b10 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 10:55:20 -0400
Subject: merge fixes

---
 src/client/documents/tempCodeRunnerFile.ts |  1 +
 src/client/views/PropertiesView.tsx        | 42 ++++--------------------------
 src/fields/Doc.ts                          | 22 ----------------
 3 files changed, 6 insertions(+), 59 deletions(-)
 create mode 100644 src/client/documents/tempCodeRunnerFile.ts

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

diff --git a/src/client/documents/tempCodeRunnerFile.ts b/src/client/documents/tempCodeRunnerFile.ts
new file mode 100644
index 000000000..76ea17be0
--- /dev/null
+++ b/src/client/documents/tempCodeRunnerFile.ts
@@ -0,0 +1 @@
+Initializing
\ No newline at end of file
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index ca27e244d..25477f948 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -6,11 +6,8 @@ import { Checkbox, Tooltip } from '@material-ui/core';
 import { action, computed, Lambda, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-<<<<<<< Updated upstream
-import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
-=======
-import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
->>>>>>> Stashed changes
+import { Doc, Field, FieldResult, HierarchyMapping, NumListCast, Opt, StrListCast } from '../../fields/Doc';
+import { AclAdmin, DocAcl, DocData,  Height,  Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -451,7 +448,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         }
         // all selected docs
         const docs =
-            SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]));
+            SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
 
         const target = docs[0];
 
@@ -459,22 +456,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
         const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)]
         const showAdmin = curUserAcl == 'Admin' || curUserAcl == '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 tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
-<<<<<<< Updated upstream
-        // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
-        if (commonKeys.length) {
-            for (const key of commonKeys) {
-                const name = denormalizeEmail(key.substring(4));
-                const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
-                if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
-                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
-=======
         // adds each user to usersAdded
         SharingManager.Instance.users.forEach(eachUser => {
             var userOnDashboard = true;
@@ -482,7 +467,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             if (Doc.ActiveDashboard) {
                 if (Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == '' || Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == undefined) {
                     userOnDashboard = false;
->>>>>>> Stashed changes
                 }
             }
             if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email != 'Public' && eachUser.user.email != target.author) {
@@ -491,28 +475,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             }
         });
 
-<<<<<<< Updated upstream
-        const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
-        // shifts the current user, owner, public to the top of the doc.
-        // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
-        if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
-        tableEntries.unshift(this.sharingItem('Public', showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-'));
-        tableEntries.unshift(
-            this.sharingItem(
-                'Me',
-                showAdmin,
-                docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-',
-                !ownerSame
-            )
-        );
-=======
         // adds each user to the table
         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
         });
->>>>>>> Stashed changes
 
         // adds current user
         var userEmail = Doc.CurrentUserEmail;
@@ -525,7 +493,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // adds groups
         const groupList = GroupManager.Instance?.allGroups || [];
         const groups = groupList.slice().sort(this.sortGroups);
-        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
+        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.map(group => {
             if (group.title != 'Public'){
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 824aa014b..88587b3c1 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -121,28 +121,6 @@ export function DocListCast(field: FieldResult, defaultVal: Doc[] = []) {
     return Cast(field, listSpec(Doc), defaultVal).filter(d => d instanceof Doc) as Doc[];
 }
 
-export const WidthSym = Symbol('Width');
-export const HeightSym = Symbol('Height');
-export const AnimationSym = Symbol('Animation');
-export const HighlightSym = Symbol('Highlight');
-export const DataSym = Symbol('Data');
-export const LayoutSym = Symbol('Layout');
-export const FieldsSym = Symbol('Fields');
-export const CssSym = Symbol('Css');
-export const AclSym = Symbol('Acl');
-export const DirectLinksSym = Symbol('DirectLinks');
-export const AclUnset = Symbol('AclUnset');
-export const AclPrivate = Symbol('AclOwnerOnly');
-export const AclReadonly = Symbol('AclReadOnly');
-export const AclAugment = Symbol('AclAugment');
-export const AclSelfEdit = Symbol('AclSelfEdit');
-export const AclEdit = Symbol('AclEdit');
-export const AclAdmin = Symbol('AclAdmin');
-export const UpdatingFromServer = Symbol('UpdatingFromServer');
-export const Initializing = Symbol('Initializing');
-export const ForceServerWrite = Symbol('ForceServerWrite');
-export const CachedUpdates = Symbol('Cached updates');
-
 export enum aclLevel {
     unset = -1,
     unshared = 0,
-- 
cgit v1.2.3-70-g09d2


From 3225f565acd8edaac5992f9766dc66ac5547257f Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 11:18:19 -0400
Subject: more merge fixes

---
 src/client/views/DocumentDecorations.tsx           | 27 ++++++-------
 src/client/views/MarqueeAnnotator.tsx              |  5 ++-
 src/client/views/PropertiesView.tsx                | 24 +++++------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 25 ++++++------
 .../formattedText/ProsemirrorExampleTransfer.ts    |  2 +-
 src/fields/Doc.ts                                  |  2 +-
 src/fields/util.ts                                 | 47 ++++++++++++----------
 7 files changed, 69 insertions(+), 63 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0cd92e2cf..9bc4cb48f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,38 +5,37 @@ import { IconButton } from 'browndash-components';
 import { action, computed, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
+import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
-import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
-import { Document } from '../../fields/documentSchemas';
+import { Doc, DocListCast, Field } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl } from '../../fields/util';
-import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
-import { Docs } from '../documents/Documents';
+import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
+import { GetEffectiveAcl, normalizeEmail } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
+import { Docs } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
 import { DragManager } from '../util/DragManager';
 import { LinkFollower } from '../util/LinkFollower';
 import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
-import { Colors } from './global/globalEnums';
-import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
+import { InkingStroke } from './InkingStroke';
 import { LightboxView } from './LightboxView';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
+import { Colors } from './global/globalEnums';
 import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
-import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import React = require('react');
 import _ = require('lodash');
-import { DocumentManager } from '../util/DocumentManager';
-import { isUndefined } from 'lodash';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -805,7 +804,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.props.hideDeleteButton ||
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
-                const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit;
+                const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit;
                 return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 11336d079..7edc1494b 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,6 +1,7 @@
 import { action, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, Opt } from '../../fields/Doc';
+import { Doc, Opt } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData,  } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast } from '../../fields/Types';
@@ -207,7 +208,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     @action
     highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean, summarize?: boolean) => {
         // creates annotation documents for current highlights
-        const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
+        const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DocData]);
         const annotationDoc = [AclAugment, AclSelfEdit, 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.tsx b/src/client/views/PropertiesView.tsx
index 25477f948..abbf9a22e 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -3,39 +3,39 @@ import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox, Tooltip } from '@material-ui/core';
-import { action, computed, Lambda, observable } from 'mobx';
+import { intersection } from 'lodash';
+import { Lambda, action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { Doc, Field, FieldResult, HierarchyMapping, NumListCast, Opt, StrListCast } from '../../fields/Doc';
-import { AclAdmin, DocAcl, DocData,  Height,  Width } from '../../fields/DocSymbols';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { Doc, Field, FieldResult, NumListCast, Opt, StrListCast } from '../../fields/Doc';
+import { DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { ComputedField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { SharingPermissions, normalizeEmail } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
+import { GroupManager } from '../util/GroupManager';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SharingManager } from '../util/SharingManager';
 import { Transform } from '../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
+import { UndoManager, undoBatch, undoable } from '../util/UndoManager';
 import { EditableView } from './EditableView';
 import { FilterPanel } from './FilterPanel';
-import { Colors } from './global/globalEnums';
 import { InkStrokeProperties } from './InkStrokeProperties';
-import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
-import { KeyValueBox } from './nodes/KeyValueBox';
-import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 import { PropertiesButtons } from './PropertiesButtons';
 import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector';
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
-import { GroupManager } from '../util/GroupManager';
-import { intersection } from 'lodash';
+import { Colors } from './global/globalEnums';
+import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
+import { KeyValueBox } from './nodes/KeyValueBox';
+import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f4cecb1dc..2f31c9c57 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,7 +12,8 @@ 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, CssSym, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, StrListCast, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast,StrListCast,  Field, Opt } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, Height, Width,ForceServerWrite,  UpdatingFromServer} from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -301,16 +302,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             this._editorView.updateState(state);
 
             const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
-            const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
-            const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField) : undefined; // the actual text in the text box
-            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 newText = state.doc.textBetween(0, state.doc.content.size, ' \n');
+            const newJson = JSON.stringify(state.toJSON());
+            const prevData = Cast(this.layoutDoc[this.fieldKey], RichTextField, null); // the actual text in the text box
+            const prevLayoutData = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
+            const protoData = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
             const effectiveAcl = GetEffectiveAcl(dataDoc);
 
             const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
 
-            if ([AclEdit, AclAdmin, AclAugment].includes(effectiveAcl)) {
+            if ([AclEdit, AclAdmin, AclSelfEdit, 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('#')) {
@@ -1806,12 +1807,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             default:
                 if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
             case ' ':
-                if (e.code == 'Space') {
-                    break;
+                if (e.code !== 'Space') {
+                    [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) })));
                 }
-                [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) })));
-        }
+                break;
+            }
         this.startUndoTypingBatch();
     };
     ondrop = (e: React.DragEvent) => {
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index c4a9b4a7e..ecd0bf8aa 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 { AclAdmin, AclAugment, AclEdit} from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit} from '../../../../fields/DocSymbols';
 import { GetEffectiveAcl } from '../../../../fields/util';
 import { Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 88587b3c1..555e768c1 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -145,7 +145,7 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }>
 export function updateCachedAcls(doc: Doc) {
     if (!doc) return;
 
-    const target = (doc as any)?.__fields ?? doc;
+    const target = (doc as any)?.__fieldTuples ?? doc;
     const permissions: { [key: string]: symbol } = !target.author || target.author === Doc.CurrentUserEmail ? { 'acl-Me': AclAdmin } : {};
     Object.keys(target).filter(key => key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl));
     if (Object.keys(permissions).length || doc[DocAcl]?.length) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 6058a5a5f..e0f87f805 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -8,27 +8,32 @@ import { UndoManager } from '../client/util/UndoManager';
 import { returnZero } from '../Utils';
 import CursorField from './CursorField';
 import {
-    AclAdmin,
-    AclEdit,
-    aclLevel,
-    AclPrivate,
-    AclSelfEdit,
-    AclSym,
-    DataSym,
     Doc,
+    aclLevel,
     DocListCast,
     DocListCastAsync,
-    ForceServerWrite,
-    HeightSym,
     HierarchyMapping,
-    Initializing,
-    LayoutSym,
     ReverseHierarchyMap,
     updateCachedAcls,
-    UpdatingFromServer,
-    WidthSym,
 } from './Doc';
-import { Id, OnUpdate, Parent, SelfProxy, ToValue, Update } from './FieldSymbols';
+import {
+    AclAdmin,
+    AclEdit,
+    AclPrivate,
+    AclAugment,
+    FieldKeys,
+    DocAcl,
+    DocData,
+    ForceServerWrite,
+    Height,
+    Initializing,
+    DocLayout,
+    UpdatingFromServer,
+    Width,
+    SelfProxy,
+    Update
+} from './DocSymbols';
+import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols';
 import { List } from './List';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
@@ -150,12 +155,12 @@ export function denormalizeEmail(email: string) {
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
     return;
-    const dataDoc = parent[DataSym];
-    for (const key of Object.keys(dataDoc)) {
-        // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
-        key.startsWith('acl') && distributeAcls(key, permission, child);
-    }
+    // const dataDoc = parent[DocData];
+    // for (const key of Object.keys(dataDoc)) {
+    //     // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
+    //     const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
+    //     key.startsWith('acl') && distributeAcls(key, permission, child);
+    // }
 }
 
 /**
@@ -214,7 +219,7 @@ export function SetCachedGroups(groups: string[]) {
     runInAction(() => cachedGroups.push(...groups));
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
-    const targetAcls = target[AclSym];
+    const targetAcls = target[DocAcl];
     if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
 
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
-- 
cgit v1.2.3-70-g09d2


From d92775bffab6470dc6142e02092b7cae2c30b5ff Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 15 Jun 2023 12:00:12 -0400
Subject: fixes post-merge

---
 src/client/documents/tempCodeRunnerFile.ts |  1 -
 src/client/util/SharingManager.tsx         | 13 ++++++------
 src/client/views/DocComponent.tsx          |  4 ++--
 src/client/views/DocumentDecorations.tsx   | 34 +++++++++++++++++++-----------
 src/client/views/MarqueeAnnotator.tsx      |  2 +-
 src/client/views/PropertiesView.tsx        | 20 +++++++++---------
 src/fields/Doc.ts                          |  4 +---
 src/fields/util.ts                         | 31 +++------------------------
 8 files changed, 45 insertions(+), 64 deletions(-)
 delete mode 100644 src/client/documents/tempCodeRunnerFile.ts

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

diff --git a/src/client/documents/tempCodeRunnerFile.ts b/src/client/documents/tempCodeRunnerFile.ts
deleted file mode 100644
index 76ea17be0..000000000
--- a/src/client/documents/tempCodeRunnerFile.ts
+++ /dev/null
@@ -1 +0,0 @@
-Initializing
\ No newline at end of file
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 78e502e6e..bea870f21 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,4 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { IconButton, Size } from 'browndash-components';
 import { intersection } from 'lodash';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -6,7 +7,7 @@ import * as React from 'react';
 import Select from 'react-select';
 import * as RequestPromise from 'request-promise';
 import { Doc, DocListCast, DocListCastAsync, HierarchyMapping } from '../../fields/Doc';
-import { AclAdmin, AclPrivate, DocAcl, AclUnset, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast, StrCast } from '../../fields/Types';
@@ -23,7 +24,6 @@ import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
-import { IconButton, Size } from 'browndash-components';
 
 export interface User {
     email: string;
@@ -652,7 +652,7 @@ export class SharingManager extends React.Component<{}> {
                             </div>
                         </div>
                     ) : (
-                        <br></br>
+                        <br />
                     )}
                     <div className="main-container">
                         <div className={'individual-container'}>
@@ -671,9 +671,8 @@ export class SharingManager extends React.Component<{}> {
                         <div className={'group-container'}>
                             <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
                                 <div className="container">
-                                    Groups
-                                    &nbsp;
-                                    <div className="group-info" onClick={action(() => (GroupManager.Instance?.open()))}>
+                                    Groups &nbsp;
+                                    <div className="group-info" onClick={action(() => GroupManager.Instance?.open())}>
                                         <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
                                     </div>
                                     &nbsp;
@@ -697,4 +696,4 @@ export class SharingManager extends React.Component<{}> {
     render() {
         return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index dc9c8a0ce..db24229dc 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -214,7 +214,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             // only make a pushpin if we have acl's to edit the document
                             //DocUtils.LeavePushpin(doc);
                             doc._stayInCollection = undefined;
-                            doc.context = this.props.Document;
+                            doc.embedContainer = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
 
                             Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
@@ -222,7 +222,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added);
                     else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
-                    targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now()));
+                    targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField(new Date(Date.now()));
                 }
             }
             return true;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9bc4cb48f..744d04370 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -13,7 +13,7 @@ import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, normalizeEmail } from '../../fields/util';
+import { GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { Docs } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
@@ -655,7 +655,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         const rotCtr = [docwidth / 2, docheight / 2];
                         const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
 
-                        const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
+                        const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '_scrollHeight']))) * docView.NativeDimScaling();
                         dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
                         dH && (doc._layout_autoHeight = false);
@@ -760,23 +760,23 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // sharing
         // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
-        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-'+normalizeEmail(Doc.CurrentUserEmail)];
+        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch (shareMode) {
-            case 'Admin':
+            case SharingPermissions.Admin:
                 shareSymbolIcon = '⬢ ';
                 break;
-            case 'Edit':
+            case SharingPermissions.Edit:
                 shareSymbolIcon = '⬢ ';
                 break;
-            case 'Augment':
+            case SharingPermissions.Augment:
                 shareSymbolIcon = '⬟ ';
                 break;
-            case 'View':
+            case SharingPermissions.View:
                 shareSymbolIcon = '♦ ';
                 break;
-            case 'Not-Shared':
+            case SharingPermissions.None:
                 shareSymbolIcon = '▲ ';
                 break;
             default:
@@ -805,7 +805,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
                 const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit;
-                return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return (docView.rootDoc.stayInCollection && !docView.rootDoc._isTimelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && 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">
@@ -833,7 +833,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // Radius constants
         const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
-        const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding));
+        const borderRadius = numberValue(StrCast(seldocview.rootDoc.layout_borderRounding));
         const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
         const radiusHandle = (borderRadius / docMax) * maxDist;
@@ -858,7 +858,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             </div>
         );
         const sharingMenu = docShareMode ? (
-            <div className='documentDecorations-share' onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => SettingsManager.propertiesWidth =250))}>
+            <div
+                className="documentDecorations-share"
+                onPointerDown={e =>
+                    setupMoveUpEvents(
+                        this,
+                        e,
+                        returnFalse,
+                        returnFalse,
+                        action(() => (SettingsManager.propertiesWidth = 250))
+                    )
+                }>
                 <div className={`documentDecorations-share${shareMode}`}>
                     <span>{shareSymbolIcon + ' ' + shareMode}</span>
                 </div>
@@ -899,7 +909,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
-                                {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
+                                {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as new embedding, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
                                 <>
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 7edc1494b..a4a2c1df9 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,7 +1,7 @@
 import { action, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData,  } from '../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast } from '../../fields/Types';
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index abbf9a22e..43ac2aa88 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -393,19 +393,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
         var dropDownText = '';
         switch (StrCast(permission)) {
-            case 'Admin':
+            case SharingPermissions.Admin:
                 dropDownText = '⬢ ';
                 break;
-            case 'Edit':
+            case SharingPermissions.Edit:
                 dropDownText = '⬢ ';
                 break;
-            case 'Augment':
+            case SharingPermissions.Augment:
                 dropDownText = '⬟ ';
                 break;
-            case 'View':
+            case SharingPermissions.View:
                 dropDownText = '♦ ';
                 break;
-            case 'Not-Shared':
+            case SharingPermissions.None:
                 dropDownText = '▲ ';
                 break;
         }
@@ -454,9 +454,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // const effectiveAcls = GetEffectiveAcl(target);
         // const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
-        const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)]
-        const showAdmin = curUserAcl == 'Admin' || curUserAcl == 'Owner'
-       
+        const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
+        const showAdmin = curUserAcl == 'Admin' || curUserAcl == 'Owner';
+
         const tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
@@ -496,11 +496,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.map(group => {
-            if (group.title != 'Public'){
+            if (group.title != 'Public') {
                 const permission = StrCast(target[`acl-${StrCast(group.title)}`]);
                 tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
             }
-        })
+        });
 
         // shift owner to top
         tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 555e768c1..3116fd70b 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1267,9 +1267,7 @@ export namespace Doc {
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
-        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) {
-            return DocBrushStatus.unbrushed;
-        }
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
         const status = brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
         if (status === DocBrushStatus.unbrushed) {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
diff --git a/src/fields/util.ts b/src/fields/util.ts
index e0f87f805..9c0f4ba1f 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -7,41 +7,16 @@ import { SerializationHelper } from '../client/util/SerializationHelper';
 import { UndoManager } from '../client/util/UndoManager';
 import { returnZero } from '../Utils';
 import CursorField from './CursorField';
-import {
-    Doc,
-    aclLevel,
-    DocListCast,
-    DocListCastAsync,
-    HierarchyMapping,
-    ReverseHierarchyMap,
-    updateCachedAcls,
-} from './Doc';
-import {
-    AclAdmin,
-    AclEdit,
-    AclPrivate,
-    AclAugment,
-    FieldKeys,
-    DocAcl,
-    DocData,
-    ForceServerWrite,
-    Height,
-    Initializing,
-    DocLayout,
-    UpdatingFromServer,
-    Width,
-    SelfProxy,
-    Update
-} from './DocSymbols';
+import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, updateCachedAcls } from './Doc';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, Update, UpdatingFromServer, Width } from './DocSymbols';
 import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols';
 import { List } from './List';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
 import { RefField } from './RefField';
 import { SchemaHeaderField } from './SchemaHeaderField';
-import { ComputedField, ScriptField } from './ScriptField';
+import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
-import { convertCompilerOptionsFromJson } from 'typescript';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
-- 
cgit v1.2.3-70-g09d2


From a366394a1db42628b7299947e6c1be2845c9a77d Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 13:41:48 -0400
Subject: fixed group sharing distribution bug and group sharing in the
 properties menu

---
 src/client/util/SharingManager.tsx  | 13 +++++++------
 src/client/util/UndoManager.ts      |  2 +-
 src/client/views/PropertiesView.tsx | 11 +++++++----
 src/fields/util.ts                  |  1 +
 4 files changed, 16 insertions(+), 11 deletions(-)

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index a51a71268..6cbc060fa 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -200,13 +200,14 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${key}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
-        // setting the same acl for a docs within the doc being shared
-        if (this.overridePrivate) {
-            var childDocs = DocListCast(target.data);
-            childDocs.map(doc => {
+        // setting the same acl for a docs within the doc being shared if they haven't been set yet
+        // or if the 'Override Private' checkbox is selected
+        var childDocs = DocListCast(target.data);
+        childDocs.map(doc => {
+            if (this.overridePrivate || doc[acl]==undefined){
                 this.setInternalGroupSharing(group, permission, doc);
-            });
-        }
+            }
+        });
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 6fef9d660..d4532c8ef 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -95,7 +95,7 @@ export namespace UndoManager {
 
     export function AddEvent(event: UndoEvent, value?: any): void {
         if (currentBatch && batchCounter.get() && !undoing) {
-            console.log('                         '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + value);
+            // console.log('                         '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + value);
             currentBatch.push(event);
             tempEvents?.push(event);
         }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 43ac2aa88..4674bc0f4 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -463,7 +463,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // adds each user to usersAdded
         SharingManager.Instance.users.forEach(eachUser => {
             var userOnDashboard = true;
-            var permission = StrCast(target[`acl-${normalizeEmail(eachUser.user.email)}`]);
             if (Doc.ActiveDashboard) {
                 if (Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == '' || Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == undefined) {
                     userOnDashboard = false;
@@ -496,9 +495,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.map(group => {
-            if (group.title != 'Public') {
-                const permission = StrCast(target[`acl-${StrCast(group.title)}`]);
-                tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+            if (group.title != 'Public' && Doc.ActiveDashboard) {
+                const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
+                if (Doc.ActiveDashboard[groupKey] != '' && Doc.ActiveDashboard[groupKey] != undefined) {
+                    const permission = StrCast(target[groupKey]);
+                    console.log(permission)
+                    tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+                }
             }
         });
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 9c0f4ba1f..469e4d944 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -195,6 +195,7 @@ export function SetCachedGroups(groups: string[]) {
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
     const targetAcls = target[DocAcl];
+    console.log(target.title, targetAcls)
     if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
 
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
-- 
cgit v1.2.3-70-g09d2


From a217c9261990cb751119ae68e7dcc7e32f48e529 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 16 Jun 2023 11:13:52 -0400
Subject: cleaner w/ effectiveAcls

---
 src/client/util/SharingManager.tsx       | 19 ++++++-------------
 src/client/views/DocComponent.tsx        | 14 +++++++++-----
 src/client/views/DocumentDecorations.tsx |  5 ++---
 src/client/views/PropertiesView.tsx      |  1 -
 src/client/views/topbar/TopBar.tsx       |  2 +-
 src/fields/util.ts                       | 32 ++++++++++++++------------------
 6 files changed, 32 insertions(+), 41 deletions(-)

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6cbc060fa..a161b4dc2 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -80,7 +80,7 @@ export class SharingManager extends React.Component<{}> {
     @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
     private populating: boolean = false; // whether the list of users is populating or not
     @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
-    @observable private overridePrivate: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
+    @observable private overridePrevious: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
     @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
 
     // private get linkVisible() {
@@ -160,10 +160,10 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
         // setting the same acl for a docs within the doc being shared if they haven't been set yet
-        // or if the 'Override Private' checkbox is selected
+        // or if the 'Override previous' checkbox is selected
         var childDocs = DocListCast(target.data);
         childDocs.map(doc => {
-            if (this.overridePrivate || doc[acl]==undefined){
+            if (this.overridePrevious || doc[acl]==undefined){
                 this.setInternalSharing(recipient, permission, doc);
             }
         });
@@ -204,7 +204,7 @@ export class SharingManager extends React.Component<{}> {
         // or if the 'Override Private' checkbox is selected
         var childDocs = DocListCast(target.data);
         childDocs.map(doc => {
-            if (this.overridePrivate || doc[acl]==undefined){
+            if (this.overridePrevious || doc[acl]==undefined){
                 this.setInternalGroupSharing(group, permission, doc);
             }
         });
@@ -279,7 +279,7 @@ export class SharingManager extends React.Component<{}> {
             const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
-                if (this.overridePrivate) this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data));
+                if (this.overridePrevious) 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);
             });
@@ -520,13 +520,6 @@ export class SharingManager extends React.Component<{}> {
             // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
             .filter(({ user }) => docs[0]?.author !== user.email)
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
-                const dashboardList = SelectionManager.Views().length < 2 ? [targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
-                // const dashboard = dashboardList[0]
-                const dashboard = Doc.ActiveDashboard;
-                var docToUse = dashboard;
-
-                docToUse = Doc.GetProto(this.targetDoc!);
-
                 const userKey = `acl-${normalizeEmail(user.email)}`;
                 const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
@@ -647,7 +640,7 @@ export class SharingManager extends React.Component<{}> {
                             <div className="acl-container">
                                 {Doc.noviceMode ? null : (
                                     <div className="layoutDoc-acls">
-                                        <input type="checkbox" onChange={action(() => (this.overridePrivate = !this.overridePrivate))} checked={this.overridePrivate} /> <label>Override Private </label>
+                                        <input type="checkbox" onChange={action(() => (this.overridePrevious = !this.overridePrevious))} checked={this.overridePrevious} /> <label>Override previous </label>
                                         <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
                                     </div>
                                 )}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index db24229dc..7e8946ca0 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,9 +1,9 @@
 import { action, computed, observable } from 'mobx';
 import { DateField } from '../../fields/DateField';
-import { DocListCast, Opt, Doc } from '../../fields/Doc';
+import { DocListCast, Opt, Doc, ReverseHierarchyMap, HierarchyMapping } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
-import { Cast, ScriptCast } from '../../fields/Types';
+import { Cast, ScriptCast, StrCast } from '../../fields/Types';
 import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
@@ -191,11 +191,15 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const added = docs;
             if (added.length) {
-                const aclKeys = Object.keys(this.props.Document[DocAcl] ?? {});
+                const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
+
                 aclKeys.forEach(key =>
                     added.forEach(d => {
-                        if (d.author === denormalizeEmail(key.substring(4)) && !d.createdFrom) {
-                            distributeAcls(key, SharingPermissions.Admin, d);
+                        if (key != 'acl-Me'){
+                            const permissionString = StrCast(Doc.GetProto(this.props.Document)[key])
+                            const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl
+                            const permission = HierarchyMapping.get(permissionSymbol)!.name
+                            distributeAcls(key, permission, d)
                         }
                     })
                 );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ff98e18d4..8e224a4b6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,7 +7,7 @@ import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
 import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, Field } from '../../fields/Doc';
+import { Doc, DocListCast, Field, HierarchyMapping } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
@@ -765,8 +765,7 @@ const dragDocView = SelectionManager.Views()[0];
         }
 
         // sharing
-        // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
-        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
+        const docShareMode = HierarchyMapping.get(GetEffectiveAcl(seldocview.rootDoc))!.name
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch (shareMode) {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 4674bc0f4..8648d84c2 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -499,7 +499,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (Doc.ActiveDashboard[groupKey] != '' && Doc.ActiveDashboard[groupKey] != undefined) {
                     const permission = StrCast(target[groupKey]);
-                    console.log(permission)
                     tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
                 }
             }
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 71daad1a9..834156295 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -103,7 +103,7 @@ export class TopBar extends React.Component {
                     }}
                 />
                 <Button
-                    text={GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'View Original'}
+                    text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'}
                     onClick={() => {
                         SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
                     }}
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 469e4d944..6c8758875 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -17,6 +17,7 @@ import { RefField } from './RefField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
+import { GroupManager } from '../client/util/GroupManager';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -171,7 +172,15 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
  * Calculates the effective access right to a document for the current user.
  */
 export function GetEffectiveAcl(target: any, user?: string): symbol {
-    target = Doc.GetProto(target);
+    if (!target) return AclPrivate;
+    if (target[UpdatingFromServer]) return AclAdmin;
+    return getEffectiveAclCache(Doc.GetProto(target), user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
+}
+
+/**
+ * Calculates the effective access layout right to a document for the current user.
+ */
+export function GetEffectiveLayoutAcl(target: any, user?: string): symbol {
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
     return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
@@ -195,7 +204,6 @@ export function SetCachedGroups(groups: string[]) {
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
     const targetAcls = target[DocAcl];
-    console.log(target.title, targetAcls)
     if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
 
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
@@ -205,31 +213,20 @@ function getEffectiveAcl(target: any, user?: string): symbol {
             // there are issues with storing fields with . in the name, so they are replaced with _ during creation
             // as a result we need to restore them again during this comparison.
             const entity = denormalizeEmail(key.substring(4)); // an individual or a group
-            if (HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) {
-                if (GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') {
+            if (GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') {
+                if (HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) {
                     effectiveAcl = value as symbol;
                 }
             }
+            console.log(targetAcls)
         }
 
-        // if there's an overriding acl set through the properties panel or sharing menu, that's what's returned if the user isn't an admin of the document
-        //const override = targetAcls['acl-Override'];
-        // if (override !== AclUnset && override !== undefined) effectiveAcl = override;
-
         return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
     }
     // authored documents are private until an ACL is set.
     const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc of Proxy, so check __fieldTuples.author and .author
     if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
     return AclAdmin;
-    let acl = AclPrivate;
-    if (user) {
-        acl = target['acl-' + user];
-    } else {
-        acl = target['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
-    }
-    console.log(target['acl-' + normalizeEmail(Doc.CurrentUserEmail)]);
-    return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(acl)!.level < aclLevel.editable ? AclEdit : acl;
 }
 /**
  * Recursively distributes the access right for a user across the children of a document and its annotations.
@@ -240,11 +237,10 @@ function getEffectiveAcl(target: any, user?: string): symbol {
  * inheritingFromCollection is not currently being used but could be used if acl assignment defaults change
  */
 export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[], isDashboard?: boolean) {
+    console.log(key,acl,target.title)
     if (!visited) visited = [] as Doc[];
     if (!target || visited.includes(target)) return;
-
     if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
-        target[key] = acl;
         Doc.GetProto(target)[key] = acl;
         if (target !== Doc.GetProto(target)) {
             //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
-- 
cgit v1.2.3-70-g09d2


From 3be20e0d7555e68662b7b63359082c9f1c3421da Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 16 Jun 2023 13:40:12 -0400
Subject: fixed groups displaying in PropertiesView (sorted by the selected doc
 rather than the open dashboard)

---
 src/client/views/PropertiesView.tsx | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8648d84c2..f5a39898e 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -462,13 +462,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // adds each user to usersAdded
         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;
+            var userOnDoc = true;
+            if (this.selectedDoc) {
+                if (this.selectedDoc['acl-' + normalizeEmail(eachUser.user.email)] == '' || this.selectedDoc['acl-' + normalizeEmail(eachUser.user.email)] == undefined) {
+                    userOnDoc = false;
                 }
             }
-            if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email != 'Public' && eachUser.user.email != target.author) {
+            if (userOnDoc && !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);
             }
@@ -491,13 +491,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // adds groups
         const groupList = GroupManager.Instance?.allGroups || [];
-        const groups = groupList.slice().sort(this.sortGroups);
-        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
-        const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
-        groupListMap.map(group => {
-            if (group.title != 'Public' && Doc.ActiveDashboard) {
+        groupList.map(group => {
+            if (group.title != 'Public' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
-                if (Doc.ActiveDashboard[groupKey] != '' && Doc.ActiveDashboard[groupKey] != undefined) {
+                if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
                     const permission = StrCast(target[groupKey]);
                     tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
                 }
-- 
cgit v1.2.3-70-g09d2


From 116961977c69b77d7437a1c1b9719fc977060caf Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 16 Jun 2023 21:53:34 -0400
Subject: PropertiesView UI updates and sharing symbol icon in heirarchy
 mapping

---
 src/client/views/DocumentDecorations.tsx | 24 ++-------------
 src/client/views/PropertiesView.scss     | 21 ++++++-------
 src/client/views/PropertiesView.tsx      | 51 +++++++-------------------------
 src/fields/Doc.ts                        | 16 +++++-----
 4 files changed, 32 insertions(+), 80 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c82257d1f..fdbe2f866 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,7 +7,7 @@ import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
 import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, Field, HierarchyMapping } from '../../fields/Doc';
+import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
@@ -767,27 +767,7 @@ const dragDocView = SelectionManager.Views()[0];
         // sharing
         const docShareMode = HierarchyMapping.get(GetEffectiveAcl(seldocview.rootDoc))!.name
         const shareMode = StrCast(docShareMode);
-        var shareSymbolIcon = null;
-        switch (shareMode) {
-            case SharingPermissions.Admin:
-                shareSymbolIcon = '⬢ ';
-                break;
-            case SharingPermissions.Edit:
-                shareSymbolIcon = '⬢ ';
-                break;
-            case SharingPermissions.Augment:
-                shareSymbolIcon = '⬟ ';
-                break;
-            case SharingPermissions.View:
-                shareSymbolIcon = '♦ ';
-                break;
-            case SharingPermissions.None:
-                shareSymbolIcon = '▲ ';
-                break;
-            default:
-                shareSymbolIcon = '';
-                break;
-        }
+        var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image;
 
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 3cf7f2546..7e0da5520 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -162,8 +162,8 @@
 
     .propertiesView-shareDropDown{
         margin-right: 10px;
-        width: 92%;
-
+        min-width: 65px;
+        
         & .propertiesView-shareDropDownNone{
             height: 16px;
             padding: 0px;
@@ -370,13 +370,13 @@
     }
 
     .expansion-button {
-        margin-left: 0;
+        margin-left: -15px;
+        margin-right: 20px;
 
         .expansion-button-icon {
             width: 11px;
             height: 11px;
             color: black;
-            margin-left: -15px;
 
             &:hover {
                 color: rgb(131, 131, 131);
@@ -394,8 +394,7 @@
         border-radius: 6px; // remove when adding buttons
         margin-right: 10px; // remove when adding buttons
         background-color: #ececec;
-        max-height: 130px;
-        width: 92%;
+        width: 97%;
 
         .propertiesView-sharingTable-item {
             display: flex;
@@ -420,10 +419,10 @@
 
             .propertiesView-sharingTable-item-permission {
                 display: flex;
-                position: fixed;
-                right: 30px;
                 align-items: flex-end;
+                text-align: right;
                 margin-left: auto;
+                margin-right: -12px;
             }
 
             &:last-child {
@@ -437,8 +436,10 @@
         background: inherit;
         border: none;
         background: inherit;
-        text-align: justify; // for Edge
-        text-align-last: end;
+        width: max;
+        text-align: left;
+        display: flex;
+        right: 35px;
     }
 
     .propertiesView-fields {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index f5a39898e..c7db657ff 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -3,19 +3,19 @@ import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox, Tooltip } from '@material-ui/core';
-import { intersection } from 'lodash';
+import { concat, intersection } from 'lodash';
 import { Lambda, action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
-import { Doc, Field, FieldResult, NumListCast, Opt, StrListCast } from '../../fields/Doc';
-import { DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
+import { Doc, Field, FieldResult, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
+import { AclAdmin, DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { ComputedField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { SharingPermissions, normalizeEmail } from '../../fields/util';
+import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
@@ -321,7 +321,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     .filter(permission => !Doc.noviceMode || ![SharingPermissions.View].includes(permission as any))
                     .map(permission => (
                         <option className="propertiesView-permisssions-select" key={permission} value={permission}>
-                            {permission}
+                            {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
                         </option>
                     ))}
             </select>
@@ -391,32 +391,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns a colored dropdown bar reflective of the permission
      */
     colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
-        var dropDownText = '';
-        switch (StrCast(permission)) {
-            case SharingPermissions.Admin:
-                dropDownText = '⬢ ';
-                break;
-            case SharingPermissions.Edit:
-                dropDownText = '⬢ ';
-                break;
-            case SharingPermissions.Augment:
-                dropDownText = '⬟ ';
-                break;
-            case SharingPermissions.View:
-                dropDownText = '♦ ';
-                break;
-            case SharingPermissions.None:
-                dropDownText = '▲ ';
-                break;
-        }
+        var shareImage = ReverseHierarchyMap.get(permission)?.image;
         return (
             <div>
                 <div className={'propertiesView-shareDropDown'}>
                     <div className={`propertiesView-shareDropDown${permission}`}>
                         <div className="propertiesView-shareDropDown">
-                            {' '}
-                            {dropDownText}
-                            {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
+                            {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}
                         </div>
                     </div>
                 </div>
@@ -442,21 +423,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns the sharing and permissions panel.
      */
     @computed get sharingTable() {
-        const docToUse = this.selectedDoc;
-        if (!docToUse) {
-            return null;
-        }
         // all selected docs
         const docs =
             SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
-
         const target = docs[0];
 
-        // const effectiveAcls = GetEffectiveAcl(target);
-        // const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
-        const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
-        const showAdmin = curUserAcl == 'Admin' || curUserAcl == 'Owner';
-
+        const showAdmin = GetEffectiveAcl(this.selectedDoc!) == AclAdmin
         const tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
@@ -469,12 +441,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 }
             }
             if (userOnDoc && !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);
             }
         });
 
-        // adds each user to the table
+        // sorts and then adds each user to the table
         usersAdded.sort(this.sortUsers);
         usersAdded.map(userEmail => {
             const permission = StrCast(target[`acl-${normalizeEmail(userEmail)}`]);
@@ -507,11 +478,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return (
             <div>
                 {' '}
-                Sharing Mode for Public / Guest Users
+                Public / Guest Users
                 <div>{this.colorACLDropDown('Public', showAdmin, StrCast(target['acl-Public']), false)}</div>
                 <div>
                     {' '}
-                    <br></br> Sharing Modes for Individual Users and Groups with Access to the Dashboard{' '}
+                    <br></br> Individual Users with Access to this Document{' '}
                 </div>
                 <div className="propertiesView-sharingTable">{<div> {tableEntries}</div>}</div>
             </div>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 3116fd70b..8f9f36153 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -130,15 +130,15 @@ export enum aclLevel {
     admin = 4,
 }
 // prettier-ignore
-export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermissions }> = new Map([
-    [AclPrivate,  { level: aclLevel.unshared,     name: SharingPermissions.None }],
-    [AclReadonly, { level: aclLevel.viewable,     name: SharingPermissions.View }],
-    [AclAugment,  { level: aclLevel.augmentable,  name: SharingPermissions.Augment}],
-    [AclEdit,     { level: aclLevel.editable,     name: SharingPermissions.Edit }],
-    [AclAdmin,    { level: aclLevel.admin,        name: SharingPermissions.Admin }],
-    [AclUnset,    { level: aclLevel.unset,        name: SharingPermissions.Unset }],
+export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermissions; image: string }> = new Map([
+    [AclPrivate,  { level: aclLevel.unshared,     name: SharingPermissions.None,    image: '▲' }],
+    [AclReadonly, { level: aclLevel.viewable,     name: SharingPermissions.View,    image: '♦' }],
+    [AclAugment,  { level: aclLevel.augmentable,  name: SharingPermissions.Augment, image: '⬟' }],
+    [AclEdit,     { level: aclLevel.editable,     name: SharingPermissions.Edit,    image: '⬢' }],
+    [AclAdmin,    { level: aclLevel.admin,        name: SharingPermissions.Admin,   image: '⬢' }],
+    [AclUnset,    { level: aclLevel.unset,        name: SharingPermissions.Unset,   image: '▲' }],
 ]);
-export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }> = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0] }]));
+export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol ; image: string}> = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0], image: value[1].image }]));
 
 // caches the document access permissions for the current user.
 // this recursively updates all protos as well.
-- 
cgit v1.2.3-70-g09d2


From a481170445975dbc964d38d99d65012856c32d68 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 19 Jun 2023 17:25:14 -0400
Subject: groups in Properties View

---
 src/client/views/PropertiesView.tsx | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index c7db657ff..ab3864849 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -412,13 +412,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return u1 > u2 ? -1 : u1 === u2 ? 0 : 1;
     };
 
-    /**
-     * Sorting algorithm to sort groups.
-     */
-    sortGroups = (g1: Doc, g2: Doc) => {
-        return g1 > g2 ? -1 : g1 === g2 ? 0 : 1;
-    };
-
     /**
      * @returns the sharing and permissions panel.
      */
@@ -429,7 +422,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const target = docs[0];
 
         const showAdmin = GetEffectiveAcl(this.selectedDoc!) == AclAdmin
-        const tableEntries = [];
+        const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
         // adds each user to usersAdded
@@ -449,32 +442,33 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         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
+            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission, false)); // adds each user
         });
 
         // adds 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
+            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, StrCast(target[`acl-${normalizeEmail(userEmail)}`]), false)); // adds each user
             usersAdded.push(userEmail);
         }
 
+        // shift owner to top
+        individualTableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
+
         // adds groups
+        const groupTableEntries: JSX.Element[] = [];
         const groupList = GroupManager.Instance?.allGroups || [];
         groupList.map(group => {
             if (group.title != 'Public' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
                     const permission = StrCast(target[groupKey]);
-                    tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+                    groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
                 }
             }
         });
 
-        // shift owner to top
-        tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
-
         return (
             <div>
                 {' '}
@@ -484,7 +478,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     {' '}
                     <br></br> Individual Users with Access to this Document{' '}
                 </div>
-                <div className="propertiesView-sharingTable">{<div> {tableEntries}</div>}</div>
+                <div className="propertiesView-sharingTable">{<div> {individualTableEntries}</div>}</div>
+                {groupTableEntries.length>0 ? 
+                    <div>
+                        <div>
+                            {' '}
+                            <br></br> Groups with Access to this Document{' '}
+                        </div>
+                        <div className="propertiesView-sharingTable">{<div> {groupTableEntries}</div>}</div>
+                    </div>
+                : null}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 50ee9be87269ced48f0c0c8867316c9ff0b4258b Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Tue, 20 Jun 2023 11:55:32 -0400
Subject: fixing buttons tues morn

---
 src/client/views/MainView.tsx          |  2 +-
 src/client/views/PropertiesButtons.tsx | 96 +++++++++++++++++-----------------
 src/client/views/PropertiesView.scss   | 11 ++++
 src/client/views/PropertiesView.tsx    | 53 +++++++++----------
 4 files changed, 85 insertions(+), 77 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 50f451c0a..541b74722 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -810,7 +810,7 @@ export class MainView extends React.Component {
                         {this.dockingContent}
 
                         {this._hideUI ? null : (
-                            <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
+                            <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1, background : 'linen' }}>
                                 <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
                             </div>
                         )}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 749b33539..7527a0793 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -56,7 +56,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedDoc;
         const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => ((dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true);
 
-        console.log('current icon ' + icon(targetDoc?.[property]));
+     //   console.log('current icon ' + icon(targetDoc?.[property]));
 
         return !targetDoc ? null : (
             <Tooltip title={<div className={`dash-tooltip`}>{tooltip(targetDoc?.[property])} </div>} placement="top">
@@ -141,14 +141,14 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         );
     }
 
-    // @computed get forceActiveButton() { // WHAT HAPPENS HERE
-    //     return this.propertyToggleBtn(
-    //         on => 'Active',
-    //         '_forceActive',
-    //         on => `${on ? 'Select to activate' : 'Contents always active'} `,
-    //         on => 'eye'
-    //     );
-    // }
+    @computed get forceActiveButton() { //select text
+        return this.propertyToggleBtn(
+            on => 'Active',
+            '_forceActive',
+            on => `${on ? 'Select to activate' : 'Contents always active'} `,
+            on => 'eye'
+        );
+    }
 
     @computed get fitContentButton() {
         return this.propertyToggleBtn(
@@ -162,7 +162,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     // // this implments a container pattern by marking the targetDoc (collection) as a lightbox
     // // that always fits its contents to its container and that hides all other documents when
     // // a link is followed that targets a 'lightbox' destination
-    // @computed get isLightboxButton() { WHAT IS THIS
+    // @computed get isLightboxButton() { // developer
     //     return this.propertyToggleBtn(
     //         on => 'Lightbox',
     //         'isLightbox',
@@ -205,31 +205,31 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         );
     }
 
-    // @computed get chromeButton() { // WHAT
-    //     return this.propertyToggleBtn(
-    //         on => 'Controls',
-    //         '_chromeHidden',
-    //         on => `${on ? 'Show' : 'Hide'} editing UI`,
-    //         on => 'edit',
-    //         (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden)
-    //     );
-    // }
+    @computed get chromeButton() { // developer -- removing UI decoration
+        return this.propertyToggleBtn(
+            on => 'Controls',
+            '_chromeHidden',
+            on => `${on ? 'Show' : 'Hide'} editing UI`,
+            on => 'edit',
+            (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden)
+        );
+    }
 
-    // @computed get layout_autoHeightButton() { // shouldn't be here unless you can set it back to its original size
-    //     return this.propertyToggleBtn(
-    //         on => 'Auto\xA0Size',
-    //         '_layout_autoHeight',
-    //         on => `Automatical vertical sizing to show all content`,
-    //         on => 'arrows-alt-v'
-    //     );
-    // }
+    @computed get layout_autoHeightButton() { // shouldn't be here unless you can set it back to its original size
+        return this.propertyToggleBtn(
+            on => 'Auto\xA0Size',
+            '_layout_autoHeight',
+            on => `Automatical vertical sizing to show all content`,
+            on => 'arrows-alt-v'
+        );
+    }
 
     @computed get gridButton() {
         return this.propertyToggleBtn(
             on => (on ? 'HIDE GRID' : 'DISPLAY GRID'),
             '_freeform_backgroundGrid',
             on => `Display background grid in collection`,
-            on => (on ? <MdGridOn /> : <MdGridOff />) //'border-all'
+            on => (on ? <MdGridOff /> :<MdGridOn /> ) //'border-all'
         );
     }
 
@@ -294,23 +294,23 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     //         </Tooltip>
     //     );
     // }
-    @computed
-    get perspectiveButton() {
-        return !this.selectedDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
-                <div>
-                    <div className="propertiesButtons-linkFlyout">
-                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
-                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
-                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                            </div>
-                        </Flyout>
-                    </div>
-                    <div className="propertiesButtons-title"> Perspective </div>
-                </div>
-            </Tooltip>
-        );
-    }
+    // @computed
+    // get perspectiveButton() { //WHERE ARE YOU
+    //     return !this.selectedDoc ? null : (
+    //         <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
+    //             <div>
+    //                 <div className="propertiesButtons-linkFlyout">
+    //                     <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
+    //                         <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
+    //                             <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
+    //                         </div>
+    //                     </Flyout>
+    //                 </div>
+    //                 <div className="propertiesButtons-title"> Perspective </div>
+    //             </div>
+    //         </Tooltip>
+    //     );
+    // }
 
     @undoBatch
     handlePerspectiveChange = (e: any) => {
@@ -447,13 +447,13 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {/* {toggle(this.onClickButton)} */}
                 {toggle(this.layout_fitWidthButton)}
                 {/* {toggle(this.freezeThumb)} */}
-                {/* {toggle(this.forceActiveButton)} */}
+                {toggle(this.forceActiveButton)}
                 {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {/* {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} */}
-                {/* {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} */}
+                {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
                 {toggle(this.maskButton, { display: !isInk ? 'none' : '' })}
                 {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })}
-                {/* {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })} */}
+                {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })}
                 {toggle(this.gridButton, { display: !isCollection ? 'none' : '' })}
                 {/* {toggle(this.groupButton, { display: isTabView || !isCollection ? 'none' : '' })} */}
                 {/* {toggle(this.snapButton, { display: !isCollection ? 'none' : '' })} */}
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 49f2fc6fe..a9577b232 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -14,6 +14,10 @@
     overflow-x: hidden;
     overflow-y: auto;
 
+    .propertiesView-propAndInfoGrouping{
+        display: flex;
+    }
+
     .propertiesView-title {
         text-align: left;
         padding-top: 12px;
@@ -40,6 +44,13 @@
         }
     }
 
+    .propertiesView-info{
+        margin-top: 20;
+        margin-right: 10;
+        float: right;
+        font-size: 20;
+    }
+
     .propertiesView-name {
        // border-bottom: 1px solid black;
         padding: 8.5px;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 9c327b3f1..6b208f25e 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,6 +1,6 @@
 import React = require('react');
 import { IconLookup } from '@fortawesome/fontawesome-svg-core';
-import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
+import { faAnchor, faArrowRight, faListNumeric, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox, Icon, Tooltip } from '@material-ui/core';
 import { intersection } from 'lodash';
@@ -37,6 +37,7 @@ import { DefaultStyleProvider } from './StyleProvider';
 import { RichTextField } from '../../fields/RichTextField';
 import { AiFillFileText } from "react-icons/ai" //* as Icons from "react-icons/ai" //
 import * as Icons from "react-icons/bs" //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs"
+import { GrCircleInformation } from 'react-icons/gr'
 import { CgBrowser} from "react-icons/cg"
 import { ImageField, VideoField, WebField } from '../../fields/URLField';
 import { FaFileVideo } from 'react-icons/fa'; //* as Icons from "react-icons/fa"; //
@@ -114,6 +115,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @observable inOptions: boolean = false;
     @observable _controlButton: boolean = false;
 
+
+    @observable allClose: boolean = false;
+
     componentDidMount() {
         this.selectedDocListenerDisposer?.();
         // this.selectedDocListenerDisposer = autorun(() => this.openFilters && this.selectedDoc && this.checkFilterDoc());
@@ -504,7 +508,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get currentType() {
-        console.log("current type " + this.selectedDoc?.type)
+       // console.log("current type " + this.selectedDoc?.type)
 
         const documentType = StrCast(this.selectedDoc?.type)
         var currentType: string = documentType;
@@ -1201,37 +1205,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
-    @computed get doubleClickClose() {
-        //action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = !this.openTransform))
-        //console.log(this.openContexts + " hello " + this.openOptions + this.openTransform + this.openLinks + this.openFields)
-        if (this.openContexts || this.openOptions || this.openTransform || this.openLinks || this.openFields){
-            this.openContexts = false;
-            this.openOptions = false;
-            this.openTransform = false;
-            this.openLinks = false;
-            this.openFields = false;
-            
-        } else if (!this.openContexts && !this.openOptions && !this.openTransform && !this.openLinks && !this.openFields){
-            this.openContexts = true;
-            this.openOptions = true;
-            this.openTransform = true;
-            this.openLinks = true;
-            this.openFields = true;
 
-        }
-        
-        return (
-            console.log("made")
-        )
-    }
+// create observable with current property
+// if everyother one is closed, set allClsoe  = true
+// on double click close all except current
+// if everything is closed on double clikc then open everythigng 
+
+// if everythign is closed except current property ==> double click = open all
+// else ==> double click = close except current property
 
     @computed get linksSubMenu() {
     //     onPointerDown={action(() => (this.openLinks = !this.openLinks))}
-        
+    //(this.openContexts = false, this.openOptions = false, this.openTransform = false))} onClick={action(() => (this.openLinks = !this.openLinks))
+        //this.allClose == false ? (this.openContexts = false , this.allClose = true ): (this.openContexts = true, this.allClose = false)
         return (
             <div className="propertiesView-contexts">
             
-                <div className="propertiesView-contexts-title" onDoubleClick={action(() => ( this.doubleClickClose, this.openLinks = true))} onClick={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
+                <div className="propertiesView-contexts-title" style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
                     Linked To
                     <div className="propertiesView-contexts-title-icon">
                         <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1492,9 +1482,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             minWidth: this.props.width,
                             //overflowY: this.scrolling ? "scroll" : "visible"
                         }}>
-                        <div className="propertiesView-title" style={{ width: this.props.width }}>
-                            Properties
+                        <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>
+
+                        
                         <div className="propertiesView-name">{this.editableTitle}</div>
 
                         <div className = "propertiesView-type"> {this.currentType} </div>
-- 
cgit v1.2.3-70-g09d2


From 899a6bdeb8ab0ce749328fc6093496daada1c4ad Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 21 Jun 2023 10:17:31 -0400
Subject: sort groups in PropertiesView

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

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index ab3864849..825a802cd 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -412,6 +412,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return u1 > u2 ? -1 : u1 === u2 ? 0 : 1;
     };
 
+    /**
+     * Sorting algorithm to sort groups.
+     */
+    sortGroups = (group1: Doc, group2: Doc) => {
+        const g1 = StrCast(group1.title);
+        const g2 = StrCast(group2.title);
+        return g1 > g2 ? -1 : g1 === g2 ? 0 : 1;
+    };
+
     /**
      * @returns the sharing and permissions panel.
      */
@@ -459,6 +468,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // adds groups
         const groupTableEntries: JSX.Element[] = [];
         const groupList = GroupManager.Instance?.allGroups || [];
+        groupList.sort(this.sortGroups)
         groupList.map(group => {
             if (group.title != 'Public' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
-- 
cgit v1.2.3-70-g09d2


From c357aefb0668780b3bb4f96270886e0570723d09 Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Wed, 21 Jun 2023 12:31:14 -0400
Subject: double click working with small bug

---
 src/client/views/PropertiesView.tsx | 51 ++++++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 6b208f25e..89a355ae3 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -43,7 +43,7 @@ import { ImageField, VideoField, WebField } from '../../fields/URLField';
 import { FaFileVideo } from 'react-icons/fa'; //* as Icons from "react-icons/fa"; //
 import { IconButton } from 'browndash-components';
 import { IconBase } from 'react-icons';
-import { MdOutlineMedicalServices } from 'react-icons/md';
+import { MdOutlineAddShoppingCart, MdOutlineMedicalServices } from 'react-icons/md';
 import { createPromiseCapability } from 'pdfjs-dist';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
@@ -115,9 +115,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @observable inOptions: boolean = false;
     @observable _controlButton: boolean = false;
 
-
-    @observable allClose: boolean = false;
-
     componentDidMount() {
         this.selectedDocListenerDisposer?.();
         // this.selectedDocListenerDisposer = autorun(() => this.openFilters && this.selectedDoc && this.checkFilterDoc());
@@ -1055,7 +1052,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get optionsSubMenu() {
         return (
             <div className="propertiesView-settings" onPointerEnter={action(() => (this.inOptions = true))} onPointerLeave={action(() => (this.inOptions = false))}>
-                <div className="propertiesView-settings-title" onPointerDown={action(() => (this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}>
+                <div className="propertiesView-settings-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform = false,
+                this.openFields = false, this.openSharing = false, this.openFilters = false, this.openLayout = false))}
+                onClick={action(() => (this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}>
                     Options
                     <div className="propertiesView-settings-title-icon">
                         <FontAwesomeIcon icon={this.openOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1073,7 +1072,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get sharingSubMenu() {
         return (
             <div className="propertiesView-sharing">
-                <div className="propertiesView-sharing-title" onPointerDown={action(() => (this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}>
+                <div className="propertiesView-sharing-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform = false, 
+                this.openOptions = false, this.openFields = false, this.openFilters = false, this.openLayout))}
+                onClick={action(() => (this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}>
                     Sharing {'&'} Permissions
                     <div className="propertiesView-sharing-title-icon">
                         <FontAwesomeIcon icon={this.openSharing ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1128,7 +1129,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get filtersSubMenu() {
         return (
             <div className="propertiesView-filters">
-                <div className="propertiesView-filters-title" onPointerDown={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
+                <div className="propertiesView-filters-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform = false,
+                this.openOptions = false, this.openFields = false, this.openSharing = false, this.openLayout = false))}
+                onClick={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
                     Filters
                     <div className="propertiesView-filters-title-icon">
                         <FontAwesomeIcon icon={this.openFilters ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1159,7 +1162,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 )}
 
                 <div className="propertiesView-transform">
-                    <div className="propertiesView-transform-title" onPointerDown={action(() => (this.openTransform = !this.openTransform))} style={{ backgroundColor: this.openTransform ? 'black' : '' }}>
+                    <div className="propertiesView-transform-title" onClick={action(() => (this.openTransform = !this.openTransform))}  onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openOptions = false,
+                    this.openFields = false, this.openSharing = false, this.openFilters = false, this.openLayout = false, this.openTransform = true))}
+                     style={{ backgroundColor: this.openTransform ? 'black' : '' }}>
                         Transform
                         <div className="propertiesView-transform-title-icon">
                             <FontAwesomeIcon icon={this.openTransform ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1174,7 +1179,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get fieldsSubMenu() {
         return (
             <div className="propertiesView-fields">
-                <div className="propertiesView-fields-title" onPointerDown={action(() => (this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}>
+                <div className="propertiesView-fields-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform = false, 
+                this.openOptions = false, this.openSharing = false, this.openFilters = false, this.openLayout = false))}
+                 onClick={action(() => (this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}>
                     Fields {'&'} Tags
                     <div className="propertiesView-fields-title-icon">
                         <FontAwesomeIcon icon={this.openFields ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1194,7 +1201,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get contextsSubMenu() {
         return (
             <div className="propertiesView-contexts">
-                <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}>
+                <div className="propertiesView-contexts-title" onDoubleClick={action(() => (this.openLinks = false, this.openTransform = false, this.openOptions = false, this.openFields = false,
+                 this.openSharing = false, this.openFilters = false, this.openLayout = false ))}
+                      onClick ={action(() => (this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}>
                     Other Contexts
                     <div className="propertiesView-contexts-title-icon">
                         <FontAwesomeIcon icon={this.openContexts ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1205,30 +1214,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    @computed get linksSubMenu() {
+        var isDouble: boolean = false;
 
-// create observable with current property
-// if everyother one is closed, set allClsoe  = true
-// on double click close all except current
-// if everything is closed on double clikc then open everythigng 
-
-// if everythign is closed except current property ==> double click = open all
-// else ==> double click = close except current property
 
-    @computed get linksSubMenu() {
-    //     onPointerDown={action(() => (this.openLinks = !this.openLinks))}
-    //(this.openContexts = false, this.openOptions = false, this.openTransform = false))} onClick={action(() => (this.openLinks = !this.openLinks))
-        //this.allClose == false ? (this.openContexts = false , this.allClose = true ): (this.openContexts = true, this.allClose = false)
         return (
             <div className="propertiesView-contexts">
             
-                <div className="propertiesView-contexts-title" style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
+                <div className="propertiesView-contexts-title" onClick={action(() => (this.openLinks = !this.openLinks))} onDoubleClick={action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = false, 
+                this.openFields = false, this.openSharing = false, this.openLayout = false, this.openFilters = false, this.openLinks = false ))}
+                    style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
                     Linked To
                     <div className="propertiesView-contexts-title-icon">
                         <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
                     </div>
                 </div>
                 {!this.openLinks ? null : this.linkCount > 0 ? <div className="propertiesView-contexts-content">{this.links}</div> : <div className="propertiesView-contexts-content">There are no current links.</div>}
-                {/* {!this.openLinks ? null : <div className="propertiesView-contexts-content">{this.links}</div>} */}
             </div>
         );
     }
@@ -1236,7 +1237,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get layoutSubMenu() {
         return (
             <div className="propertiesView-layout">
-                <div className="propertiesView-layout-title" onPointerDown={action(() => (this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}>
+                <div className="propertiesView-layout-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform  = false,
+                this.openOptions = false, this.openFields = false, this.openSharing = false, this.openFilters = false ))}
+                 onClick={action(() => (this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}>
                     Layout
                     <div className="propertiesView-layout-title-icon">
                         <FontAwesomeIcon icon={this.openLayout ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-- 
cgit v1.2.3-70-g09d2


From ddf20606684cd8755571a82eed1c4ffdc27f9274 Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Wed, 21 Jun 2023 15:15:00 -0400
Subject: fixed double click bug for links

---
 src/client/views/PropertiesView.tsx | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 89a355ae3..a0c45bc70 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1214,15 +1214,40 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+
+
     @computed get linksSubMenu() {
-        var isDouble: boolean = false;
+//onClick={action(() => (this.openLinks = !this.openLinks))} onDoubleClick={action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = false, 
+//this.openFields = false, this.openSharing = false, this.openLayout = false, this.openFilters = false, this.openLinks = false ))}
 
+        let isDouble = false;
 
         return (
             <div className="propertiesView-contexts">
             
-                <div className="propertiesView-contexts-title" onClick={action(() => (this.openLinks = !this.openLinks))} onDoubleClick={action(() => (this.openContexts = false, this.openOptions = false, this.openTransform = false, 
-                this.openFields = false, this.openSharing = false, this.openLayout = false, this.openFilters = false, this.openLinks = false ))}
+                <div className="propertiesView-contexts-title" onClick = {action(() => {
+                    if (!isDouble){
+                        this.openLinks = !this.openLinks
+                    } } )}
+
+                    onDoubleClick = {action(() => {
+                        isDouble = true;
+
+                        this.openContexts = false;
+                        this.openOptions = false;
+                        this.openTransform = false;
+                        this.openFields = false;
+                        this.openSharing = false;
+                        this.openLayout = false;
+                        this.openFilters = false;
+                        this.openLinks = true;
+
+                        setTimeout(() => {
+                            isDouble = false;
+                        }, 300)
+                    }
+                        
+        )}
                     style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
                     Linked To
                     <div className="propertiesView-contexts-title-icon">
-- 
cgit v1.2.3-70-g09d2


From 72dad52c4fea5a2fccabacbb6a49ca1494093954 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 22 Jun 2023 10:14:26 -0400
Subject: cleaned up dragging - abort presItem drags properly, fix naming for
 drag/dropAction, small fix undo of drag removeproperties, dragging in/out of
 headerBar,  fixed isContentActive to avoid SnappingManager.GetIsDragging. 
 fixed resizing videos.

---
 src/client/documents/Documents.ts                  | 49 +++++------
 src/client/util/CurrentUserUtils.ts                | 81 +++++++++---------
 src/client/util/DragManager.ts                     | 38 ++++-----
 src/client/util/DropConverter.ts                   |  8 +-
 src/client/util/UndoManager.ts                     | 19 ++---
 src/client/views/DashboardView.tsx                 |  6 +-
 src/client/views/DocComponent.tsx                  |  6 +-
 src/client/views/DocumentDecorations.tsx           |  8 +-
 src/client/views/GestureOverlay.tsx                | 95 +---------------------
 src/client/views/MainView.tsx                      | 12 ++-
 src/client/views/Palette.scss                      | 30 -------
 src/client/views/Palette.tsx                       | 69 ----------------
 src/client/views/PropertiesView.tsx                |  1 -
 src/client/views/StyleProvider.tsx                 |  1 +
 src/client/views/TemplateMenu.tsx                  |  5 +-
 .../views/collections/CollectionCarouselView.tsx   |  4 +-
 .../views/collections/CollectionNoteTakingView.tsx |  6 +-
 .../views/collections/CollectionPileView.tsx       | 11 ++-
 .../collections/CollectionStackedTimeline.scss     |  2 +-
 .../collections/CollectionStackedTimeline.tsx      | 10 ++-
 .../views/collections/CollectionStackingView.tsx   | 18 ++--
 src/client/views/collections/CollectionSubView.tsx | 10 +--
 .../views/collections/CollectionTimeView.tsx       |  2 -
 .../views/collections/CollectionTreeView.tsx       | 14 ++--
 src/client/views/collections/CollectionView.tsx    |  5 +-
 src/client/views/collections/TabDocView.tsx        |  1 -
 src/client/views/collections/TreeView.tsx          | 29 ++++---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 11 ++-
 .../collections/collectionFreeForm/MarqueeView.tsx |  1 -
 .../collectionGrid/CollectionGridView.tsx          |  2 +-
 .../collectionLinear/CollectionLinearView.tsx      | 40 ++-------
 .../CollectionMulticolumnView.tsx                  | 18 ++--
 .../CollectionMultirowView.tsx                     | 19 +++--
 .../collectionSchema/CollectionSchemaView.tsx      |  3 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |  8 +-
 .../collectionSchema/SchemaTableCell.tsx           |  2 +-
 src/client/views/linking/LinkMenuItem.tsx          |  2 +-
 src/client/views/linking/LinkPopup.tsx             |  1 -
 src/client/views/nodes/ComparisonBox.scss          |  2 +-
 src/client/views/nodes/ComparisonBox.tsx           | 80 ++++++++++++------
 src/client/views/nodes/DocumentView.tsx            | 52 +++++++-----
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/ImageBox.tsx                |  4 +-
 src/client/views/nodes/KeyValueBox.tsx             |  8 +-
 src/client/views/nodes/KeyValuePair.tsx            |  1 -
 src/client/views/nodes/LinkAnchorBox.tsx           |  2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  5 +-
 src/client/views/nodes/MapBox/MapBox2.tsx          |  2 +-
 src/client/views/nodes/PDFBox.tsx                  |  2 -
 src/client/views/nodes/WebBox.tsx                  | 14 +++-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 21 ++---
 src/client/views/nodes/trails/PresBox.tsx          |  4 +-
 src/client/views/nodes/trails/PresElementBox.tsx   | 13 +--
 src/client/views/pdf/PDFViewer.tsx                 | 18 ++--
 src/fields/Doc.ts                                  |  2 +-
 src/fields/documentSchemas.ts                      | 10 +--
 src/fields/util.ts                                 |  4 +-
 57 files changed, 356 insertions(+), 537 deletions(-)
 delete mode 100644 src/client/views/Palette.scss
 delete mode 100644 src/client/views/Palette.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ffde9fe1b..a61ef1da3 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -25,7 +25,7 @@ import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox';
 import { FollowLinkScript } from '../util/LinkFollower';
 import { LinkManager } from '../util/LinkManager';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
-import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
+import { undoable, UndoManager } from '../util/UndoManager';
 import { CollectionDockingView } from '../views/collections/CollectionDockingView';
 import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionView } from '../views/collections/CollectionView';
@@ -184,7 +184,8 @@ export class DocumentOptions {
     opacity?: NUMt = new NumInfo('document opacity');
     viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters');
     dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc');
-    dontUndo?: BOOLt = new BoolInfo('whether button clicks should be undoable (true for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)');
+    _undoIgnoreFields?: List<string>; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
+    undoIgnoreFields?: List<string>; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
     _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title');
     _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes');
     _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
@@ -355,20 +356,19 @@ export class DocumentOptions {
     waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire.  'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
     onPointerDown?: ScriptField;
     onPointerUp?: ScriptField;
-    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     openFactoryLocation?: string; // an OpenWhere value to place the factory created document
     openFactoryAsDelegate?: BOOLt = new BoolInfo('create a delegate of the factory');
     _forceActive?: BOOLt = new BoolInfo('flag to handle pointer events when not selected (or otherwise active)');
-    enableDragWhenActive?: BOOLt = new BoolInfo('allow dragging even if document contentts are active (e.g., tree, groups)');
-    _stayInCollection?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere');
+    _dragOnlyWithinContainer?: BOOLt = new BoolInfo('whether the document should remain in its collection when someone tries to drag and drop it elsewhere');
     _raiseWhenDragged?: BOOLt = new BoolInfo('whether a document is brought to front when dragged.');
-    allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?', true);
-    childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
-    targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? ');
+    childDragAction?: DROPt = new DAInfo('what should happen to the child documents when they are dragged fromt he collection');
+    dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
+    dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
     _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
-    _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
+    _dropPropertiesToRemove?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
-    dropConverter?: ScriptField; // script to run when documents are dropped on this Document.
+    dragAction?: DROPt = new DAInfo('how to drag document when it is active (e.g., tree, groups)');
+    dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
     dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script
     clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
@@ -536,7 +536,7 @@ export namespace Docs {
                         link_description: '',
                         layout_showCaption: 'link_description',
                         backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
-                        _removeDropProperties: new List(['onClick']),
+                        _dropPropertiesToRemove: new List(['onClick']),
                     },
                 },
             ],
@@ -545,7 +545,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'embed', title: 'Global Link Database' },
+                    options: { title: 'Global Link Database' },
                 },
             ],
             [
@@ -553,7 +553,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'embed', title: 'Global Script Database' },
+                    options: { title: 'Global Script Database' },
                 },
             ],
             [
@@ -615,7 +615,7 @@ export namespace Docs {
                 DocumentType.FONTICON,
                 {
                     layout: { view: FontIconBox, dataField: 'icon' },
-                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, layout_hideLinkButton: true, _width: 40, _height: 40 },
+                    options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideLinkButton: true, _width: 40, _height: 40 },
                 },
             ],
             [
@@ -659,7 +659,7 @@ export namespace Docs {
                 {
                     data: '',
                     layout: { view: ComparisonBox, dataField: defaultDataKey },
-                    options: { backgroundColor: 'gray', targetDropAction: 'embed' },
+                    options: { backgroundColor: 'gray', dropAction: 'move', waitForDoubleClickToClick: 'always' },
                 },
             ],
             [
@@ -667,7 +667,7 @@ export namespace Docs {
                 {
                     data: new List<Doc>(),
                     layout: { view: EmptyBox, dataField: defaultDataKey },
-                    options: { childDropAction: 'embed', title: 'Global Group Database' },
+                    options: { childDragAction: 'embed', title: 'Global Group Database' },
                 },
             ],
             [
@@ -1066,12 +1066,7 @@ export namespace Docs {
         }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(
-                Prototypes.get(DocumentType.COL),
-                new List(documents),
-                { enableDragWhenActive: true, _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile },
-                id
-            );
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { dropAction: 'move', _forceActive: true, _freeform_noZoom: true, _freeform_noAutoPan: true, ...options, _type_collection: CollectionViewType.Pile }, id);
         }
 
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -1523,7 +1518,7 @@ export namespace DocUtils {
         !simpleMenu &&
             ContextMenu.Instance.addItem({
                 description: 'Quick Notes',
-                subitems: DocListCast((Doc.UserDoc()['template-notes'] as Doc).data).map((note, i) => ({
+                subitems: DocListCast((Doc.UserDoc()['template_notes'] as Doc).data).map((note, i) => ({
                     description: ':' + StrCast(note.title),
                     event: undoable((args: { x: number; y: number }) => {
                         const textDoc = Docs.Create.TextDocument('', {
@@ -1583,10 +1578,10 @@ export namespace DocUtils {
     }
     export function findTemplate(templateName: string, type: string, signature: string) {
         let docLayoutTemplate: Opt<Doc>;
-        const iconViews = DocListCast(Cast(Doc.UserDoc()['template-icons'], Doc, null)?.data);
-        const templBtns = DocListCast(Cast(Doc.UserDoc()['template-buttons'], Doc, null)?.data);
-        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data);
-        const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template-clickFuncs'], Doc, null)?.data);
+        const iconViews = DocListCast(Cast(Doc.UserDoc()['template_icons'], Doc, null)?.data);
+        const templBtns = DocListCast(Cast(Doc.UserDoc()['template_buttons'], Doc, null)?.data);
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
+        const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
         const allTemplates = iconViews
             .concat(templBtns)
             .concat(noteTypes)
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 592f6d836..11a8dcaf6 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -97,7 +97,7 @@ export class CurrentUserUtils {
 
         const reqdOpts:DocumentOptions = {
             title: "Experimental Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true,
-            _stayInCollection: true, _layout_hideContextMenu: true, _forceActive: true, isSystem: true,
+            _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _forceActive: true, isSystem: true,
             _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
@@ -123,7 +123,7 @@ export class CurrentUserUtils {
     }
 
     /// Initializes templates for editing click funcs of a document 
-    static setupClickEditorTemplates(doc: Doc, field = "template-clickFuncs") {
+    static setupClickEditorTemplates(doc: Doc, field = "template_clickFuncs") {
         const tempClicks = DocCast(doc[field]);
         const reqdClickOpts:DocumentOptions = { _width: 300, _height:200, isSystem: true};
         const reqdTempOpts:{opts:DocumentOptions, script: string}[] = [
@@ -144,7 +144,7 @@ export class CurrentUserUtils {
     }
 
     /// Initializes templates that can be applied to notes
-    static setupNoteTemplates(doc: Doc, field="template-notes") {
+    static setupNoteTemplates(doc: Doc, field="template_notes") {
         const tempNotes = DocCast(doc[field]);
         const reqdTempOpts:DocumentOptions[] = [
             { noteType: "Note", backgroundColor: "yellow", icon: "sticky-note"},
@@ -174,7 +174,7 @@ export class CurrentUserUtils {
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
-    static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
+    static setupDefaultIconTemplates(doc: Doc, field="template_icons") {
         const reqdOpts = { title: "icon templates", _height: 75, isSystem: true };
         const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
@@ -279,11 +279,11 @@ export class CurrentUserUtils {
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts),   opts: { _width: 300, _height: 300 }},
             {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeViewHideUnrendered: true}},
-            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, targetDropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
+            {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: "embed" as dropActionType, treeViewHideTitle: true, _layout_fitWidth:true, _chromeHidden: true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, 
                                                                                                 treeViewHasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
-                                                                                                allowOverlayDrop: true, treeViewType: TreeViewType.outline, 
+                                                                                                dropAction:'move', treeViewType: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
@@ -322,17 +322,16 @@ export class CurrentUserUtils {
         const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
             const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, 
-                _width: 35, _height: 35, _layout_hideContextMenu: true, _stayInCollection: true, 
+                _width: 35, _height: 35, _layout_hideContextMenu: true, _dragOnlyWithinContainer: true, 
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
-                _removeDropProperties: new List<string>(["_stayInCollection"]),
                 };
             return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
         });
 
         const reqdOpts:DocumentOptions = {
-            title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
+            title: "Basic Item Creators", _layout_showTitle: "title", _xMargin: 0, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, isSystem: true,
             _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-             childDropAction: 'embed' 
+             childDragAction: 'embed' 
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
         return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
@@ -357,7 +356,7 @@ 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:Doc) => {doc.isSystem = true; return doc;})(new Doc()), {isSystem:true});
+        DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
     }
 
     /// Initializes the left sidebar menu buttons and the panels they open up
@@ -367,15 +366,14 @@ export class CurrentUserUtils {
         const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => {
             const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
             const reqdBtnOpts:DocumentOptions = {
-                title, icon, target, btnType: ButtonType.MenuButton, isSystem: true, dontUndo: true, dontRegisterView: true,
-                _width: 60, _height: 60,  _stayInCollection: true, _layout_hideContextMenu: true,
-                _removeDropProperties: new List<string>(["_stayInCollection"]),
+                title, icon, target, 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);
         });
 
         const reqdStackOpts:DocumentOptions ={
-            title: "menuItemPanel", childDropAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
+            title: "menuItemPanel", childDragAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
             _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true,
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
@@ -458,7 +456,7 @@ export class CurrentUserUtils {
     /// Search option on the left side button panel
     static setupSearcher(doc: Doc, field:string) {
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
-                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDropAction: "embed", 
+                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", isSystem: true, childDragAction: "embed", 
                 _lockedPosition: true, _type_collection: CollectionViewType.Schema  });
     }
 
@@ -469,7 +467,7 @@ export class CurrentUserUtils {
         const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined);
         const reqdToolOps:DocumentOptions = {
             title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0",
-            _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _chromeHidden: true, 
+            _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _chromeHidden: true, 
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
     }
@@ -481,7 +479,7 @@ export class CurrentUserUtils {
         const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`;
         const newDashboard = `createNewDashboard()`;
 
-        const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _layout_hideContextMenu: true,
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
              title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", isSystem: true };
         const reqdBtnScript = {onClick: newDashboard,}
         const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.layout_headerButton), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
@@ -495,8 +493,8 @@ export class CurrentUserUtils {
         const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, treeViewFreezeChildren: "remove|add", treeViewHideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
-            targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
-            layout_headerButton: newDashboardButton, childDropAction: "embed",
+            dropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, isSystem: true,  treeViewTruncateTitleWidth: 350, ignoreClick: true, 
+            layout_headerButton: newDashboardButton, childDragAction: "embed",
             _layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
             contextMenuLabels:new List<string>(contextMenuLabels),
             contextMenuIcons:new List<string>(contextMenuIcons),
@@ -520,20 +518,20 @@ 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", _stayInCollection: true, isSystem: true, isFolder: true });
+        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 = {
-            _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, _width: 30, _height: 30,
+            _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
         };
         const newFolderScript = { onClick: newFolder};
         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, targetDropAction: "proto", isSystem: true,
+            title: "My Documents", layout_headerButton: newFolderButton, treeViewHideTitle: true, dropAction: "proto", isSystem: true,
             isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, layout_boxShadow: "0 0", childDontRegisterViews: true, 
-            treeViewTruncateTitleWidth: 350, ignoreClick: true, childDropAction: "embed",
+            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."
@@ -549,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, childDropAction: "embed", isSystem: true,
-            treeViewTruncateTitleWidth: 350, ignoreClick: true,  layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
+            title: "My Recently Closed", childHideLinkButton: true, treeViewHideTitle: true, childDragAction: "embed", 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"]),
             layout_explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
@@ -558,7 +556,7 @@ export class CurrentUserUtils {
         const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
 
         const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
-        const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _layout_hideContextMenu: true, 
+        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,
             toolTip: "Empty recently closed",};
         DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
@@ -575,7 +573,7 @@ export class CurrentUserUtils {
     static setupUserDocView(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = {
             _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
-            layout_boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, isSystem: true,
+            layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: "same", ignoreClick: true, isSystem: true,
             treeViewHideTitle: true, treeViewTruncateTitleWidth: 350
         };
         if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
@@ -590,7 +588,7 @@ export class CurrentUserUtils {
 
     static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
         btnType: ButtonType.ToolButton, _forceActive: true, _layout_hideContextMenu: true, 
-        _removeDropProperties: new List<string>([ "_layout_hideContextMenu", "stayInCollection"]), 
+        _dropPropertiesToRemove: new List<string>([ "_layout_hideContextMenu"]), 
         _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, isSystem: true, ...opts,
     })
 
@@ -608,9 +606,9 @@ export class CurrentUserUtils {
             { scripts: { },                       opts: { title: "linker",            layout: "<LinkingUI>",          toolTip: "link started"}},
             { scripts: { },                       opts: { title: "currently playing", layout: "<CurrentlyPlayingUI>", toolTip: "currently playing media"}},
         ];
-        const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts));
+        const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts:DocumentOptions = {
-            title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDropAction: 'embed',
+            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 });
@@ -716,12 +714,11 @@ export class CurrentUserUtils {
         const reqdOpts:DocumentOptions = {
             ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
             backgroundColor: params.backgroundColor ??"transparent", ///  a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
-            color: Colors.WHITE, isSystem: true, //dontUndo: true, 
+            color: Colors.WHITE, isSystem: true,
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
             toolType: params.toolType, expertMode: params.expertMode,
-            _stayInCollection: true, _layout_hideContextMenu: true, _lockedPosition: true,
-            _removeDropProperties: new List<string>([ "_stayInCollection"]),
+            _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, _lockedPosition: true,
         };
         const reqdFuncs:{[key:string]:any} = {
             ...params.funcs,
@@ -732,14 +729,14 @@ export class CurrentUserUtils {
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", dontUndo:true, flexGap: 0, childDropAction: 'embed', childDontRegisterViews: true,  linearView_IsExpanded: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsExpanded"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true,  linearView_IsExpanded: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
             if (!params.subMenu) {
                 return this.setupContextMenuButton(params, menuBtnDoc);
             } else {
-                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, dontUndo: true,
+                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsExpanded"]),
                     childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
                     linearView_SubMenu: true, linearView_Expandable: true, };
                 const items = params.subMenu?.map(sub =>
@@ -786,13 +783,13 @@ export class CurrentUserUtils {
         const sharedDocOpts:DocumentOptions = {
             title: "My Shared Docs",
             userColor: "rgb(202, 202, 202)",
-            isFolder:true,
+            isFolder:true, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
             childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
             childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
             childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             childContextMenuIcons: new List<string>(["user-plus",]),
             "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
-            childDropAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
+            childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
             // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
             _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
             layout_explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
@@ -805,14 +802,14 @@ export class CurrentUserUtils {
     static setupImportSidebar(doc: Doc, field:string) {   
         const reqdOpts:DocumentOptions = {
             title: "My Imports", _forceActive: true, ignoreClick: true, _layout_showTitle: "title", 
-            _stayInCollection: true, _layout_hideContextMenu: true, childLimitHeight: 0,
-            childDropAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
+            _dragOnlyWithinContainer: true, _layout_hideContextMenu: true, childLimitHeight: 0,
+            childDragAction: "copy", _layout_autoHeight: true, _yMargin: 50, _gridGap: 15, layout_boxShadow: "0 0", _lockedPosition: true, isSystem: true, _chromeHidden: true,
             dontRegisterView: true, layout_explainer: "This is where documents that are Imported into Dash will go."
         };
         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, _stayInCollection: true, _layout_hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
+            _width: 30, _height: 30, _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;
@@ -864,7 +861,7 @@ export class CurrentUserUtils {
         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
         DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", isSystem: true }); // drop down panel at top of dashboard for stashing documents
+        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
         
         Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs)
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 87833d322..85101fcab 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -15,42 +15,36 @@ 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 removeDropProperties
+export type dropActionType = 'embed' | 'copy' | 'move' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
 
 /**
  * Initialize drag
  * @param _reference: The HTMLElement that is being dragged
  * @param docFunc: The Dash document being moved
- * @param moveFunc: The function called when the document is moved
- * @param dropAction: What to do with the document when it is dropped
- * @param dragStarted: Method to call when the drag is started
  */
-export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () => Doc | Promise<Doc | undefined> | undefined, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, dragStarted?: () => void) {
-    const onRowMove = async (e: PointerEvent) => {
+export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () => Doc | undefined) {
+    const onRowMove = (e: PointerEvent) => {
         e.stopPropagation();
         e.preventDefault();
 
         document.removeEventListener('pointermove', onRowMove);
         document.removeEventListener('pointerup', onRowUp);
-        const doc = await docFunc();
+        const doc = docFunc();
         if (doc) {
             const dragData = new DragManager.DocumentDragData([doc]);
-            dragData.dropAction = dropAction;
-            dragData.moveDocument = moveFunc;
             DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y);
-            dragStarted?.();
         }
     };
     const onRowUp = (): void => {
         document.removeEventListener('pointermove', onRowMove);
         document.removeEventListener('pointerup', onRowUp);
     };
-    const onItemDown = async (e: React.PointerEvent) => {
+    const onItemDown = (e: React.PointerEvent) => {
         if (e.button === 0) {
             e.stopPropagation();
             if (e.shiftKey) {
                 e.persist();
-                const dragDoc = await docFunc();
+                const dragDoc = docFunc();
                 dragDoc && DragManager.StartWindowDrag?.(e, [dragDoc]);
             } else {
                 document.addEventListener('pointermove', onRowMove);
@@ -132,7 +126,7 @@ export namespace DragManager {
         userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
         defaultDropAction?: dropActionType; // an optionally specified default drop action when there is no user drop actionl - this will be honored if there is no user drop action
         dropAction: dropActionType; // a drop action request by the initiating code.  the actual drop action may be different -- eg, if the request is 'embed', but the document is dropped within the same collection, the drop action will be switched to 'move'
-        removeDropProperties?: string[];
+        dropPropertiesToRemove?: string[];
         moveDocument?: MoveFunction;
         removeDocument?: RemoveFunction;
         isDocDecorationMove?: boolean; // Flags that Document decorations are used to drag document which allows suppression of onDragStart scripts
@@ -189,7 +183,7 @@ export namespace DragManager {
         const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
         const preDropHandler = (e: Event) => {
             const de = (e as CustomEvent<DropEvent>).detail;
-            (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc?.targetDropAction) as dropActionType);
+            (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc?.dropAction) as dropActionType);
         };
         element.addEventListener('dashOnDrop', handler);
         doc && element.addEventListener('dashPreDrop', preDropHandler);
@@ -230,11 +224,13 @@ export namespace DragManager {
                     )
                 ).filter(d => d);
                 !['same', 'proto'].includes(docDragData.dropAction as any) &&
-                    docDragData.droppedDocuments.forEach((drop: Doc, i: number) => {
-                        const dragProps = StrListCast(dragData.draggedDocuments[i].removeDropProperties);
-                        const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps));
-                        remProps.map(prop => (drop[prop] = undefined));
-                    });
+                    docDragData.droppedDocuments
+                        // .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any))
+                        .forEach((drop: Doc, i: number) => {
+                            const dragProps = StrListCast(dragData.draggedDocuments[i].dropPropertiesToRemove);
+                            const remProps = (dragData?.dropPropertiesToRemove || []).concat(Array.from(dragProps));
+                            [...remProps, 'dropPropertiesToRemove'].map(prop => (drop[prop] = undefined));
+                        });
             }
             return e;
         };
@@ -586,6 +582,7 @@ export namespace DragManager {
 
     async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number; y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) {
         const dropArgs = {
+            cancelable: true, // allows preventDefault() to be called to cancel the drop
             bubbles: true,
             detail: {
                 ...pos,
@@ -598,8 +595,9 @@ export namespace DragManager {
             },
         };
         target.dispatchEvent(new CustomEvent<DropEvent>('dashPreDrop', dropArgs));
+        UndoManager.StartTempBatch(); // run drag/drop in temp batch in case drop is not allowed (so we can undo any intermediate changes)
         await finishDrag?.(complete);
-        target.dispatchEvent(new CustomEvent<DropEvent>('dashOnDrop', dropArgs));
+        UndoManager.EndTempBatch(target.dispatchEvent(new CustomEvent<DropEvent>('dashOnDrop', dropArgs))); // event return val is true unless the event preventDefault() is called
         options?.dragComplete?.(complete);
         endDrag?.();
     }
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index f46ea393a..47997cc5c 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -57,11 +57,11 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
         let dbox = doc;
         // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant
         if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes('FontIconBox')) {
-            if (data.removeDropProperties || dbox.removeDropProperties) {
+            if (data.dropPropertiesToRemove || dbox.dropPropertiesToRemove) {
                 //dbox = Doc.MakeEmbedding(doc);  // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon
                 dbox = Doc.MakeEmbedding(dbox);
-                const dragProps = Cast(dbox.removeDropProperties, listSpec('string'), []);
-                const remProps = (data.removeDropProperties || []).concat(Array.from(dragProps));
+                const dragProps = Cast(dbox.dropPropertiesToRemove, listSpec('string'), []);
+                const remProps = (data.dropPropertiesToRemove || []).concat(Array.from(dragProps));
                 remProps.map(prop => (dbox[prop] = undefined));
             }
         } else if (!doc.onDragStart && !doc.isButtonBar) {
@@ -81,7 +81,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
                 icon: layoutDoc.isTemplateDoc ? 'font' : 'bolt',
             });
             dbox.dragFactory = layoutDoc;
-            dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
+            dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined;
             dbox.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory)');
         } else if (doc.isButtonBar) {
             dbox.ignoreClick = true;
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 6fef9d660..2379cb2ab 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -91,7 +91,7 @@ export namespace UndoManager {
     let currentBatch: UndoBatch | undefined;
     export let batchCounter = observable.box(0);
     let undoing = false;
-    let tempEvents: UndoEvent[] | undefined = undefined;
+    export let tempEvents: UndoEvent[] | undefined = undefined;
 
     export function AddEvent(event: UndoEvent, value?: any): void {
         if (currentBatch && batchCounter.get() && !undoing) {
@@ -187,15 +187,11 @@ export namespace UndoManager {
         return false;
     });
 
-    export function RunInTempBatch<T>(fn: () => T) {
+    export function StartTempBatch() {
         tempEvents = [];
-        try {
-            const success = runInAction(fn);
-            if (!success) UndoManager.UndoTempBatch();
-            return success;
-        } finally {
-            tempEvents = undefined;
-        }
+    }
+    export function EndTempBatch<T>(success: boolean) {
+        UndoManager.UndoTempBatch(success);
     }
     //TODO Make this return the return value
     export function RunInBatch<T>(fn: () => T, batchName: string) {
@@ -206,10 +202,11 @@ export namespace UndoManager {
             batch.end();
         }
     }
-    export const UndoTempBatch = action(() => {
-        if (tempEvents) {
+    export const UndoTempBatch = action((success: any) => {
+        if (tempEvents && !success) {
             undoing = true;
             for (let i = tempEvents.length - 1; i >= 0; i--) {
+                currentBatch?.includes(tempEvents[i]) && currentBatch.splice(currentBatch.indexOf(tempEvents[i]));
                 tempEvents[i].undo();
             }
             undoing = false;
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 50cf2226e..6aae302ac 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -389,7 +389,7 @@ export class DashboardView extends React.Component {
             _forceActive: true,
             _width: 30,
             _height: 30,
-            _stayInCollection: true,
+            _dragOnlyWithinContainer: true,
             _layout_hideContextMenu: true,
             title: 'New trail',
             toolTip: 'Create new trail',
@@ -412,7 +412,7 @@ export class DashboardView extends React.Component {
             _layout_fitWidth: true,
             _gridGap: 5,
             _forceActive: true,
-            childDropAction: 'embed',
+            childDragAction: 'embed',
             treeViewTruncateTitleWidth: 150,
             ignoreClick: true,
             layout_headerButton: myTrailsBtn,
@@ -421,7 +421,7 @@ export class DashboardView extends React.Component {
             _lockedPosition: true,
             layout_boxShadow: '0 0',
             childDontRegisterViews: true,
-            targetDropAction: 'same',
+            dropAction: 'same',
             isSystem: true,
             layout_explainer: 'All of the trails that you have created will appear here.',
         };
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index db24229dc..0709d6cb9 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -172,8 +172,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 return true;
             }
             const first = doc instanceof Doc ? doc : doc[0];
-            if (!first?._stayInCollection && addDocument !== returnFalse) {
-                return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey));
+            if (!first?._dragOnlyWithinContainer && addDocument !== returnFalse) {
+                return this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey);
             }
             return false;
         };
@@ -213,7 +213,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                         .map(doc => {
                             // only make a pushpin if we have acl's to edit the document
                             //DocUtils.LeavePushpin(doc);
-                            doc._stayInCollection = undefined;
+                            doc._dragOnlyWithinContainer = undefined;
                             doc.embedContainer = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 528b82e3e..41ce08d3c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -603,7 +603,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     let actualdH = Math.max(docheight + dH * scale, 20);
                     let dX = !dWin ? 0 : scale * refCent[0] * (1 - (1 + dWin / refWidth));
                     let dY = !dHin ? 0 : scale * refCent[1] * (1 - (1 + dHin / refHeight));
-                    const preserveNativeDim = doc._nativeHeightUnfrozen === false && doc._nativeDimModifiable === false;
+                    const preserveNativeDim = !doc._nativeHeightUnfrozen && !doc._nativeDimModifiable;
                     const fixedAspect = nwidth && nheight && (!doc._layout_fitWidth || preserveNativeDim || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable);
                     if (fixedAspect) {
                         if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) {
@@ -636,7 +636,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 }
                             }
                             if (!modifyNativeDim) {
-                                actualdH = (nheight / nwidth) * docwidth; //, actualdH);
+                                actualdH = (nheight / nwidth) * NumCast(doc._width); //, actualdH);
                             }
                             doc._height = actualdH;
                         }
@@ -756,7 +756,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             hideDecorations ||
             seldocview.props.hideOpenButton ||
             seldocview.rootDoc.layout_hideOpenButton ||
-            SelectionManager.Views().some(docView => docView.rootDoc._stayInCollection || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) ||
+            SelectionManager.Views().some(docView => docView.rootDoc._dragOnlyWithinContainer || docView.rootDoc.isGroup || docView.rootDoc.layout_hideOpenButton) ||
             this._isRounding ||
             this._isRotating;
         const hideDeleteButton =
@@ -767,7 +767,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
                 const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit;
-                return (docView.rootDoc.stayInCollection && !docView.rootDoc._isTimelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin;
             });
 
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 0951bff22..54ad519de 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -4,8 +4,7 @@ import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../fields/Doc';
 import { InkData, InkTool } from '../../fields/InkField';
-import { ScriptField } from '../../fields/ScriptField';
-import { Cast, FieldValue, NumCast } from '../../fields/Types';
+import { NumCast } from '../../fields/Types';
 import MobileInkOverlay from '../../mobile/MobileInkOverlay';
 import { GestureUtils } from '../../pen-gestures/GestureUtils';
 import { MobileInkOverlayContent } from '../../server/Message';
@@ -35,7 +34,6 @@ import { InkTranscription } from './InkTranscription';
 import { checkInksToGroup } from './nodes/button/FontIconBox';
 import { DocumentView } from './nodes/DocumentView';
 import { RadialMenu } from './nodes/RadialMenu';
-import HorizontalPalette from './Palette';
 import { Touchable } from './Touchable';
 import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu';
 
@@ -79,7 +77,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
     private _overlayRef = React.createRef<HTMLDivElement>();
     private _d1: Doc | undefined;
     private _inkToTextDoc: Doc | undefined;
-    private _thumbDoc: Doc | undefined;
     private thumbIdentifier?: number;
     private pointerIdentifier?: number;
     private _hands: Map<number, React.Touch[]> = new Map<number, React.Touch[]>();
@@ -93,78 +90,12 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
         GestureOverlay.Instances.push(this);
     }
 
-    static setupThumbButtons(doc: Doc) {
-        const docProtoData: { title: string; icon: string; drag?: string; toolType?: string; ignoreClick?: boolean; pointerDown?: string; pointerUp?: string; clipboard?: Doc; backgroundColor?: string; dragFactory?: Doc }[] = [
-            { title: 'use pen', icon: 'pen-nib', pointerUp: 'resetPen()', pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: 'blue' },
-            { title: 'use highlighter', icon: 'highlighter', pointerUp: 'resetPen()', pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: 'yellow' },
-            {
-                title: 'notepad',
-                icon: 'clipboard',
-                pointerUp: 'GestureOverlay.Instance.closeFloatingDoc()',
-                pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)',
-                clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, isSystem: true }),
-                backgroundColor: 'orange',
-            },
-            { title: 'interpret text', icon: 'font', toolType: 'inktotext', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'orange' },
-            { title: 'ignore gestures', icon: 'signature', toolType: 'ignoregesture', pointerUp: "setToolglass('none')", pointerDown: 'setToolglass(self.toolType)', backgroundColor: 'green' },
-        ];
-        return docProtoData.map(data =>
-            Docs.Create.FontIconDocument({
-                _nativeWidth: 10,
-                _nativeHeight: 10,
-                _width: 10,
-                _height: 10,
-                title: data.title,
-                icon: data.icon,
-                toolType: data.toolType,
-                _dropAction: data.pointerDown ? 'copy' : undefined,
-                ignoreClick: data.ignoreClick,
-                onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
-                clipboard: data.clipboard,
-                onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
-                onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
-                backgroundColor: data.backgroundColor,
-                dragFactory: data.dragFactory,
-                isSystem: true,
-            })
-        );
-    }
-
-    static setupThumbDoc(userDoc: Doc) {
-        if (!userDoc.thumbDoc) {
-            const thumbDoc = Docs.Create.LinearDocument(GestureOverlay.setupThumbButtons(userDoc), {
-                _width: 100,
-                _height: 50,
-                ignoreClick: true,
-                _lockedPosition: true,
-                title: 'buttons',
-                _layout_autoHeight: true,
-                _yMargin: 5,
-                linearView_IsExpanded: true,
-                backgroundColor: 'white',
-                isSystem: true,
-            });
-            thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
-                _width: 300,
-                _height: 25,
-                _layout_autoHeight: true,
-                linearView_IsExpanded: true,
-                flexDirection: 'column',
-                isSystem: true,
-            });
-            userDoc.thumbDoc = thumbDoc;
-        }
-        return Cast(userDoc.thumbDoc, Doc);
-    }
-
     componentWillUnmount() {
         GestureOverlay.Instances.splice(GestureOverlay.Instances.indexOf(this), 1);
         GestureOverlay.Instance = GestureOverlay.Instances.lastElement();
     }
     componentDidMount = () => {
         GestureOverlay.Instance = this;
-        this._thumbDoc = FieldValue(Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc));
-        this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc));
     };
 
     // TODO: nda - add dragging groups with one finger drag and have to click into group to scroll within the group
@@ -395,24 +326,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
 
         this.thumbIdentifier = thumb?.identifier;
         this._hands.set(thumb.identifier, fingers);
-        const others = fingers.filter(f => f !== thumb);
-        const minX = Math.min(...others.map(f => f.clientX));
-        const minY = Math.min(...others.map(f => f.clientY));
-
-        // load up the palette collection around the thumb
-        const thumbDoc = await Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc);
-        if (thumbDoc) {
-            runInAction(() => {
-                RadialMenu.Instance._display = false;
-                this._inkToTextDoc = FieldValue(Cast(thumbDoc.inkToTextDoc, Doc));
-                this._thumbDoc = thumbDoc;
-                this._thumbX = thumb.clientX;
-                this._thumbY = thumb.clientY;
-                this._menuX = thumb.clientX + 50;
-                this._menuY = thumb.clientY;
-                this._palette = <HorizontalPalette key="palette" x={minX} y={minY} thumb={[thumb.clientX, thumb.clientY]} thumbDoc={thumbDoc} />;
-            });
-        }
 
         this.removeMoveListeners();
         document.removeEventListener('touchmove', this.handleHandMove);
@@ -462,11 +375,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
                         if (Math.abs(pt.clientY - this._thumbY) > 10 * window.devicePixelRatio) {
                             this._selectedIndex = Math.min(Math.max(-1, -Math.ceil((pt.clientY - this._thumbY) / (10 * window.devicePixelRatio)) - 1), this._possibilities.length - 1);
                         }
-                    } else if (this._thumbDoc) {
-                        if (Math.abs(pt.clientX - this._thumbX) > 15 * window.devicePixelRatio) {
-                            this._thumbDoc.selectedIndex = Math.max(-1, NumCast(this._thumbDoc.selectedIndex) - Math.sign(pt.clientX - this._thumbX));
-                            this._thumbX = pt.clientX;
-                        }
                     }
                 }
             }
@@ -485,7 +393,6 @@ export class GestureOverlay extends Touchable<GestureOverlayProps> {
             if (this.thumbIdentifier) this._hands.delete(this.thumbIdentifier);
             this._palette = undefined;
             this.thumbIdentifier = undefined;
-            this._thumbDoc = undefined;
 
             // this chunk of code is for handling the ink to text toolglass
             let scriptWorked = false;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1a9d15c1a..dbe8fb608 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -30,6 +30,7 @@ import { ColorScheme, SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { Transform } from '../util/Transform';
+import { UndoManager } from '../util/UndoManager';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
@@ -571,7 +572,7 @@ export class MainView extends React.Component {
 
     @action
     createNewFolder = async () => {
-        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
+        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true });
         Doc.AddDocToList(Doc.MyFilesystem, 'data', folder);
     };
 
@@ -582,6 +583,8 @@ export class MainView extends React.Component {
     waitForDoubleClick = () => (this._exploreMode ? 'never' : undefined);
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
     mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1);
+    addHeaderDoc = (doc: Doc | Doc[], annotationKey?: string) => (doc instanceof Doc ? [doc] : doc).reduce((done, doc) => Doc.AddDocToList(this.headerBarDoc, 'data', doc), true);
+    removeHeaderDoc = (doc: Doc | Doc[], annotationKey?: string) => (doc instanceof Doc ? [doc] : doc).reduce((done, doc) => Doc.RemoveDocFromList(this.headerBarDoc, 'data', doc), true);
     @computed get headerBarDocView() {
         return (
             <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
@@ -589,18 +592,19 @@ export class MainView extends React.Component {
                     key="headerBarDoc"
                     Document={this.headerBarDoc}
                     DataDoc={undefined}
-                    addDocument={undefined}
                     addDocTab={DocumentViewInternal.addDocTabFunc}
                     pinToPres={emptyFunction}
                     docViewPath={returnEmptyDoclist}
                     styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
-                    removeDocument={returnFalse}
+                    addDocument={this.addHeaderDoc}
+                    removeDocument={this.removeHeaderDoc}
                     fitContentsToBox={returnTrue}
                     isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
                     isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                     ScreenToLocalTransform={this.headerBarScreenXf}
                     childHideResizeHandles={returnTrue}
+                    childDragAction="move"
                     dontRegisterView={true}
                     hideResizeHandles={true}
                     PanelWidth={this.headerBarDocWidth}
@@ -865,7 +869,7 @@ export class MainView extends React.Component {
         this._leftMenuFlyoutWidth = 0;
     });
 
-    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true);
+    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && !doc.dragOnlyWithinContainer && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true);
     moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
     addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.MyDockedBtns, 'data', doc), true);
 
diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss
deleted file mode 100644
index 0ec879288..000000000
--- a/src/client/views/Palette.scss
+++ /dev/null
@@ -1,30 +0,0 @@
-.palette-container {
-    .palette-thumb {
-        touch-action: pan-x;
-        position: absolute;
-        height: 70px;
-        overflow: hidden;
-
-        .palette-thumbContent {
-            transition: transform .3s;
-            width: max-content;
-            overflow: hidden;
-
-            .collectionView {
-                overflow: visible;
-
-                .collectionLinearView-outer {
-                    overflow: visible;
-                }
-            }
-        }
-
-        .palette-cover {
-            width: 50px;
-            height: 50px;
-            position: absolute;
-            bottom: 0;
-            border: 1px solid black;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
deleted file mode 100644
index 749eb08a2..000000000
--- a/src/client/views/Palette.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { IReactionDisposer, observable, reaction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../fields/Doc';
-import { NumCast } from '../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, emptyPath } from '../../Utils';
-import { Transform } from '../util/Transform';
-import { DocumentView } from './nodes/DocumentView';
-import './Palette.scss';
-
-export interface PaletteProps {
-    x: number;
-    y: number;
-    thumb: number[];
-    thumbDoc: Doc;
-}
-
-@observer
-export default class Palette extends React.Component<PaletteProps> {
-    private _selectedDisposer?: IReactionDisposer;
-    @observable private _selectedIndex: number = 0;
-
-    componentDidMount = () => {
-        this._selectedDisposer = reaction(
-            () => NumCast(this.props.thumbDoc.selectedIndex),
-            i => (this._selectedIndex = i),
-            { fireImmediately: true }
-        );
-    };
-
-    componentWillUnmount = () => {
-        this._selectedDisposer?.();
-    };
-
-    render() {
-        return (
-            <div className="palette-container" style={{ transform: `translate(${this.props.x}px, ${this.props.y}px)` }}>
-                <div className="palette-thumb" style={{ transform: `translate(${this.props.thumb[0] - this.props.x}px, ${this.props.thumb[1] - this.props.y}px)` }}>
-                    <div className="palette-thumbContent" style={{ transform: `translate(-${this._selectedIndex * 50 + 10}px, 0px)` }}>
-                        <DocumentView
-                            Document={this.props.thumbDoc}
-                            DataDoc={undefined}
-                            addDocument={undefined}
-                            addDocTab={returnFalse}
-                            rootSelected={returnTrue}
-                            pinToPres={emptyFunction}
-                            removeDocument={undefined}
-                            ScreenToLocalTransform={Transform.Identity}
-                            PanelWidth={() => window.screen.width}
-                            PanelHeight={() => window.screen.height}
-                            renderDepth={0}
-                            isDocumentActive={returnTrue}
-                            isContentActive={emptyFunction}
-                            focus={emptyFunction}
-                            docViewPath={returnEmptyDoclist}
-                            styleProvider={returnEmptyString}
-                            whenChildContentsActiveChanged={emptyFunction}
-                            bringToFront={emptyFunction}
-                            childFilters={returnEmptyFilter}
-                            childFiltersByRanges={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
-                        />
-                        <div className="palette-cover" style={{ transform: `translate(${Math.max(0, this._selectedIndex) * 50.75 + 23}px, 0px)` }}></div>
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 1f2e21dd5..09aac053a 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -287,7 +287,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         pinToPres={emptyFunction}
                         bringToFront={returnFalse}
                         dontRegisterView={true}
-                        dropAction={undefined}
                     />
                 </div>
             );
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 2cca2c5a9..330ccc583 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -265,6 +265,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         }
         case StyleProp.PointerEvents:
             const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString;
+            if (StrCast(doc?.pointerEvents) && !props?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc
             if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.();
             if (MainView.Instance._exploreMode || doc?.layout_unrendered) return isInk ? 'visiblePainted' : 'all';
             if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents);
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 62f47ba71..5c2ab3f70 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -97,8 +97,8 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         TraceMobx();
         const firstDoc = this.props.docViews[0].props.Document;
         const templateName = StrCast(firstDoc.layout_fieldKey, 'layout').replace('layout_', '');
-        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data);
-        const addedTypes = DocListCast(Cast(Doc.UserDoc()['template-clickFuncs'], Doc, null)?.data);
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
+        const addedTypes = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
         const templateMenu: Array<JSX.Element> = [];
         this.props.templates?.forEach((checked, template) => templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key={'default'} name={'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
@@ -122,7 +122,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                     rootSelected={returnFalse}
                     onCheckedClick={this.scriptField}
                     onChildClick={this.scriptField}
-                    dropAction={undefined}
                     isAnyChildContentActive={returnFalse}
                     isContentActive={returnTrue}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 0eb61a0b2..ea02bcd4c 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Opt } from '../../../fields/Doc';
 import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { returnFalse, returnZero, StopEvent } from '../../../Utils';
+import { emptyFunction, returnFalse, returnZero, StopEvent } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
@@ -60,6 +60,8 @@ export class CollectionCarouselView extends CollectionSubView() {
                         NativeHeight={returnZero}
                         onDoubleClick={this.onContentDoubleClick}
                         onClick={this.onContentClick}
+                        isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.props.isContentActive}
+                        isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction}
                         hideCaptions={show_captions ? true : false}
                         renderDepth={this.props.renderDepth + 1}
                         LayoutTemplate={this.props.childLayoutTemplate}
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 675f23970..9a3ab8074 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -10,7 +10,7 @@ import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, returnFalse, returnZero, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -250,7 +250,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
                 rootSelected={this.rootSelected}
                 layout_showTitle={this.props.childlayout_showTitle}
-                dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+                dragAction={StrCast(this.layoutDoc.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={noteTakingDocTransform}
@@ -619,7 +619,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     }
 
     @computed get backgroundEvents() {
-        return SnappingManager.GetIsDragging();
+        return this.props.isContentActive() === false ? 'none' : undefined;
     }
 
     observer: any;
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 57d9bbb49..15570b195 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -52,18 +52,21 @@ export class CollectionPileView extends CollectionSubView() {
     @computed get toggleIcon() {
         return ScriptField.MakeScript('documentView.iconify()', { documentView: 'any' });
     }
+    @computed get contentEvents() {
+        const isStarburst = this.layoutEngine() === computeStarburstLayout.name;
+        return this.props.isContentActive() && isStarburst ? undefined : 'none';
+    }
 
     // returns the contents of the pileup in a CollectionFreeFormView
     @computed get contents() {
-        const isStarburst = this.layoutEngine() === computeStarburstLayout.name;
         return (
-            <div className="collectionPileView-innards" style={{ pointerEvents: isStarburst || SnappingManager.GetIsDragging() ? undefined : 'none' }}>
+            <div className="collectionPileView-innards" style={{ pointerEvents: this.contentEvents }}>
                 <CollectionFreeFormView
-                    {...this.props}
+                    {...this.props} //
                     childContentsActive={returnFalse}
                     layoutEngine={this.layoutEngine}
                     addDocument={this.addPileDoc}
-                    childCanEmbedOnDrag={true}
+                    childDragAction="move"
                     childClickScript={this.toggleIcon}
                     moveDocument={this.removePileDoc}
                 />
diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index a55b70e22..a19d8e696 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -104,7 +104,7 @@
 
         .collectionStackedTimeline-left-resizer,
         .collectionStackedTimeline-resizer {
-            background: $medium-gray;
+            background: $dark-gray;
             position: absolute;
             top: 0;
             height: 100%;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 9d5cb257a..e835fa659 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -119,8 +119,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         // onClick play scripts
         CollectionStackedTimeline.RangeScript =
             CollectionStackedTimeline.RangeScript ||
-            ScriptField.MakeFunction(`setTimeout(() => scriptContext.clickAnchor(this, clientX))`, {
-                // setTimeout is a hack to run script in its own properly named undo group (instead of being part of the generic onClick)
+            ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
                 self: Doc.name,
                 scriptContext: 'any',
                 clientX: 'number',
@@ -406,7 +405,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
                 _label_minFontSize: 12,
                 _label_maxFontSize: 24,
-                _stayInCollection: true,
+                _dragOnlyWithinContainer: true,
                 backgroundColor: 'rgba(128, 128, 128, 0.5)',
                 layout_hideLinkButton: true,
                 onClick: FollowLinkScript(),
@@ -528,6 +527,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         );
     }
 
+    @computed get timelineEvents() {
+        return this.props.isContentActive() ? 'all' : this.props.isContentActive() === false ? 'none' : undefined;
+    }
     render() {
         const overlaps: {
             anchorStartTime: number;
@@ -540,7 +542,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         }));
         const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
         return this.clipDuration === 0 ? null : (
-            <div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}>
+            <div ref={this.createDashEventsTarget} style={{ pointerEvents: this.timelineEvents }}>
                 <div
                     className="collectionStackedTimeline-timelineContainer"
                     style={{ width: this.props.PanelWidth(), cursor: SnappingManager.GetIsDragging() ? 'grab' : '' }}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 3c0d8cbc3..8eed4f716 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -17,7 +17,7 @@ import { CollectionViewType } from '../../documents/DocumentTypes';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch } from '../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
@@ -295,7 +295,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             return this.addDocument?.(newDoc);
         }
     };
-    isContentActive = () => (this.props.isSelected() || this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined);
+    isContentActive = () => (this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined);
 
     @observable _renderCount = 5;
     isChildContentActive = () =>
@@ -341,7 +341,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to.
                 rootSelected={this.rootSelected}
                 layout_showTitle={this.props.childlayout_showTitle}
-                dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+                dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={stackedDocTransform}
@@ -402,11 +402,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     // This following three functions must be from the view Mehek showed
     columnDividerDown = (e: React.PointerEvent) => {
         runInAction(() => (this._cursor = 'grabbing'));
+        const batch = UndoManager.StartBatch('stacking width');
         setupMoveUpEvents(
             this,
             e,
             this.onDividerMove,
-            action(() => (this._cursor = 'grab')),
+            action(() => {
+                this._cursor = 'grab';
+                batch.end();
+            }),
             emptyFunction
         );
     };
@@ -700,7 +704,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     }
 
     @computed get backgroundEvents() {
-        return SnappingManager.GetIsDragging();
+        return this.props.isContentActive() === false ? 'none' : undefined;
     }
     observer: any;
     render() {
@@ -710,7 +714,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             SetValue: this.addGroup,
             contents: '+ ADD A GROUP',
         };
-        const buttonMenu = this.rootDoc.buttonMenu;
+        const buttonMenu = this.rootDoc.layout_headerButton;
         const noviceExplainer = this.rootDoc.layout_explainer;
         return (
             <>
@@ -727,7 +731,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                         style={{
                             overflowY: this.isContentActive() ? 'auto' : 'hidden',
                             background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
-                            pointerEvents: (this.props.pointerEvents?.() as any) ?? (this.backgroundEvents ? 'all' : undefined),
+                            pointerEvents: (this.props.pointerEvents?.() as any) ?? this.backgroundEvents,
                         }}
                         onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
                         onDrop={this.onExternalDrop.bind(this)}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d1b7f6ff6..539dde7e0 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -187,7 +187,7 @@ export function CollectionSubView<X>(moreProps?: X) {
 
         protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
             if (de.complete.docDragData) {
-                // if targetDropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
+                // if dropAction is, say 'embed', but we're just dragging within a collection, we want to ignore the targetAction.
                 // otherwise, the targetAction should become the actual action (which can still be overridden by the userDropAction -eg, shift/ctrl keys)
                 if (targetAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) {
                     de.complete.docDragData.dropAction = targetAction;
@@ -212,16 +212,16 @@ export function CollectionSubView<X>(moreProps?: X) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
-                        const canAdd = this.props.Document._type_collection === CollectionViewType.Pile || de.embedKey || this.props.Document.allowOverlayDrop || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
-                        added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
+                        const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc);
+                        added = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
                     } else {
-                        ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
+                        ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                         added = addedDocs.length ? this.addDocument(addedDocs) : true;
                     }
                     added && e.stopPropagation();
                     return added;
                 } else {
-                    ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
+                    ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
                     added = this.addDocument(docDragData.droppedDocuments);
                 }
                 !added && alert('You cannot perform this move');
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 60e6815e5..192d48c64 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -34,8 +34,6 @@ export class CollectionTimeView extends CollectionSubView() {
 
     async componentDidMount() {
         this.props.setContentView?.(this);
-        //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), "");
-        ///const childText = "const embedding = getEmbedding(self); switchView(embedding, detailView); embedding.dropAction='embed'; useRightSplit(embedding, shiftKey); ";
         runInAction(() => {
             this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name });
             this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ed1e0c067..7598bb753 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -3,7 +3,6 @@ import { observer } from 'mobx-react';
 import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
 import { DocData, Height, Width } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
-import { InkTool } from '../../../fields/InkField';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -90,8 +89,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
     @observable _isAnyChildContentActive = false;
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
-    isContentActive = (outsideReaction?: boolean) =>
-        Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction) ? true : false;
+    isContentActive = (outsideReaction?: boolean) => (this._isAnyChildContentActive ? true : this.props.isContentActive() ? true : false);
 
     componentWillUnmount() {
         this._isDisposing = true;
@@ -261,7 +259,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _renderCount = 1;
     @computed get treeViewElements() {
         TraceMobx();
-        const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
+        const dragAction = StrCast(this.doc.childDragAction) as dropActionType;
         const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
         const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false;
         if (this._renderCount < this.treeChildren.length) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20))));
@@ -276,7 +274,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             addDoc,
             this.remove,
             moveDoc,
-            dropAction,
+            dragAction,
             this.props.addDocTab,
             this.props.styleProvider,
             this.screenToLocalTransform,
@@ -383,7 +381,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _headerHeight = 0;
     @computed get content() {
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
-        const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined);
+        const pointerEvents = () => (this.props.isContentActive() === false ? 'none' : undefined);
         const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
         return (
             <div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}>
@@ -440,12 +438,12 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                         setContentView={emptyFunction}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
-                        pointerEvents={SnappingManager.GetIsDragging() ? returnAll : returnNone}
+                        pointerEvents={this.props.isContentActive() && SnappingManager.GetIsDragging() ? returnAll : returnNone}
                         isAnnotationOverlay={true}
                         isAnnotationOverlayScrollable={true}
                         childDocumentsActive={this.props.isDocumentActive}
                         fieldKey={this.props.fieldKey + '_annotations'}
-                        dropAction={'move'}
+                        dropAction="move"
                         select={emptyFunction}
                         addDocument={this.addAnnotationDocument}
                         removeDocument={this.remAnnotationDocument}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7913d3188..c33519afb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -9,6 +9,7 @@ import { TraceMobx } from '../../../fields/util';
 import { returnEmptyString } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { CollectionViewType } from '../../documents/DocumentTypes';
+import { dropActionType } from '../../util/DragManager';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ContextMenu } from '../ContextMenu';
@@ -55,7 +56,7 @@ interface CollectionViewProps_ extends FieldViewProps {
     childHideDecorationTitle?: () => boolean;
     childHideResizeHandles?: () => boolean;
     childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
-    childCanEmbedOnDrag?: boolean;
+    childDragAction?: dropActionType;
     childXPadding?: number;
     childYPadding?: number;
     childLayoutString?: string;
@@ -220,7 +221,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
     childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
-    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() || this.isAnyChildContentActive();
+    isContentActive = (outsideReaction?: boolean) => (this.isAnyChildContentActive() ? true : this.props.isContentActive());
 
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 3b99339af..97d4d989b 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -583,7 +583,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     isContentActive={emptyFunction}
                     isAnyChildContentActive={returnFalse}
                     select={emptyFunction}
-                    dropAction={undefined}
                     isSelected={returnFalse}
                     dontRegisterView={true}
                     fieldKey={Doc.LayoutFieldKey(this.props.document)}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6bc30c451..91ae2b3cc 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -21,7 +21,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
 import { EditableView } from '../EditableView';
 import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
 import { DocumentView, DocumentViewInternal, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
@@ -45,7 +45,7 @@ export interface TreeViewProps {
     dataDoc?: Doc;
     treeViewParent: Doc;
     renderDepth: number;
-    dropAction: dropActionType;
+    dragAction: dropActionType;
     addDocTab: (doc: Doc, where: OpenWhere) => boolean;
     panelWidth: () => number;
     panelHeight: () => number;
@@ -276,7 +276,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc);
-        if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
+        if (e.buttons === 1 && SnappingManager.GetIsDragging() && this.props.isContentActive()) {
             this._header.current!.className = 'treeView-header';
             document.removeEventListener('pointermove', this.onDragMove, true);
             document.removeEventListener('pointerup', this.onDragUp, true);
@@ -341,7 +341,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     };
 
     makeFolder = () => {
-        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
+        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true });
         TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
         return this.props.addDocument(folder);
     };
@@ -391,7 +391,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeViewFreezeChildren).includes('add')) || forceAdd;
         if (canAdd) {
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
-            const res = UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false));
+            const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
             this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false);
             return res;
         }
@@ -460,7 +460,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     addDoc,
                     remDoc,
                     moveDoc,
-                    this.props.dropAction,
+                    this.props.dragAction,
                     this.props.addDocTab,
                     this.titleStyleProvider,
                     this.props.ScreenToLocalTransform,
@@ -585,12 +585,12 @@ export class TreeView extends React.Component<TreeViewProps> {
                             downY = e.clientY;
                             e.stopPropagation();
                         }}
-                        onClick={e => {
+                        onClick={undoable(e => {
                             if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
                                 !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
                                 e.stopPropagation();
                             }
-                        }}>
+                        }, 'sort order')}>
                         {!docs
                             ? null
                             : TreeView.GetChildElements(
@@ -604,7 +604,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                                   addDoc,
                                   remDoc,
                                   moveDoc,
-                                  StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType,
+                                  StrCast(this.doc.childDragAction, this.props.dragAction) as dropActionType,
                                   this.props.addDocTab,
                                   this.titleStyleProvider,
                                   this.props.ScreenToLocalTransform,
@@ -904,7 +904,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideDecorationTitle={this.props.treeView.outlineMode}
                 hideResizeHandles={this.props.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
-                enableDragWhenActive={true}
                 onClickScriptDisable="never" // tree docViews have a script to show fields, etc.
                 docViewPath={this.props.treeView.props.docViewPath}
                 treeViewDoc={this.props.treeView.props.Document}
@@ -914,7 +913,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 pinToPres={emptyFunction}
                 onClick={this.onChildClick}
                 onDoubleClick={this.onChildDoubleClick}
-                dropAction={this.props.dropAction}
+                dragAction={this.props.dragAction}
                 moveDocument={this.move}
                 removeDocument={this.props.removeDoc}
                 ScreenToLocalTransform={this.getTransform}
@@ -950,7 +949,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined,
                         textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined,
                         outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined,
-                        pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+                        pointerEvents: !this.props.isContentActive() ? 'none' : undefined,
                     }}>
                     {view}
                 </div>
@@ -1132,7 +1131,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         add: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean,
         remove: undefined | ((doc: Doc | Doc[]) => boolean),
         move: DragManager.MoveFunction,
-        dropAction: dropActionType,
+        dragAction: dropActionType,
         addDocTab: (doc: Doc, where: OpenWhere) => boolean,
         styleProvider: undefined | StyleProviderFunc,
         screenToLocalXf: () => Transform,
@@ -1218,7 +1217,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         panelHeight={rowHeight}
                         dontRegisterView={dontRegisterView}
                         moveDocument={move}
-                        dropAction={dropAction}
+                        dragAction={dragAction}
                         addDocTab={addDocTab}
                         ScreenToLocalTransform={screenToLocalXf}
                         isContentActive={isContentActive}
@@ -1239,6 +1238,6 @@ export class TreeView extends React.Component<TreeViewProps> {
 
 ScriptingGlobals.add(function TreeView_addNewFolder() {
     TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
-    const opts = { title: 'Untitled folder', _stayInCollection: true, isFolder: true };
+    const opts = { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true };
     return Doc.AddDocToList(Doc.MyFilesystem, 'data', Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
 });
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 36472dd2e..d7e073c5f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1240,7 +1240,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         };
     };
 
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isContentActive = () => this.props.isContentActive();
 
     @undoBatch
     @action
@@ -1267,7 +1267,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
         const pointerEvents = DocumentDecorations.Instance.Interacting
             ? 'none'
-            : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.());
+            : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
         return pointerEvents;
     };
     getChildDocView(entry: PoolData) {
@@ -1298,7 +1298,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 childFiltersByRanges={this.childDocRangeFilters}
                 searchFilterDocs={this.searchFilterDocs}
                 isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
-                isContentActive={this.props.childContentsActive ?? emptyFunction}
+                isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction}
                 focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
                 addDocTab={this.addDocTab}
                 addDocument={this.props.addDocument}
@@ -1308,10 +1308,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
                 docViewPath={this.props.docViewPath}
                 styleProvider={this.getClusterColor}
-                canEmbedOnDrag={this.props.childCanEmbedOnDrag}
+                dragAction={(this.rootDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
                 dataProvider={this.childDataProvider}
                 sizeProvider={this.childSizeProvider}
-                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
                 bringToFront={this.bringToFront}
                 layout_showTitle={this.props.childlayout_showTitle}
                 dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
@@ -1987,7 +1986,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 onDragOver={e => e.preventDefault()}
                 onContextMenu={this.onContextMenu}
                 style={{
-                    pointerEvents: SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement()) ? 'all' : (this.props.pointerEvents?.() as any),
+                    pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : (this.props.pointerEvents?.() as any),
                     textAlign: this.isAnnotationOverlay ? 'initial' : undefined,
                     transform: `scale(${this.nativeDimScaling || 1})`,
                     width: `${100 / (this.nativeDimScaling || 1)}%`,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 77cb7f88c..898452ccb 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -386,7 +386,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection._height = this.Bounds.height;
         newCollection._isGroup = makeGroup;
         newCollection.forceActive = makeGroup;
-        newCollection.enableDragWhenActive = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
         newCollection.layout_fitWidth = true;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index e8ae88ae5..cd8b7a0cc 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -368,7 +368,7 @@ export class CollectionGridView extends CollectionSubView() {
             <div
                 className="collectionGridView-contents"
                 ref={this.createDashEventsTarget}
-                style={{ pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined }}
+                style={{ pointerEvents: !this.props.isContentActive() ? 'none' : undefined }}
                 onContextMenu={this.onContextMenu}
                 onPointerDown={this.onPointerDown}
                 onDrop={this.onExternalDrop}>
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 65578f214..8f90e4444 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Opt } from '../../../../fields/Doc';
@@ -50,34 +50,10 @@ export class CollectionLinearView extends CollectionSubView() {
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
-
-        this._selectedDisposer = reaction(
-            () => NumCast(this.layoutDoc.selectedIndex),
-            i =>
-                runInAction(() => {
-                    this._selectedIndex = i;
-                    let selected: any = undefined;
-                    this.childLayoutPairs.map(async (pair, ind) => {
-                        const isSelected = this._selectedIndex === ind;
-                        if (isSelected) {
-                            selected = pair;
-                        } else {
-                            ScriptCast(DocCast(pair.layout.proto)?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
-                        }
-                    });
-                    if (selected && selected.layout) {
-                        ScriptCast(DocCast(selected.layout.proto)?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
-                    }
-                }),
-            { fireImmediately: true }
-        );
     }
     protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
-        //used for stacking and masonry view
-        this._dropDisposer && this._dropDisposer();
-        if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
-        }
+        this._dropDisposer?.();
+        if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
     };
 
     dimension = () => NumCast(this.rootDoc._height);
@@ -89,12 +65,8 @@ export class CollectionLinearView extends CollectionSubView() {
 
     @action
     exitLongLinks = () => {
-        if (DocumentLinksButton.StartLink) {
-            if (DocumentLinksButton.StartLink.Document) {
-                action((e: React.PointerEvent<HTMLDivElement>) => {
-                    Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
-                });
-            }
+        if (DocumentLinksButton.StartLink?.Document) {
+            action((e: React.PointerEvent<HTMLDivElement>) => Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc));
         }
         DocumentLinksButton.StartLink = undefined;
         DocumentLinksButton.StartLinkView = undefined;
@@ -201,7 +173,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     moveDocument={this.props.moveDocument}
                     addDocTab={this.props.addDocTab}
                     pinToPres={emptyFunction}
-                    dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+                    dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
                     rootSelected={this.props.isSelected}
                     removeDocument={this.props.removeDocument}
                     ScreenToLocalTransform={docXf}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 34fa0343d..32b42223b 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -5,7 +5,6 @@ import { Doc, DocListCast } from '../../../../fields/Doc';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -194,11 +193,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         let dropInd = -1;
         if (de.complete.docDragData && this._mainCont) {
             let curInd = -1;
-            de.complete.docDragData?.droppedDocuments.forEach(
-                action((d: Doc) => {
-                    curInd = this.childDocs.indexOf(d);
-                })
-            );
+            de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d)));
             Array.from(this._mainCont.children).forEach((child, index) => {
                 const brect = child.getBoundingClientRect();
                 if (brect.x < de.x && brect.x + brect.width > de.x) {
@@ -234,7 +229,14 @@ export class CollectionMulticolumnView extends CollectionSubView() {
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
-    isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false);
+    isChildContentActive = () => {
+        const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive;
+        return this.props.isContentActive?.() === false || childDocsActive === false
+            ? false //
+            : this.props.isDocumentActive?.() && childDocsActive
+            ? true
+            : undefined;
+    };
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return (
             <DocumentView
@@ -248,7 +250,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 PanelWidth={width}
                 PanelHeight={height}
                 rootSelected={this.rootSelected}
-                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+                dragAction={(this.props.Document.childDragAction ?? this.props.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 suppressSetHeight={true}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index bab77ed48..fa3a9f352 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -5,7 +5,6 @@ import { Doc, DocListCast } from '../../../../fields/Doc';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
 import { DocumentView } from '../../nodes/DocumentView';
@@ -194,11 +193,7 @@ export class CollectionMultirowView extends CollectionSubView() {
         let dropInd = -1;
         if (de.complete.docDragData && this._mainCont) {
             let curInd = -1;
-            de.complete.docDragData?.droppedDocuments.forEach(
-                action((d: Doc) => {
-                    curInd = this.childDocs.indexOf(d);
-                })
-            );
+            de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d)));
             Array.from(this._mainCont.children).forEach((child, index) => {
                 const brect = child.getBoundingClientRect();
                 if (brect.y < de.y && brect.y + brect.height > de.y) {
@@ -234,7 +229,14 @@ export class CollectionMultirowView extends CollectionSubView() {
     onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
-    isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false);
+    isChildContentActive = () => {
+        const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive;
+        return this.props.isContentActive?.() === false || childDocsActive === false
+            ? false //
+            : this.props.isDocumentActive?.() && childDocsActive
+            ? true
+            : undefined;
+    };
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return (
             <DocumentView
@@ -248,7 +250,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                 PanelWidth={width}
                 PanelHeight={height}
                 rootSelected={this.rootSelected}
-                dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+                dropAction={StrCast(this.rootDoc.childDragAction) as dropActionType}
                 onClick={this.onChildClickHandler}
                 onDoubleClick={this.onChildDoubleClickHandler}
                 ScreenToLocalTransform={dxf}
@@ -257,6 +259,7 @@ export class CollectionMultirowView extends CollectionSubView() {
                 hideResizeHandles={this.props.childHideResizeHandles?.()}
                 hideDecorationTitle={this.props.childHideDecorationTitle?.()}
                 fitContentsToBox={this.props.fitContentsToBox}
+                dragAction={this.props.childDragAction}
                 focus={this.props.focus}
                 childFilters={this.childDocFilters}
                 childFiltersByRanges={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 15424de98..06b227b4b 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -952,7 +952,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 styleProvider={DefaultStyleProvider}
                                 waitForDoubleClickToClick={returnNever}
                                 defaultDoubleClick={returnIgnore}
-                                enableDragWhenActive={true}
+                                dragAction="move"
                                 onClickScriptDisable="always"
                                 focus={this.props.schema.focusDocument}
                                 childFilters={this.props.schema.childDocFilters}
@@ -970,7 +970,6 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
                                 hideLinkAnchors={true}
                                 layout_fitWidth={returnTrue}
                                 scriptContext={this}
-                                canEmbedOnDrag={true}
                             />
                         </div>
                     );
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 4f3503751..e8e606030 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -53,13 +53,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     onPointerEnter = (e: any) => {
-        if (!SnappingManager.GetIsDragging()) return;
-        document.removeEventListener('pointermove', this.onPointerMove);
-        document.addEventListener('pointermove', this.onPointerMove);
+        if (SnappingManager.GetIsDragging() && this.props.isContentActive()) {
+            document.removeEventListener('pointermove', this.onPointerMove);
+            document.addEventListener('pointermove', this.onPointerMove);
+        }
     };
 
     onPointerMove = (e: any) => {
-        if (!SnappingManager.GetIsDragging()) return;
         const dragIsRow = DragManager.docsBeingDragged.some(doc => doc.embedContainer === this.schemaDoc); //  this.schemaView?._selectedDocs.has(doc) ?? false;
 
         if (this._ref && dragIsRow) {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 97264508c..a958f53ea 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -76,7 +76,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'embed',
+            dragAction: 'move',
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 5af05e491..9ceac3b8c 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -78,7 +78,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             e,
             e => {
                 const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'embed');
-                dragData.removeDropProperties = ['hidden'];
+                dragData.dropPropertiesToRemove = ['hidden'];
                 DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
                 return true;
             },
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 664a0fa4f..745efea08 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -70,7 +70,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         linkSearch={true}
                         linkCreated={this.props.linkCreated}
                         fieldKey="data"
-                        dropAction="move"
                         isSelected={returnTrue}
                         isContentActive={returnTrue}
                         select={returnTrue}
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index 660045a6f..a12f1c12b 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -93,4 +93,4 @@
             display: flex;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index e6a4635c0..d747c4527 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -2,11 +2,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../../fields/Doc';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils';
-import { Docs } from '../../documents/Documents';
+import { Docs, DocUtils } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
-import { SnappingManager } from '../../util/SnappingManager';
 import { undoBatch } from '../../util/UndoManager';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { StyleProp } from '../StyleProvider';
@@ -32,15 +31,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
     get clipWidthKey() {
         return '_' + this.props.fieldKey + '_clipWidth';
     }
-
     componentDidMount() {
         this.props.setContentView?.(this);
     }
-
     protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
         this._disposers[disposerId]?.();
         if (ele) {
-            // create disposers identified by disposerId to remove drag & drop listeners
             this._disposers[disposerId] = DragManager.MakeDropTarget(ele, (e, dropEvent) => this.internalDrop(e, dropEvent, fieldKey), this.layoutDoc);
         }
     };
@@ -50,29 +46,40 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         if (dropEvent.complete.docDragData) {
             event.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
             const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments;
-            droppedDocs.lastElement().embedContainer = this.dataDoc;
-            this.dataDoc[fieldKey] = droppedDocs.lastElement();
+            const res = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.rootDoc, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey));
+            res && (droppedDocs.lastElement().embedContainer = this.dataDoc);
+            return res;
         }
     };
 
     private registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => {
-        e.button !== 2 &&
+        if (e.button !== 2) {
             setupMoveUpEvents(
                 this,
                 e,
                 this.onPointerMove,
                 emptyFunction,
+                action((e, doubleTap) => {
+                    if (doubleTap) {
+                        this._isAnyChildContentActive = true;
+                        if (!this.dataDoc[this.fieldKey + '_1']) this.dataDoc[this.fieldKey + '_1'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
+                        if (!this.dataDoc[this.fieldKey + '_2']) this.dataDoc[this.fieldKey + '_2'] = DocUtils.copyDragFactory(Doc.UserDoc().emptyNote as Doc);
+                    }
+                }),
+                false,
+                undefined,
                 action(() => {
-                    // on click, animate slider movement to the targetWidth
+                    if (this._isAnyChildContentActive) return;
                     this._animating = 'all 200ms';
+                    // on click, animate slider movement to the targetWidth
                     this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this.props.PanelWidth();
                     setTimeout(
                         action(() => (this._animating = '')),
                         200
                     );
-                }),
-                false
+                })
             );
+        }
     };
 
     @action
@@ -86,7 +93,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const anchor = Docs.Create.ConfigDocument({
-            title: 'ImgAnchor:' + this.rootDoc.title,
+            title: 'CompareAnchor:' + this.rootDoc.title,
             // set presentation timing properties for restoring view
             presTransition: 1000,
             annotationOn: this.rootDoc,
@@ -105,12 +112,29 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         e.stopPropagation; // prevent click event action (slider movement) in registerSliding
         delete this.dataDoc[fieldKey];
     };
+    moveDoc = (doc: Doc, addDocument: (document: Doc | Doc[]) => boolean, which: string) => this.remDoc(doc, which) && addDocument(doc);
+    addDoc = (doc: Doc, which: string) => {
+        this.dataDoc[which] = doc;
+        return true;
+    };
+    remDoc = (doc: Doc, which: string) => {
+        if (this.dataDoc[which] === doc) {
+            this.dataDoc[which] = undefined;
+            return true;
+        }
+        return false;
+    };
+
+    whenChildContentsActiveChanged = action((isActive: boolean) => (this._isAnyChildContentActive = isActive));
 
     docStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         if (property === StyleProp.PointerEvents) return 'none';
         return this.props.styleProvider?.(doc, props, property);
     };
-
+    moveDoc1 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_1'), true);
+    moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true);
+    remDoc1 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true);
+    remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true);
     render() {
         const clearButton = (which: string) => {
             return (
@@ -118,33 +142,35 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                     className={`clear-button ${which}`}
                     onPointerDown={e => e.stopPropagation()} // prevent triggering slider movement in registerSliding
                     onClick={e => this.clearDoc(e, which)}>
-                    <FontAwesomeIcon className={`clear-button ${which}`} icon={'times'} size="sm" />
+                    <FontAwesomeIcon className={`clear-button ${which}`} icon="times" size="sm" />
                 </div>
             );
         };
         const displayDoc = (which: string) => {
-            const whichDoc = Cast(this.dataDoc[which], Doc, null);
-            // if (whichDoc?.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null);
-            const targetDoc = Cast(whichDoc?.annotationOn, Doc, null) ?? whichDoc;
-            return whichDoc ? (
+            const whichDoc = DocCast(this.dataDoc[which]);
+            const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc);
+            return targetDoc ? (
                 <>
                     <DocumentView
                         {...this.props}
+                        Document={targetDoc}
+                        DataDoc={undefined}
+                        moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2}
+                        removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
-                        isContentActive={returnFalse}
+                        isContentActive={emptyFunction}
                         isDocumentActive={returnFalse}
-                        styleProvider={this.docStyleProvider}
-                        Document={targetDoc}
-                        DataDoc={undefined}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                        styleProvider={this._isAnyChildContentActive ? this.props.styleProvider : this.docStyleProvider}
                         hideLinkButton={true}
-                        pointerEvents={returnNone}
+                        pointerEvents={this._isAnyChildContentActive ? undefined : returnNone}
                     />
                     {clearButton(which)}
                 </> // placeholder image if doc is missing
             ) : (
                 <div className="placeholder">
-                    <FontAwesomeIcon className="upload-icon" icon={'cloud-upload-alt'} size="lg" />
+                    <FontAwesomeIcon className="upload-icon" icon="cloud-upload-alt" size="lg" />
                 </div>
             );
         };
@@ -157,7 +183,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         };
 
         return (
-            <div className={`comparisonBox${this.props.isContentActive() || SnappingManager.GetIsDragging() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
+            <div className={`comparisonBox${this.props.isContentActive() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
                 {displayBox(`${this.fieldKey}_2`, 1, this.props.PanelWidth() - 3)}
                 <div className="clip-div" style={{ width: this.clipWidth + '%', transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, 'gray') }}>
                     {displayBox(`${this.fieldKey}_1`, 0, 0)}
@@ -169,7 +195,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
                         left: `calc(${this.clipWidth + '%'} - 0.5px)`,
                         cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined,
                     }}
-                    onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
+                    onPointerDown={e => !this._isAnyChildContentActive && this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
                 >
                     <div className="slide-handle" />
                 </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 23148ed34..5470a72f5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -14,7 +14,7 @@ import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, return18, returnEmptyString, returnFalse, returnTrue, returnVal, returnZero, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { DocServer } from '../../DocServer';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -47,6 +47,7 @@ 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';
@@ -163,6 +164,7 @@ export interface DocumentViewSharedProps {
     docViewPath: () => DocumentView[];
     childHideDecorationTitle?: () => boolean;
     childHideResizeHandles?: () => boolean;
+    childDragAction?: dropActionType; // allows child documents to be dragged out of collection without holding the embedKey or dragging the doc decorations title bar.
     dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
     styleProvider: Opt<StyleProviderFunc>;
     setTitleFocus?: () => void;
@@ -182,7 +184,7 @@ export interface DocumentViewSharedProps {
     pinToPres: (document: Doc, pinProps: PinProps) => void;
     ScreenToLocalTransform: () => Transform;
     bringToFront: (doc: Doc, sendToBack?: boolean) => void;
-    canEmbedOnDrag?: boolean;
+    dragAction?: dropActionType;
     treeViewDoc?: Doc;
     xPadding?: number;
     yPadding?: number;
@@ -194,7 +196,6 @@ export interface DocumentViewSharedProps {
     forceAutoHeight?: boolean;
     disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
     onClickScriptDisable?: 'never' | 'always'; // undefined = only when selected
-    enableDragWhenActive?: boolean;
     waitForDoubleClickToClick?: () => 'never' | 'always' | undefined;
     defaultDoubleClick?: () => 'default' | 'ignore' | undefined;
     pointerEvents?: () => Opt<string>;
@@ -314,7 +315,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get titleHeight() {
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
     }
-    @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
+    get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
         return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
     }
     @computed get finalLayoutKey() {
@@ -355,11 +356,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     componentDidMount() {
         this.setupHandlers();
     }
-
+    preDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
+        if (de.complete.docDragData && this.isContentActive()) {
+            targetAction && (de.complete.docDragData.dropAction = targetAction);
+            e.stopPropagation();
+        }
+    };
     setupHandlers() {
         this.cleanupHandlers(false);
         if (this._mainCont.current) {
-            this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document);
+            this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document, this.preDropFunc);
             this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this));
             this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this));
         }
@@ -387,7 +393,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             dragData.treeViewDoc = this.props.treeViewDoc;
             dragData.removeDocument = this.props.removeDocument;
             dragData.moveDocument = this.props.moveDocument;
-            dragData.canEmbed = this.props.canEmbedOnDrag;
+            dragData.canEmbed = this.rootDoc.dragAction ?? this.props.dragAction ? true : false;
             const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
             ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
             DragManager.StartDocumentDrag(
@@ -478,7 +484,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                             : '';
                         DocumentViewInternal.addDocTabFunc = oldFunc;
                     };
-                    clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title));
+                    clickFunc = () => UndoManager.RunInBatch(func, 'click ' + this.rootDoc.title);
                 } else {
                     // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplateForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
@@ -506,7 +512,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     onPointerDown = (e: React.PointerEvent): void => {
         this._longPressSelector = setTimeout(() => {
             if (DocumentView.LongPress) {
-                if (this.rootDoc.dontUndo) {
+                if (this.rootDoc.undoIgnoreFields) {
                     runInAction(() => (UndoStack.HideInline = !UndoStack.HideInline));
                 } else {
                     this.props.select(false);
@@ -535,7 +541,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._type_collection !== CollectionViewType.Docking) e.preventDefault();
 
                 // listen to move events if document content isn't active or document is draggable
-                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || this.props.enableDragWhenActive || this.rootDoc.enableDragWhenActive)) {
+                if (!this.layoutDoc._lockedPosition && (!this.isContentActive() || this.props.dragAction || this.rootDoc.dragAction)) {
                     document.addEventListener('pointermove', this.onPointerMove);
                 }
             }
@@ -549,7 +555,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         if (!Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
             this.cleanupPointerEvents();
-            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dropAction || this.props.dropAction || undefined) as dropActionType));
+            this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'embed') || ((this.Document.dragAction || this.props.dragAction || undefined) as dropActionType));
         }
     };
 
@@ -608,10 +614,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @undoBatch
     @action
     drop = (e: Event, de: DragManager.DropEvent) => {
-        if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return;
+        if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false;
         if (this.props.Document === Doc.ActiveDashboard) {
+            e.stopPropagation();
             alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
-            return;
+            e.preventDefault();
+            return true;
         }
         const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
         if (linkdrag) {
@@ -625,8 +633,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
                     de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
                 }
+                return true;
             }
         }
+        return false;
     };
 
     @undoBatch
@@ -851,7 +861,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     setHeight = (height: number) => (this.layoutDoc._height = height);
     setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
     isContentActive = (outsideReaction?: boolean): boolean | undefined => {
-        return this.props.isContentActive() === false
+        //  true  - if the document has been activated directly or indirectly (by having its children selected)
+        //  false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive
+        // undefined - it is not active, but it should be responsive to actions that might active it or its contents (eg clicking)
+        return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none' || (this.rootDoc.pointerEvents === 'none' && !StrCast(this.props.LayoutTemplateString).includes(KeyValueBox.name))
             ? false
             : Doc.ActiveTool !== InkTool.None ||
               SnappingManager.GetIsDragging() ||
@@ -871,17 +884,18 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
         const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc));
         return !this.props.LayoutTemplateString &&
-            !this.props.isSelected() &&
+            !this.isContentActive() &&
             LightboxView.LightboxDoc !== this.rootDoc &&
             this.thumb &&
             !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
-            ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking) &&
-            !this._componentView?.isAnyChildContentActive?.()
+            ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking)
             ? true
             : false;
     };
     childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
-    contentPointerEvents = () => (!this.disableClickScriptFunc && this.onClickHandler ? 'none' : this.pointerEvents);
+
+    /// disable pointer events on content when there's an enabled onClick script, or if contents are marked inactive
+    contentPointerEvents = () => ((!this.disableClickScriptFunc && this.onClickHandler) || this.isContentActive() === false ? 'none' : this.pointerEvents);
     @computed get contents() {
         TraceMobx();
         const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString;
@@ -889,7 +903,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             <div
                 className="documentView-contentsView"
                 style={{
-                    pointerEvents: (isInk ? 'none' : this.pointerEvents) ?? 'all',
+                    pointerEvents: (isInk ? 'none' : this.contentPointerEvents()) ?? 'all',
                     height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
                 }}>
                 {!this._retryThumb || !this.thumbShown() ? null : (
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 85dd779fc..4ebf22ddf 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -45,7 +45,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
 @observer
 export class FieldView extends React.Component<FieldViewProps> {
     public static LayoutString(fieldType: { name: string }, fieldStr: string) {
-        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={"data} />"
+        return `<${fieldType.name} {...props} fieldKey={'${fieldStr}'}/>`; //e.g., "<ImageBox {...props} fieldKey={'data'} />"
     }
 
     @computed
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 9acbee1e7..64d6814df 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -387,7 +387,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     ref={this._overlayIconRef}
                     onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => (this.rootDoc[`_${this.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined))}
                     style={{
-                        display: (SnappingManager.GetIsDragging() && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
+                        display: (this.props.isContentActive() !== false && DragManager.DocDragData?.canEmbed) || DocListCast(this.dataDoc[this.fieldKey + '-alternates']).length ? 'block' : 'none',
                         width: 'min(10%, 25px)',
                         height: 'min(10%, 25px)',
                         background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
@@ -505,7 +505,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     }
                 })}
                 style={{
-                    display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
+                    display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
                     width: this.props.PanelWidth() ? undefined : `100%`,
                     height: this.props.PanelWidth() ? undefined : `100%`,
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 5b6b0b5a7..a6712a3db 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -213,18 +213,18 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         document.addEventListener('pointerup', this.onDividerUp);
     };
 
-    getFieldView = async () => {
+    getFieldView = () => {
         const rows = this.rows.filter(row => row.isChecked);
         if (rows.length > 1) {
-            const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document.data).title}`, _chromeHidden: true });
+            const parent = Docs.Create.StackingDocument([], { _layout_autoHeight: true, _width: 300, title: `field views for ${DocCast(this.props.Document).title}`, _chromeHidden: true });
             for (const row of rows) {
-                const field = this.createFieldView(DocCast(this.props.Document.data), row);
+                const field = this.createFieldView(DocCast(this.props.Document), row);
                 field && Doc.AddDocToList(parent, 'data', field);
                 row.uncheck();
             }
             return parent;
         }
-        return rows.length ? this.createFieldView(DocCast(this.props.Document.data), rows.lastElement()) : undefined;
+        return rows.length ? this.createFieldView(DocCast(this.props.Document), rows.lastElement()) : undefined;
     };
 
     createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index d0db60b37..01acdccb7 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -67,7 +67,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             isSelected: returnFalse,
             setHeight: returnFalse,
             select: emptyFunction,
-            dropAction: 'embed',
             bringToFront: emptyFunction,
             renderDepth: 1,
             isContentActive: returnFalse,
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index e86b881a8..9bcd04cf5 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -47,7 +47,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (separation > 100) {
                 const dragData = new DragManager.DocumentDragData([this.rootDoc]);
                 dragData.dropAction = 'embed';
-                dragData.removeDropProperties = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
+                dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
                 DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
                 return true;
             } else {
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 93e54ffb7..de0b57fd7 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -512,9 +512,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this.addDocument(doc, annotationKey);
     };
 
-    pointerEvents = () => {
-        return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
-    };
+    pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');
+
     @computed get annotationLayer() {
         return (
             <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 72f37b62c..a9154c5bb 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -510,7 +510,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     };
 
     pointerEvents = () => {
-        return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
+        return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
     };
     @computed get annotationLayer() {
         return (
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 6d040ca1a..fd4c6366b 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -559,7 +559,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             </div>
         );
     }
-    isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected() || (this.props.renderDepth === 0 && LightboxView.IsLightboxDocView(this.props.docViewPath()));
     @computed get renderPdfView() {
         TraceMobx();
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
@@ -592,7 +591,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         pdf={this._pdf}
                         focus={this.focus}
                         url={this.pdfUrl!.url.pathname}
-                        isContentActive={this.isPdfContentActive}
                         anchorMenuClick={this.anchorMenuClick}
                         loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
                         setPdfViewer={this.setPdfViewer}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 16705a6b3..34a1229ba 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -918,7 +918,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 ScreenToLocalTransform={this.scrollXf}
                 NativeDimScaling={returnOne}
                 focus={this.focus}
-                dropAction="embed"
                 childFilters={childFilters}
                 select={emptyFunction}
                 isAnyChildContentActive={returnFalse}
@@ -1003,8 +1002,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         return this.props.styleProvider?.(doc, props, property);
     };
-    pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance?.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
-    annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None ? 'all' : 'none');
+    pointerEvents = () =>
+        !this._draggingSidebar && this.props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
+            ? 'all' //
+            : 'none';
+    annotationPointerEvents = () => (this.props.isContentActive() && (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
     render() {
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
@@ -1013,7 +1015,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             <div
                 className="webBox"
                 ref={this._mainCont}
-                style={{ pointerEvents: this.pointerEvents(), position: SnappingManager.GetIsDragging() ? 'absolute' : undefined, display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined }}>
+                style={{
+                    pointerEvents: this.pointerEvents(), //
+                    position: SnappingManager.GetIsDragging() ? 'absolute' : undefined,
+                    display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
+                }}>
                 <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor) }} />
                 <div
                     className="webBox-container"
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 079129dc9..2cd3c6cd6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -297,7 +297,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     };
 
     dispatchTransaction = (tx: Transaction) => {
-        if (this._editorView) {
+        if (this._editorView && (this._editorView as any).docView) {
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
 
@@ -2020,17 +2020,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     _oldWheel: any;
     render() {
         TraceMobx();
-        const active = this.props.isContentActive() || this.props.isSelected();
-        const selected = active;
+        const active = this.props.isContentActive();
         const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
         const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : '';
-        const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
-        if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
+        if (!active && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
         const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
-        const selPad = (selected && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
-        const selPaddingClass = selected && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : '';
+        const selPad = (active && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
+        const selPaddingClass = active && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : '';
         const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._headerHeight}px' >
         return styleFromLayoutString?.height === '0px' ? null : (
             <div
@@ -2051,7 +2049,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                               width: `${100 / scale}%`,
                               height: `${100 / scale}%`,
                           }),
-                    display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
+                    display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
                     transition: 'inherit',
                     // overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined,
                     color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
@@ -2066,7 +2064,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     style={{
                         overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
                         height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
-                        pointerEvents: interactive ? undefined : 'none',
+                        pointerEvents: Doc.ActiveTool === InkTool.None ? undefined : 'none',
                     }}
                     onContextMenu={this.specificContextMenu}
                     onKeyDown={this.onKeyDown}
@@ -2078,11 +2076,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     onPointerDown={this.onPointerDown}
                     onDoubleClick={this.onDoubleClick}>
                     <div
-                        className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
+                        className={`formattedTextBox-outer${active ? '-selected' : ''}`}
                         ref={this._scrollRef}
                         style={{
                             width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
-                            pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
                             overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
                         }}
                         onScroll={this.onScroll}
@@ -2096,7 +2093,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                                 paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
                                 paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
                                 paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
-                                pointerEvents: !active && !SnappingManager.GetIsDragging() ? (IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined) : undefined,
+                                // pointerEvents: !active && IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined,
                             }}
                         />
                     </div>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 4f7d2e1c1..ef1c3911c 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1029,9 +1029,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive(outsideReaction);
-    //.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
-
     /**
      * For sorting the array so that the order is maintained when it is dropped.
      */
@@ -2462,6 +2459,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 childIgnoreNativeSize={true}
                                 moveDocument={returnFalse}
                                 ignoreUnrendered={true}
+                                childDragAction="move"
                                 //childLayoutFitWidth={returnTrue}
                                 childOpacity={returnOne}
                                 childClickScript={PresBox.navigateToDocScript}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 34e069046..f31cf6147 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -192,13 +192,14 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const dragArray = this.presBoxView?._dragArray ?? [];
         const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []);
         if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
-        dragData.dropAction = 'move';
         dragData.treeViewDoc = this.presBox?._type_collection === CollectionViewType.Tree ? this.presBox : undefined; // this.props.DocumentView?.()?.props.treeViewDoc;
         dragData.moveDocument = this.props.moveDocument;
         const dragItem: HTMLElement[] = [];
+        const classesToRestore = new Map<HTMLElement, string>();
         if (dragArray.length === 1) {
             const doc = this._itemRef.current || dragArray[0];
             if (doc) {
+                classesToRestore.set(doc, doc.className);
                 doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
                 dragItem.push(doc);
             }
@@ -212,16 +213,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             dragItem.push(doc);
         }
 
-        // const dropEvent = () => runInAction(() => this._dragging = false);
         if (activeItem) {
+            runInAction(() => (this._dragging = true));
             DragManager.StartDocumentDrag(
                 dragItem.map(ele => ele),
                 dragData,
                 e.clientX,
                 e.clientY,
-                undefined
+                undefined,
+                action(() => {
+                    Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1]));
+                    this._dragging = false;
+                })
             );
-            // runInAction(() => this._dragging = true);
             return true;
         }
         return false;
@@ -536,7 +540,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             <div
                                 className="presItem-number"
                                 title="select without navigation"
-                                style={{ pointerEvents: this.presBoxView?.isContentActive() ? 'all' : undefined }}
                                 onPointerDown={e => {
                                     e.stopPropagation();
                                     if (this._itemRef.current && this._dragRef.current) {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index dd202418b..8f6b8cd41 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -483,7 +483,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
     };
 
-    pointerEvents = () => (this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
+    pointerEvents = () =>
+        this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown()
+            ? 'all' //
+            : 'none';
     @computed get annotationLayer() {
         return (
             <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
@@ -514,10 +517,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
     panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
     panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
-    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])];
+    opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])];
     childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
-            if (this.inlineTextAnnotations.includes(doc)) return 'none';
+            if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none';
             return 'all';
         }
         return this.props.styleProvider?.(doc, props, property);
@@ -536,8 +539,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 NativeWidth={returnZero}
                 NativeHeight={returnZero}
                 setContentView={emptyFunction} // override setContentView to do nothing
-                pointerEvents={SnappingManager.GetIsDragging() ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it.
-                childPointerEvents="all" // but freeform children need to get events to allow text editing, etc
+                pointerEvents={SnappingManager.GetIsDragging() && this.props.isContentActive() ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it.
+                childPointerEvents={this.props.isContentActive() !== false ? 'all' : 'none'} // but freeform children need to get events to allow text editing, etc
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
                 fieldKey={this.props.fieldKey + '_annotations'}
@@ -549,7 +552,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 ScreenToLocalTransform={this.overlayTransform}
                 isAnyChildContentActive={returnFalse}
                 isAnnotationOverlayScrollable={true}
-                dropAction="embed"
                 childFilters={childFilters}
                 select={emptyFunction}
                 bringToFront={emptyFunction}
@@ -558,14 +560,14 @@ export class PDFViewer extends React.Component<IViewerProps> {
         </div>
     );
     @computed get overlayTransparentAnnotations() {
-        return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length ? 'none' : undefined);
+        return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length && this.props.isContentActive() ? 'none' : undefined);
     }
     @computed get overlayOpaqueAnnotations() {
         return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined);
     }
     @computed get overlayLayer() {
         return (
-            <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none' }}>
+            <div style={{ pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : 'none' }}>
                 {this.overlayTransparentAnnotations}
                 {this.overlayOpaqueAnnotations}
             </div>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index c674a20d2..c9f7e4114 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -776,7 +776,7 @@ export namespace Doc {
                 linkMap.set(link[Id], await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks));
             }
         });
-        Doc.SetInPlace(copy, 'title', 'CLONE: ' + doc.title, true);
+        Doc.SetInPlace(copy, 'title', '>:' + doc.title, true);
         copy.cloneOf = doc;
         cloneMap.set(doc[Id], copy);
 
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 757d507be..76b287be7 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -95,12 +95,12 @@ export const documentSchema = createSchema({
     link_displayArrow: 'boolean', // toggles directed arrows
 
     // drag drop properties
-    _stayInCollection: 'boolean', // whether document can be dropped into a different collection
+    _dragOnlyWithinContainer: 'boolean', // whether document can be dropped into a different collection
     dragFactory: Doc, // the document that serves as the "template" for the onDragStart script.  ie, to drag out copies of the dragFactory document.
-    dropAction: 'string', // override specifying what should happen when this document is dropped (can be "embed", "copy", "move")
-    targetDropAction: 'string', // allows the target of a drop event to specify the dropAction ("embed", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
-    childDropAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
-    removeDropProperties: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
+    dropAction: 'string', // override specifying what should happen when something is dropped on this document (can be "embed", "copy", "move")
+    dragAction: 'string', // override specifying what should happen when this document s dragged (can be "embed", "copy", "move")
+    childDragAction: 'string', // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "embed" or "copy")
+    dropPropertiesToRemove: listSpec('string'), // properties that should be removed from the embed/copy/etc of this document when it is dropped
 });
 
 export const collectionSchema = createSchema({
diff --git a/src/fields/util.ts b/src/fields/util.ts
index f365adf4b..e3d1abc53 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -7,7 +7,7 @@ import { SerializationHelper } from '../client/util/SerializationHelper';
 import { UndoManager } from '../client/util/UndoManager';
 import { returnZero } from '../Utils';
 import CursorField from './CursorField';
-import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, updateCachedAcls } from './Doc';
+import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, StrListCast, updateCachedAcls } from './Doc';
 import { AclAdmin, AclEdit, AclPrivate, AclSelfEdit, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, Update, UpdatingFromServer, Width } from './DocSymbols';
 import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols';
 import { List } from './List';
@@ -86,7 +86,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
             DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
         }
         !receiver[Initializing] &&
-            !receiver.dontUndo &&
+            !StrListCast(receiver.undoIgnoreFields).includes(prop.toString()) &&
             (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) &&
             UndoManager.AddEvent(
                 {
-- 
cgit v1.2.3-70-g09d2


From e0757ecfe8666a13a38b1c396d4a9e5138b4a3bd Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Thu, 22 Jun 2023 11:26:24 -0400
Subject: double clicking works

---
 src/client/views/PropertiesView.tsx | 196 ++++++++++++++++++++++++++++++++----
 1 file changed, 176 insertions(+), 20 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index a0c45bc70..60e93cdfd 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1050,11 +1050,34 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get optionsSubMenu() {
+        let isDouble = false;
+
         return (
             <div className="propertiesView-settings" onPointerEnter={action(() => (this.inOptions = true))} onPointerLeave={action(() => (this.inOptions = false))}>
-                <div className="propertiesView-settings-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform = false,
-                this.openFields = false, this.openSharing = false, this.openFilters = false, this.openLayout = false))}
-                onClick={action(() => (this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}>
+                <div className="propertiesView-settings-title" onClick ={action(() => {
+                    if (!isDouble){
+                        this.openOptions = !this.openOptions
+                    } } )}
+                 onDoubleClick={action(() => {
+                    isDouble = true;
+
+                    this.openContexts = false;
+                    this.openLinks = false;
+                    this.openOptions = true;
+                    this.openTransform = false;
+                    this.openFields = false;
+                    this.openSharing = false;
+                    this.openLayout = false;
+                    this.openFilters = false;
+                    
+
+                    setTimeout(() => {
+                        isDouble = false;
+                    }, 300)
+                }
+                    
+    )}
+                 style={{ backgroundColor: this.openOptions ? 'black' : '' }}>
                     Options
                     <div className="propertiesView-settings-title-icon">
                         <FontAwesomeIcon icon={this.openOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1070,11 +1093,32 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get sharingSubMenu() {
+        let isDouble:boolean = false;
+
         return (
             <div className="propertiesView-sharing">
-                <div className="propertiesView-sharing-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform = false, 
-                this.openOptions = false, this.openFields = false, this.openFilters = false, this.openLayout))}
-                onClick={action(() => (this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}>
+                <div className="propertiesView-sharing-title" onClick ={action(() => {
+                    if (!isDouble){
+                        this.openSharing = !this.openSharing
+                    } } )}
+                 onDoubleClick={action(() => {
+                    isDouble = true;
+
+                    this.openContexts = false;
+                    this.openLinks = false;
+                    this.openOptions = false;
+                    this.openTransform = false;
+                    this.openFields = false;
+                    this.openSharing = true;
+                    this.openLayout = false;
+                    this.openFilters = false;
+                    
+
+                    setTimeout(() => {
+                        isDouble = false;
+                    }, 300)
+                })}
+                style={{ backgroundColor: this.openSharing ? 'black' : '' }}>
                     Sharing {'&'} Permissions
                     <div className="propertiesView-sharing-title-icon">
                         <FontAwesomeIcon icon={this.openSharing ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1127,11 +1171,34 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     };
 
     @computed get filtersSubMenu() {
+        let isDouble: boolean = false;
+
         return (
             <div className="propertiesView-filters">
-                <div className="propertiesView-filters-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform = false,
-                this.openOptions = false, this.openFields = false, this.openSharing = false, this.openLayout = false))}
-                onClick={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
+                <div className="propertiesView-filters-title" onClick ={action(() => {
+                    if (!isDouble){
+                        this.openFilters = !this.openFilters
+                    } } )}
+                 onDoubleClick={action(() => {
+                    isDouble = true;
+
+                    this.openContexts = false;
+                    this.openLinks = false;
+                    this.openOptions = false;
+                    this.openTransform = false;
+                    this.openFields = false;
+                    this.openSharing = false;
+                    this.openLayout = false;
+                    this.openFilters = true;
+                    
+
+                    setTimeout(() => {
+                        isDouble = false;
+                    }, 300)
+                }
+                    
+    )}
+                 style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
                     Filters
                     <div className="propertiesView-filters-title-icon">
                         <FontAwesomeIcon icon={this.openFilters ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1147,6 +1214,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get inkSubMenu() {
+        let isDouble = false;
+
         return (
             <>
                 {!this.isInk ? null : (
@@ -1162,8 +1231,27 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 )}
 
                 <div className="propertiesView-transform">
-                    <div className="propertiesView-transform-title" onClick={action(() => (this.openTransform = !this.openTransform))}  onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openOptions = false,
-                    this.openFields = false, this.openSharing = false, this.openFilters = false, this.openLayout = false, this.openTransform = true))}
+                    <div className="propertiesView-transform-title" onClick ={action(() => {
+                    if (!isDouble){
+                        this.openTransform = !this.openTransform
+                    } } )}
+                 onDoubleClick={action(() => {
+                    isDouble = true;
+
+                    this.openContexts = false;
+                    this.openLinks = false;
+                    this.openOptions = false;
+                    this.openTransform = true;
+                    this.openFields = false;
+                    this.openSharing = false;
+                    this.openLayout = false;
+                    this.openFilters = false;
+                    
+
+                    setTimeout(() => {
+                        isDouble = false;
+                    }, 300)
+                })}
                      style={{ backgroundColor: this.openTransform ? 'black' : '' }}>
                         Transform
                         <div className="propertiesView-transform-title-icon">
@@ -1177,11 +1265,34 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get fieldsSubMenu() {
+        let isDouble: boolean = false;
+
         return (
             <div className="propertiesView-fields">
-                <div className="propertiesView-fields-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform = false, 
-                this.openOptions = false, this.openSharing = false, this.openFilters = false, this.openLayout = false))}
-                 onClick={action(() => (this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}>
+                <div className="propertiesView-fields-title" onClick ={action(() => {
+                    if (!isDouble){
+                        this.openFields = !this.openFields
+                    } } )}
+                 onDoubleClick={action(() => {
+                    isDouble = true;
+
+                    this.openContexts = false;
+                    this.openLinks = false;
+                    this.openOptions = false;
+                    this.openTransform = false;
+                    this.openFields = true;
+                    this.openSharing = false;
+                    this.openLayout = false;
+                    this.openFilters = false;
+                    
+
+                    setTimeout(() => {
+                        isDouble = false;
+                    }, 300)
+                }
+                    
+    )}
+                  style={{ backgroundColor: this.openFields ? 'black' : '' }}>
                     Fields {'&'} Tags
                     <div className="propertiesView-fields-title-icon">
                         <FontAwesomeIcon icon={this.openFields ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1199,11 +1310,33 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get contextsSubMenu() {
+        let isDouble = false;
         return (
             <div className="propertiesView-contexts">
-                <div className="propertiesView-contexts-title" onDoubleClick={action(() => (this.openLinks = false, this.openTransform = false, this.openOptions = false, this.openFields = false,
-                 this.openSharing = false, this.openFilters = false, this.openLayout = false ))}
-                      onClick ={action(() => (this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}>
+                <div className="propertiesView-contexts-title" onClick ={action(() => {
+                    if (!isDouble){
+                        this.openContexts = !this.openContexts
+                    } } )}
+                 onDoubleClick={action(() => {
+                    isDouble = true;
+
+                    this.openContexts = true;
+                    this.openLinks = false;
+                    this.openOptions = false;
+                    this.openTransform = false;
+                    this.openFields = false;
+                    this.openSharing = false;
+                    this.openLayout = false;
+                    this.openFilters = false;
+                    
+
+                    setTimeout(() => {
+                        isDouble = false;
+                    }, 300)
+                }
+                    
+    )}
+                      style={{ backgroundColor: this.openContexts ? 'black' : '' }}>
                     Other Contexts
                     <div className="propertiesView-contexts-title-icon">
                         <FontAwesomeIcon icon={this.openContexts ? 'caret-down' : 'caret-right'} size="lg" color="white" />
@@ -1260,11 +1393,34 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get layoutSubMenu() {
+        let isDouble: boolean = false;
+
         return (
             <div className="propertiesView-layout">
-                <div className="propertiesView-layout-title" onDoubleClick={action(() => (this.openContexts = false, this.openLinks = false, this.openTransform  = false,
-                this.openOptions = false, this.openFields = false, this.openSharing = false, this.openFilters = false ))}
-                 onClick={action(() => (this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}>
+                <div className="propertiesView-layout-title" onClick ={action(() => {
+                    if (!isDouble){
+                        this.openLayout = !this.openLayout
+                    } } )}
+                 onDoubleClick={action(() => {
+                    isDouble = true;
+
+                    this.openContexts = false;
+                    this.openLinks = false;
+                    this.openOptions = false;
+                    this.openTransform = false;
+                    this.openFields = false;
+                    this.openSharing = false;
+                    this.openLayout = true;
+                    this.openFilters = false;
+                    
+
+                    setTimeout(() => {
+                        isDouble = false;
+                    }, 300)
+                }
+                    
+    )}
+                  style={{ backgroundColor: this.openLayout ? 'black' : '' }}>
                     Layout
                     <div className="propertiesView-layout-title-icon">
                         <FontAwesomeIcon icon={this.openLayout ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-- 
cgit v1.2.3-70-g09d2


From 32239432b79664f577bf0c84d4688c4e5cf7f14f Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 23 Jun 2023 11:11:07 -0400
Subject: acls in proto so that layout acls start cleared

---
 src/client/documents/Documents.ts   | 6 ++----
 src/client/util/SharingManager.tsx  | 3 ++-
 src/client/views/DocComponent.tsx   | 2 +-
 src/client/views/PropertiesView.tsx | 4 +++-
 src/fields/util.ts                  | 2 +-
 5 files changed, 9 insertions(+), 8 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7dde70a2e..05994ebbd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -839,9 +839,7 @@ export namespace Docs {
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
             // dataProps['acl-Override'] = SharingPermissions.Unset;
-            if (dataProps['acl-Public'] == undefined && Doc.GetProto(dataProps)['acl-Public'] == undefined){
-                dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-            }
+            dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps.isSystem = viewProps.isSystem;
             dataProps.isDataDoc = true;
@@ -859,7 +857,7 @@ export namespace Docs {
 
             // users placeholderDoc as proto if it exists
             const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
-
+            
             if (placeholderDoc) {
                 dataDoc.proto = proto;
             }
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3a3d393a0..a95874fd7 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -174,7 +174,7 @@ export class SharingManager extends React.Component<{}> {
 
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
-            .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
+            .map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc)))
             .map(doc => {
                 doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
 
@@ -339,6 +339,7 @@ export class SharingManager extends React.Component<{}> {
                 const acl = `acl-${StrCast(group.title)}`;
                 const isDashboard = dashboards.indexOf(doc) !== -1;
                 distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined, isDashboard);
+                distributeAcls(acl, SharingPermissions.None, Doc.GetProto(doc), undefined, undefined, isDashboard);
 
                 const members: string[] = JSON.parse(StrCast(group.members));
                 const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 98aa9f880..6f350e0fc 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -199,7 +199,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             const permissionString = StrCast(Doc.GetProto(this.props.Document)[key])
                             const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl
                             const permission = HierarchyMapping.get(permissionSymbol)!.name
-                            distributeAcls(key, permission, d)
+                            distributeAcls(key, permission, Doc.GetProto(d))
                         }
                     })
                 );
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 825a802cd..6bca08d23 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -425,12 +425,14 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns the sharing and permissions panel.
      */
     @computed get sharingTable() {
+        console.log('doc', this.selectedDoc)
+        console.log('proto', Doc.GetProto(this.selectedDoc!))
         // all selected docs
         const docs =
             SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
         const target = docs[0];
 
-        const showAdmin = GetEffectiveAcl(this.selectedDoc!) == AclAdmin
+        const showAdmin = GetEffectiveAcl(target) == AclAdmin
         const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index f31213869..1ac450b9a 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -242,7 +242,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     if (!visited) visited = [] as Doc[];
     if (!target || visited.includes(target)) return;
     if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
-        Doc.GetProto(target)[key] = acl;
+        target[key] = acl;
         if (target !== Doc.GetProto(target)) {
             //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
             updateCachedAcls(target);
-- 
cgit v1.2.3-70-g09d2


From a807da6b6fed067888f817e2273b29b8b6219e52 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 23 Jun 2023 12:05:24 -0400
Subject: fixed collection acls

---
 src/client/documents/Documents.ts                               | 6 +++---
 src/client/views/PropertiesView.tsx                             | 2 --
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 8 ++++++--
 3 files changed, 9 insertions(+), 7 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 05994ebbd..0ab0a2ca5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1011,7 +1011,7 @@ export namespace Docs {
             I.rotation = 0;
             I.defaultDoubleClick = 'click';
             I.author_date = new DateField();
-            // I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             //I['acl-Override'] = SharingPermissions.Unset;
             I[Initializing] = false;
             return I;
@@ -1348,8 +1348,8 @@ export namespace DocUtils {
                 source,
                 target,
                 {
-                    // 'acl-Public': SharingPermissions.Augment,
-                    // '_acl-Public': SharingPermissions.Augment,
+                    'acl-Public': SharingPermissions.Augment,
+                    '_acl-Public': SharingPermissions.Augment,
                     title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
                     link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined,
                     link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined,
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 6bca08d23..d5d09389f 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -425,8 +425,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns the sharing and permissions panel.
      */
     @computed get sharingTable() {
-        console.log('doc', this.selectedDoc)
-        console.log('proto', Doc.GetProto(this.selectedDoc!))
         // all selected docs
         const docs =
             SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 77cb7f88c..430e8ecd5 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -9,7 +9,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
 import { Cast, DocCast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { GetEffectiveAcl } from '../../../../fields/util';
+import { GetEffectiveAcl, SharingPermissions } from '../../../../fields/util';
 import { intersectRect, returnFalse, Utils } from '../../../../Utils';
 import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
 import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
@@ -390,7 +390,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
         newCollection.layout_fitWidth = true;
-        selected.forEach(d => (d.embedContainer = newCollection));
+        newCollection['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+        selected.forEach(d => {
+            d.embedContainer = newCollection;
+            d['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+        });
         this.hideMarquee();
         return newCollection;
     });
-- 
cgit v1.2.3-70-g09d2


From 777381bc14e825a81f7bcb8b8ab305e1ac39ce00 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 26 Jun 2023 11:18:35 -0400
Subject: PropertiesView layout acls work

---
 src/client/util/SharingManager.tsx  | 25 ++++++++++---------
 src/client/views/PropertiesView.tsx | 49 +++++++++++++++++++++++++++----------
 2 files changed, 49 insertions(+), 25 deletions(-)

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 59a88b404..0f7ce26d2 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -214,9 +214,7 @@ export class SharingManager extends React.Component<{}> {
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
-        if (acl == 'acl-Public' && this.layoutDocAcls){
-            acl = 'acl-Public-layout';
-        }
+        if (acl == 'acl-Public' && this.layoutDocAcls) acl = 'acl-Public-layout';
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
@@ -274,7 +272,8 @@ export class SharingManager extends React.Component<{}> {
     /**
      * Called from the properties sidebar to change permissions of a user.
      */
-    shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => {
+    shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => {
+        if (layout) this.layoutDocAcls = true;
         if (shareWith !== 'Public' && shareWith !== 'Override') {
             const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith));
             docs.forEach(doc => {
@@ -285,11 +284,17 @@ export class SharingManager extends React.Component<{}> {
             const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
-                if (this.overridePrevious) this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data));
-                if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
+                if (this.overridePrevious) {
+                    this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data), layout);
+                }
+                if (GetEffectiveAcl(doc) === AclAdmin) {
+                    if ( shareWith == 'Public' && layout) shareWith = 'Public-layout';
+                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
+                }
                 this.setDashboardBackground(doc, permission as SharingPermissions);
             });
         }
+        this.layoutDocAcls = false;
     };
 
     /**
@@ -607,16 +612,12 @@ export class SharingManager extends React.Component<{}> {
             // const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
             let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][groupKey] ? HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name : StrCast(Doc.GetProto(targetDoc)[groupKey])) : StrCast(targetDoc[groupKey]);
                 if (this.layoutDocAcls){
-                    if (groupKey == 'acl-Public'){
-                        groupKey = 'acl-Public-layout'
-                    }
+                    if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout';
                     if (targetDoc[DocAcl][groupKey]){
                         permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name;
                     }
                     else{
-                        if (groupKey == 'acl-Public-layout'){
-                            groupKey = 'acl-Public'
-                        }
+                        if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public';
                         if (targetDoc['embedContainer']){
                             permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
                         }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index d5d09389f..0a8eabdb1 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -8,7 +8,7 @@ import { Lambda, action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
-import { Doc, Field, FieldResult, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
+import { Doc, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
 import { AclAdmin, DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
@@ -303,7 +303,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @undoBatch
     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)[DocData]));
-        SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
+        SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls);
     };
 
     /**
@@ -450,8 +450,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // sorts and then adds each user to the table
         usersAdded.sort(this.sortUsers);
         usersAdded.map(userEmail => {
-            const permission = StrCast(target[`acl-${normalizeEmail(userEmail)}`]);
-            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission, false)); // adds each user
+            const userKey = `acl-${normalizeEmail(userEmail)}`;
+            var permission;
+            if (this.layoutDocAcls){
+                if (target[DocAcl][userKey]){
+                    permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
+                }
+                else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
+                else permission = StrCast(Doc.GetProto(target)?.[userKey]);
+            }
+            else permission = StrCast(target[userKey]);
+            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         });
 
         // adds current user
@@ -473,17 +482,33 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             if (group.title != 'Public' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
-                    const permission = StrCast(target[groupKey]);
-                    groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+                    var permission;
+                    if (this.layoutDocAcls){
+                        if (target[DocAcl][groupKey]){
+                            permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name;
+                        }
+                        else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]);
+                        else permission = StrCast(Doc.GetProto(target)?.[groupKey]);
+                    }
+                    else permission = StrCast(target[groupKey]);
+                    groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false));
                 }
             }
         });
 
+        // public permission
+        let publicPermission = StrCast(target['acl-Public']);
+        if (this.layoutDocAcls){
+            if (target['acl-Public-layout']) publicPermission = StrCast(target['acl-Public-layout']);
+            else if (target['embedContainer']) publicPermission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))['acl-Public']);
+            else StrCast(Doc.GetProto(target)['acl-Public']);
+        }
+
         return (
             <div>
                 {' '}
                 Public / Guest Users
-                <div>{this.colorACLDropDown('Public', showAdmin, StrCast(target['acl-Public']), false)}</div>
+                <div>{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}</div>
                 <div>
                     {' '}
                     <br></br> Individual Users with Access to this Document{' '}
@@ -994,12 +1019,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 {!this.openSharing ? null : (
                     <div className="propertiesView-sharing-content">
                         <div className="propertiesView-buttonContainer">
-                            {/* {!Doc.noviceMode ? ( // what is the layout checkbox for?
-                                <div className="propertiesView-acls-checkbox">
-                                    <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
-                                    <div className="propertiesView-acls-checkbox-text">Layout</div>
-                                </div>
-                            ) : null} */}
+                            <div className="propertiesView-acls-checkbox">
+                                <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
+                                <div className="propertiesView-acls-checkbox-text">Layout</div>
+                            </div>
 
                             {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
                                         <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
-- 
cgit v1.2.3-70-g09d2


From fbfd21718e63e13ec5b8ba913a65b3ee76891a34 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 26 Jun 2023 11:43:23 -0400
Subject: PropertiesView layout UI

---
 src/client/views/PropertiesView.scss | 8 ++++----
 src/client/views/PropertiesView.tsx  | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index eafe752be..f2be966b9 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -135,12 +135,12 @@
                 }
 
                 .propertiesView-acls-checkbox {
-                    margin-top: -20px;
+                    margin-top: -15px;
+                    margin-bottom: -10px;
 
                     .propertiesView-acls-checkbox-text {
-                        font-size: 7px;
-                        margin-top: -10px;
-                        margin-left: 6px;
+                        display: inline;
+                        font-size: 9px;
                     }
                 }
             }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 0a8eabdb1..ca357f830 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -506,7 +506,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         return (
             <div>
-                {' '}
+                <br/>
                 Public / Guest Users
                 <div>{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}</div>
                 <div>
@@ -1020,8 +1020,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     <div className="propertiesView-sharing-content">
                         <div className="propertiesView-buttonContainer">
                             <div className="propertiesView-acls-checkbox">
+                                <div className="propertiesView-acls-checkbox-text"> Show / Contol Layout Permissions </div>
                                 <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
-                                <div className="propertiesView-acls-checkbox-text">Layout</div>
                             </div>
 
                             {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
-- 
cgit v1.2.3-70-g09d2


From 68e791512551261fd805f841fd88aba373df845a Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 26 Jun 2023 15:02:12 -0400
Subject: layout always an option in SharingManager, current user layout acl
 bug fix in PropertiesView + SharingManager

---
 src/client/util/SharingManager.scss |  1 +
 src/client/util/SharingManager.tsx  | 54 +++++++++++++++++--------------------
 src/client/views/PropertiesView.tsx | 15 +++++++----
 3 files changed, 36 insertions(+), 34 deletions(-)

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

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 98fea0dcb..86de5ac44 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -86,6 +86,7 @@
                 float: right;
                 align-items: baseline;
                 margin-top: -12;
+                margin-bottom: 10;
 
                 .layoutDoc-acls,
                 .myDocs-acls {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 0f7ce26d2..7593cc025 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -539,21 +539,11 @@ export class SharingManager extends React.Component<{}> {
                 // const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
                 let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(Doc.GetProto(targetDoc)[userKey])) : StrCast(targetDoc[userKey]);
                 if (this.layoutDocAcls){
-                    if (targetDoc[DocAcl][userKey]){
-                        permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
-                    }
-                    else{
-                        if (targetDoc['embedContainer']){
-                            permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
-                        }
-                        else{
-                            permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[userKey]) : '-multiple-';
-                        }
-                    }
-                }
-                else{
-                    permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                    if (targetDoc[DocAcl][userKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
+                    else if (targetDoc['embedContainer'])  permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
+                    else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[userKey]) : '-multiple-';
                 }
+                else permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
                 
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
@@ -578,7 +568,15 @@ export class SharingManager extends React.Component<{}> {
         const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author);
 
         // the owner of the doc and the current user are placed at the top of the user list.
-        const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name
+        const userKey = `acl-${normalizeEmail(Doc.CurrentUserEmail)}`;
+        var curUserPermission;
+            if (this.layoutDocAcls){
+                if (targetDoc[DocAcl][userKey]) curUserPermission = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
+                else if (targetDoc['embedContainer']) curUserPermission = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
+                else curUserPermission = StrCast(Doc.GetProto(targetDoc)?.[userKey]);
+            }
+            else curUserPermission = StrCast(targetDoc[userKey]);
+        // const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name
         userListContents.unshift(
             sameAuthor ? (
                 <div key={'owner'} className={'container'}>
@@ -593,7 +591,7 @@ export class SharingManager extends React.Component<{}> {
                     <span className={'padding'}>Me</span>
                     <div className="edit-actions">
                         <div className={`permissions-dropdown-${curUserPermission}`}>
-                            {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? concat(ReverseHierarchyMap.get(curUserPermission)?.image, ' ', curUserPermission) : '-multiple-'}
+                            {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? concat(ReverseHierarchyMap.get(curUserPermission!)?.image, ' ', curUserPermission) : '-multiple-'}
                             &nbsp;
                         </div>
                     </div>
@@ -613,22 +611,14 @@ export class SharingManager extends React.Component<{}> {
             let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][groupKey] ? HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name : StrCast(Doc.GetProto(targetDoc)[groupKey])) : StrCast(targetDoc[groupKey]);
                 if (this.layoutDocAcls){
                     if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout';
-                    if (targetDoc[DocAcl][groupKey]){
-                        permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name;
-                    }
+                    if (targetDoc[DocAcl][groupKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name;
                     else{
                         if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public';
-                        if (targetDoc['embedContainer']){
-                            permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
-                        }
-                        else{
-                            permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-';
-                        }
+                        if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
+                        else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-';
                     }
                 }
-                else{
-                    permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
-                }
+                else permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
 
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
@@ -710,7 +700,13 @@ export class SharingManager extends React.Component<{}> {
                             </div>
                         </div>
                     ) : (
-                        <br />
+                        <div className='share-container'>
+                            <div className='acl-container'>
+                                <div className='layoutDoc-acls'>
+                                    <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
+                                </div>
+                            </div>
+                        </div>
                     )}
                     <div className="main-container">
                         <div className={'individual-container'}>
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index ca357f830..904755132 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -453,9 +453,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             const userKey = `acl-${normalizeEmail(userEmail)}`;
             var permission;
             if (this.layoutDocAcls){
-                if (target[DocAcl][userKey]){
-                    permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
-                }
+                if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
                 else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
                 else permission = StrCast(Doc.GetProto(target)?.[userKey]);
             }
@@ -465,10 +463,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // adds current user
         var userEmail = Doc.CurrentUserEmail;
+        const userKey = `acl-${normalizeEmail(userEmail)}`;
         if (userEmail == 'guest') userEmail = 'Public';
         if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
-            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, StrCast(target[`acl-${normalizeEmail(userEmail)}`]), false)); // adds each user
-            usersAdded.push(userEmail);
+            var permission;
+            if (this.layoutDocAcls){
+                if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
+                else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
+                else permission = StrCast(Doc.GetProto(target)?.[userKey]);
+            }
+            else permission = StrCast(target[userKey]);
+            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         }
 
         // shift owner to top
-- 
cgit v1.2.3-70-g09d2


From 5b7a0804fa2bd4b956b3617501619737814bd28b Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Fri, 30 Jun 2023 14:47:41 -0400
Subject: contexts work now

---
 src/client/views/PropertiesDocContextSelector.tsx |  3 +++
 src/client/views/PropertiesView.scss              | 14 +++++++++++---
 src/client/views/PropertiesView.tsx               | 16 ++++++++++------
 3 files changed, 24 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index e1279c9a7..ea3bb434b 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -19,6 +19,7 @@ type PropertiesDocContextSelectorProps = {
 
 @observer
 export class PropertiesDocContextSelector extends React.Component<PropertiesDocContextSelectorProps> {
+
     @computed get _docs() {
         if (!this.props.DocView) return [];
         const target = this.props.DocView.props.Document;
@@ -39,6 +40,8 @@ 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))
             .filter(doc => !Doc.IsSystem(doc))
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index a9577b232..023e08e6a 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -443,12 +443,20 @@
         .propertiesView-fields-checkbox {
             float: right;
             height: 20px;
-            margin-top: -9px;
+            margin-top: -4px;
+            //margin-top: -9px;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            color:black;
+           // justify-content: center;
 
             .propertiesView-fields-checkbox-text {
                 font-size: 7px;
-                margin-top: -10px;
-                margin-left: 6px;
+                text-align: center;
+                margin-right: 6px;
+                 margin-top: -10px;
+                // margin-left: 6px;
             }
         }
 
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 60e93cdfd..585076b67 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -198,7 +198,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             });
 
             rows.push(
-                <div className="propertiesView-field" key="newKeyValue" style={{ marginTop: '3px' }}>
+                <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>
             );
@@ -257,9 +257,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get contextCount(){
-        if (this.selectedDoc != undefined){
-            const aliases = DocListCast(this.selectedDoc.aliases)
-            return aliases.length   
+        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{
             return 0;
         }
@@ -462,6 +465,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get fieldsCheckbox() {
+        // color= "primary"
         return <Checkbox color="primary" onChange={this.toggleCheckbox} checked={this.layoutFields} />;
     }
 
@@ -1301,7 +1305,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 {!Doc.noviceMode && this.openFields ? (
                     <div className="propertiesView-fields-checkbox">
                         {this.fieldsCheckbox}
-                        <div className="propertiesView-fields-checkbox-text">Layout</div>
+                        <div className="propertiesView-fields-checkbox-text">Embedding</div>
                     </div>
                 ) : null}
                 {!this.openFields ? null : <div className="propertiesView-fields-content">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>}
@@ -1342,7 +1346,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         <FontAwesomeIcon icon={this.openContexts ? 'caret-down' : 'caret-right'} size="lg" color="white" />
                     </div>
                 </div>
-                {!this.openContexts ? null : this.contextCount > 0 ? <div className="propertiesView-contexts-content">{this.contexts}</div> : <div className="propertiesView-contexts-content">There are no other contexts.</div>}
+                {!this.openContexts ? null : this.contextCount > 0 ? <div className="propertiesView-contexts-content">{this.contexts}</div> : <div className="propertiesView-contexts-content">There are no other contexts.</div>} 
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From fa38dbe06d6ddb5f4499b759459a24d2b3c111e8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 7 Jul 2023 13:36:21 -0400
Subject: a bunch of fixes to simplify collaboration and make it work better.

---
 src/client/DocServer.ts                            |  12 +-
 src/client/util/SharingManager.tsx                 | 203 +++++----------------
 src/client/views/DashboardView.scss                | 139 +++++++-------
 src/client/views/DashboardView.tsx                 |  18 +-
 src/client/views/DocComponent.tsx                  |  61 ++-----
 src/client/views/DocumentDecorations.tsx           |  29 ++-
 src/client/views/GlobalKeyHandler.ts               |  12 +-
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/PropertiesView.tsx                |  47 ++---
 .../views/collections/CollectionDockingView.tsx    |  17 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 src/fields/Doc.ts                                  |   4 +-
 src/fields/util.ts                                 |  77 ++------
 14 files changed, 231 insertions(+), 400 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 2a7f5a09b..876f2400d 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -76,10 +76,14 @@ export namespace DocServer {
     const fieldWriteModes: { [field: string]: WriteMode } = {};
     const docsWithUpdates: { [field: string]: Set<Doc> } = {};
 
-    export var PlaygroundFields: string[];
-    export function setPlaygroundFields(livePlaygroundFields: string[]) {
-        DocServer.PlaygroundFields = livePlaygroundFields;
-        livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
+    export var PlaygroundFields: string[] = [];
+    export function setLivePlaygroundFields(livePlaygroundFields: string[]) {
+        DocServer.PlaygroundFields.push(...livePlaygroundFields);
+        livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground));
+    }
+    export function setPlaygroundFields(playgroundFields: string[]) {
+        DocServer.PlaygroundFields.push(...playgroundFields);
+        playgroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
     }
     export function IsPlaygroundField(field: string) {
         return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ''));
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 64b2bb5b8..d3241009a 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -6,10 +6,9 @@ import * as React from 'react';
 import Select from 'react-select';
 import * as RequestPromise from 'request-promise';
 import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
-import { AclAdmin, AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
-import { List } from '../../fields/List';
-import { DocCast, NumCast, StrCast } from '../../fields/Types';
+import { StrCast } from '../../fields/Types';
 import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
 import { Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
@@ -73,13 +72,11 @@ export class SharingManager extends React.Component<{}> {
     @observable private individualSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of individuals
     @observable private groupSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of groups
     private shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the share button, used for the position of the popup
-    private distributeAclsButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the distribute button, used for the position of the popup
     // if both showUserOptions and showGroupOptions are false then both are displayed
     @observable private showUserOptions: boolean = false; // whether to show individuals as options when sharing (in the react-select component)
     @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
     private populating: boolean = false; // whether the list of users is populating or not
     @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
-    @observable private overrideNested: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
     @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
 
     // private get linkVisible() {
@@ -110,7 +107,6 @@ export class SharingManager extends React.Component<{}> {
             500
         );
         this.layoutDocAcls = false;
-        this.overrideNested = false;
     });
 
     constructor(props: {}) {
@@ -153,41 +149,19 @@ export class SharingManager extends React.Component<{}> {
     /**
      * Shares the document with a user.
      */
-    setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => {
+    setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc: Doc | undefined) => {
         const { user, sharingDoc } = recipient;
         const target = targetDoc || this.targetDoc!;
         const acl = `acl-${normalizeEmail(user.email)}`;
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
-        const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
-
-        // setting the same acl for a docs within the doc being shared if they haven't been set yet
-        // or if the 'Override Nested' checkbox is selected
-        var childDocs = DocListCast(target.data);
-        childDocs.map(doc => {
-            if (this.overrideNested || doc[acl] == undefined) {
-                this.setInternalSharing(recipient, 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.GetProto(doc)))
-            .map(doc => {
-                doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
+        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
+        docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+            doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined);
 
-                if (permission === SharingPermissions.None) {
-                    if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
-                } else {
-                    if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
-                }
-                distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-                this.setDashboardBackground(doc, permission as SharingPermissions);
-                if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
-                return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
-            })
-            .some(success => !success);
+            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
+            if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc);
+            else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
+        });
     };
 
     /**
@@ -199,51 +173,24 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const key = normalizeEmail(StrCast(group.title));
         let acl = `acl-${key}`;
-        const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
-
-        // setting the same acl for a docs within the doc being shared if they haven't been set yet
-        // or if the 'Override Private' checkbox is selected
-        var childDocs = DocListCast(target.data);
-        childDocs.map(doc => {
-            if (this.overrideNested || doc[acl] == undefined) {
-                this.setInternalGroupSharing(group, permission, doc);
-            }
-        });
-
-        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
-        if (acl == 'acl-Public' && this.layoutDocAcls) acl = 'acl-Public-layout';
-        // ! ensures it returns true if document has been shared successfully, false otherwise
-        return !docs
-            .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
-            .map(doc => {
-                doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
-                if (permission === SharingPermissions.None) {
-                    if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1;
-                } else {
-                    if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1;
-                }
 
-                distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-                this.setDashboardBackground(doc, permission as SharingPermissions);
+        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
+        docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+            doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined);
 
-                if (group instanceof Doc) {
-                    const members: string[] = JSON.parse(StrCast(group.members));
-                    const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
+            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
 
-                    // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
-                    group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : (group.docsShared = new List<Doc>([doc]));
+            if (group instanceof Doc) {
+                Doc.AddDocToList(group, 'docsShared', doc);
 
-                    return users
-                        .map(({ user, sharingDoc }) => {
-                            if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
-                            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
-                        })
-                        .some(success => !success);
-                }
-            })
-            .some(success => success);
+                this.users
+                    .filter(({ user: { email } }) => JSON.parse(StrCast(group.members)).includes(email))
+                    .forEach(({ user, sharingDoc }) => {
+                        if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
+                        else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
+                    });
+            }
+        });
     };
 
     /**
@@ -279,44 +226,15 @@ export class SharingManager extends React.Component<{}> {
                 else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
             });
         } else {
-            const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
-                const isDashboard = dashboards.indexOf(doc) !== -1;
-                if (this.overrideNested) {
-                    this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data), layout);
-                }
                 if (GetEffectiveAcl(doc) === AclAdmin) {
-                    if (shareWith == 'Public' && layout) shareWith = 'Public-layout';
-                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
+                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined);
                 }
-                this.setDashboardBackground(doc, permission as SharingPermissions);
             });
         }
         this.layoutDocAcls = false;
     };
 
-    /**
-     * Sets the background of the Dashboard if it has been shared as a visual indicator
-     */
-    setDashboardBackground = (doc: Doc, permission: SharingPermissions) => {
-        if (Doc.IndexOf(doc, DocListCast(Doc.MyDashboards.data)) !== -1) {
-            if (permission !== SharingPermissions.None) {
-                doc.isShared = true;
-                doc.backgroundColor = 'green';
-            } else {
-                const acls = doc[DocData][DocAcl];
-                if (
-                    Object.keys(acls)
-                        .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && key !== 'acl-Me')
-                        .every(key => [AclUnset, AclPrivate].includes(acls[key]))
-                ) {
-                    doc.isShared = undefined;
-                    doc.backgroundColor = undefined;
-                }
-            }
-        }
-    };
-
     /**
      * Removes the documents shared with a user through a group when the user is removed from the group.
      * @param group
@@ -341,12 +259,9 @@ export class SharingManager extends React.Component<{}> {
      */
     removeGroup = (group: Doc) => {
         if (group.docsShared) {
-            const dashboards = DocListCast(Doc.MyDashboards.data);
             DocListCast(group.docsShared).forEach(doc => {
                 const acl = `acl-${StrCast(group.title)}`;
-                const isDashboard = dashboards.indexOf(doc) !== -1;
-                distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined, isDashboard);
-                distributeAcls(acl, SharingPermissions.None, Doc.GetProto(doc), undefined, undefined, isDashboard);
+                distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined);
 
                 const members: string[] = JSON.parse(StrCast(group.members));
                 const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
@@ -440,7 +355,7 @@ export class SharingManager extends React.Component<{}> {
         if (this.selectedUsers) {
             this.selectedUsers.forEach(user => {
                 if (user.value.includes(indType)) {
-                    this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
+                    this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions, undefined);
                 } else {
                     this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
                 }
@@ -457,7 +372,6 @@ export class SharingManager extends React.Component<{}> {
             );
 
             this.layoutDocAcls = false;
-            this.overrideNested = false;
             this.selectedUsers = null;
         }
     };
@@ -509,8 +423,7 @@ export class SharingManager extends React.Component<{}> {
         const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
         const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
 
-        // handles the case where multiple documents are selected
-        let docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DocData]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DocData]));
+        let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
 
         if (this.myDocAcls) {
             const newDocs: Doc[] = [];
@@ -518,36 +431,32 @@ export class SharingManager extends React.Component<{}> {
             docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin);
         }
 
-        const targetDoc: Doc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
+        const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
 
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
         const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
         const admin = this.myDocAcls ? Boolean(docs.length) : effectiveAcls.every(acl => acl === AclAdmin);
 
         // users in common between all docs
-        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
+        const commonKeys = intersection(docs).reduce((list, doc) => (doc?.[DocAcl] ? [...list, ...Object.keys(doc[DocAcl])] : list), [] as string[]);
 
         // the list of users shared with
-        const userListContents: (JSX.Element | null)[] = users
+        const userListContents = users
             // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
             .filter(({ user }) => docs[0]?.author !== user.email)
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
                 const userKey = `acl-${normalizeEmail(user.email)}`;
-                const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
+                const uniform = docs.every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 // const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
-                let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(Doc.GetProto(targetDoc)[userKey])) : StrCast(targetDoc[userKey]);
-                if (this.layoutDocAcls) {
-                    if (targetDoc[DocAcl][userKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
-                    else if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
-                    else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[userKey]) : '-multiple-';
-                } else permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                let permissions = targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(targetDoc[userKey]);
+                permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
 
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
                         <span className={'padding'}>{user.email}</span>
                         <div className="edit-actions">
                             {admin || this.myDocAcls ? (
-                                <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value)}>
+                                <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value, undefined)}>
                                     {this.sharingOptions(uniform)}
                                 </select>
                             ) : (
@@ -566,12 +475,7 @@ export class SharingManager extends React.Component<{}> {
 
         // the owner of the doc and the current user are placed at the top of the user list.
         const userKey = `acl-${normalizeEmail(Doc.CurrentUserEmail)}`;
-        var curUserPermission;
-        if (this.layoutDocAcls) {
-            if (targetDoc[DocAcl][userKey]) curUserPermission = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
-            else if (targetDoc['embedContainer']) curUserPermission = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
-            else curUserPermission = StrCast(Doc.GetProto(targetDoc)?.[userKey]);
-        } else curUserPermission = StrCast(targetDoc[userKey]);
+        const curUserPermission = StrCast(targetDoc[userKey]);
         // const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name
         userListContents.unshift(
             sameAuthor ? (
@@ -600,20 +504,8 @@ export class SharingManager extends React.Component<{}> {
         groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
         const groupListContents = groupListMap.map(group => {
             let groupKey = `acl-${StrCast(group.title)}`;
-            const uniform = docs
-                .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
-                .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey]));
-            // const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
-            let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][groupKey] ? HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name : StrCast(Doc.GetProto(targetDoc)[groupKey])) : StrCast(targetDoc[groupKey]);
-            if (this.layoutDocAcls) {
-                if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout';
-                if (targetDoc[DocAcl][groupKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name;
-                else {
-                    if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public';
-                    if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
-                    else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-';
-                }
-            } else permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
+            const uniform = docs.every(doc => doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey]);
+            const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
 
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
@@ -693,7 +585,6 @@ export class SharingManager extends React.Component<{}> {
                             <div className="acl-container">
                                 {Doc.noviceMode ? null : (
                                     <div className="layoutDoc-acls">
-                                        <input type="checkbox" onChange={action(() => (this.overrideNested = !this.overrideNested))} checked={this.overrideNested} /> <label>Override Nested </label>
                                         <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
                                     </div>
                                 )}
@@ -713,16 +604,10 @@ export class SharingManager extends React.Component<{}> {
                             <div className="user-sort" onClick={action(() => (this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}>
                                 <div className="title-individual">
                                     Individuals &nbsp;
-                                    {this.individualSort === 'ascending' ? (
-                                        <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
-                                    ) : this.individualSort === 'descending' ? (
-                                        <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
-                                    ) : (
-                                        <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
-                                    )}
+                                    <FontAwesomeIcon icon={this.individualSort === 'ascending' ? 'caret-up' : this.individualSort === 'descending' ? 'caret-down' : 'caret-right'} size="xs" />
                                 </div>
                             </div>
-                            <div className={'users-list'}>{userListContents}</div>
+                            <div className="users-list">{userListContents}</div>
                         </div>
                         <div className={'group-container'}>
                             <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
@@ -732,13 +617,7 @@ export class SharingManager extends React.Component<{}> {
                                         <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
                                     </div>
                                     &nbsp;
-                                    {this.groupSort === 'ascending' ? (
-                                        <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
-                                    ) : this.groupSort === 'descending' ? (
-                                        <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
-                                    ) : (
-                                        <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
-                                    )}
+                                    <FontAwesomeIcon icon={this.groupSort === 'ascending' ? 'caret-up' : this.groupSort === 'descending' ? 'caret-down' : 'caret-right'} size="xs" />
                                 </div>
                             </div>
                             <div className={'groups-list'}>{groupListContents}</div>
diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
index b8a6f6c05..a37df106b 100644
--- a/src/client/views/DashboardView.scss
+++ b/src/client/views/DashboardView.scss
@@ -1,39 +1,38 @@
-@import "./global/globalCssVariables";
-
+@import './global/globalCssVariables';
 
 .dashboard-view {
-      padding: 50px;
-      display: flex;
-      flex-direction: row;
-      width: 100%;
-      position: absolute;
-
-      .left-menu {
-            display: flex;
-            justify-content: flex-start;
-            flex-direction: column;
-            width: 250px;
-            min-width: 250px;
-      }
-
-      .all-dashboards {
-            display: flex;
-            flex-direction: row;
-            flex-wrap: wrap;
-            overflow-y: scroll;
-      }
+    padding: 50px;
+    display: flex;
+    flex-direction: row;
+    width: 100%;
+    position: absolute;
+
+    .left-menu {
+        display: flex;
+        justify-content: flex-start;
+        flex-direction: column;
+        width: 250px;
+        min-width: 250px;
+    }
+
+    .all-dashboards {
+        display: flex;
+        flex-direction: row;
+        flex-wrap: wrap;
+        overflow-y: scroll;
+    }
 }
 
 .text-button {
     cursor: pointer;
-      padding: 3px 0;
-      &:hover {
-            font-weight: 500;
-      }
-
-      &.selected {
-            font-weight: 700;
-      }
+    padding: 3px 0;
+    &:hover {
+        font-weight: 500;
+    }
+
+    &.selected {
+        font-weight: 700;
+    }
 }
 
 .new-dashboard-button {
@@ -64,41 +63,51 @@
 }
 
 .dashboard-container {
-      border-radius: 10px;
-      cursor: pointer;
-      width: 250px;
-      height: 200px;
-      outline: solid 2px $light-gray;
-      display: flex;
-      flex-direction: column;
-      margin: 0 0px 30px 30px;
-      overflow: hidden;
-
-      &:hover{
+    border-radius: 10px;
+    cursor: pointer;
+    width: 250px;
+    height: 200px;
+    outline: solid 2px $light-gray;
+    display: flex;
+    flex-direction: column;
+    margin: 0 0px 30px 30px;
+    overflow: hidden;
+
+    &:hover {
         outline: solid 2px $light-blue;
-      }
-
-      .title {
-            margin: 10px;
-            font-weight: 500;
-      }
-
-      img {
-            width: auto;
-            height: 80%;
-      }
-
-      .info {
-            display: flex;
-            flex-direction: row;
-            justify-content: space-between;
-            align-items: center;
-            padding: 0px 10px;
-      }
-
-      .more {
-            z-index: 100;
-      }
+    }
+
+    .title {
+        margin: 10px;
+        font-weight: 500;
+    }
+
+    img {
+        width: auto;
+        height: 80%;
+    }
+
+    .info {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        padding: 0px 10px;
+    }
+    .dashboard-status,
+    .dashboard-status-shared {
+        font-size: 9;
+        left: 10%;
+        position: relative;
+        top: -5;
+    }
+    .dashboard-status-shared {
+        background: 'lightgreen';
+    }
+
+    .more {
+        z-index: 100;
+    }
 }
 
 .new-dashboard {
@@ -136,4 +145,4 @@
         flex-direction: row;
         justify-content: flex-end;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index b60b84015..6feeb8846 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, DocListCastAsync } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
+import { AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
@@ -67,7 +67,7 @@ export class DashboardView extends React.Component {
         if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
             return allDashboards.filter(dashboard => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail);
         } else {
-            const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc.dockingConfig)
+            const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc.dockingConfig);
             // const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._type_collection === CollectionViewType.Docking);
             return sharedDashboards;
         }
@@ -168,15 +168,24 @@ export class DashboardView extends React.Component {
                     <div className="all-dashboards">
                         {this.getDashboards().map(dashboard => {
                             const href = ImageCast(dashboard.thumb)?.url.href;
+                            const shared = Object.keys(dashboard[DocAcl])
+                                .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Public'].includes(key))
+                                .some(key => ![AclUnset, AclPrivate].includes(dashboard[DocAcl][key]));
                             return (
-                                <div className="dashboard-container" key={dashboard[Id]} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
+                                <div className="dashboard-container" key={dashboard[Id]} style={{ background: shared ? 'lightgreen' : '' }} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
                                     <img
                                         src={
                                             href ?? 'https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU='
                                         }
                                     />
                                     <div className="info">
-                                        <input style={{ border: 'unset' }} className="input" onClick={e => e.stopPropagation()} defaultValue={StrCast(dashboard.title)} onChange={e => (Doc.GetProto(dashboard).title = (e.target as any).value)} />
+                                        <input
+                                            style={{ border: 'unset', borderRadius: '5px' }}
+                                            className="input"
+                                            onClick={e => e.stopPropagation()}
+                                            defaultValue={StrCast(dashboard.title)}
+                                            onChange={e => (Doc.GetProto(dashboard).title = (e.target as any).value)}
+                                        />
                                         {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></div>}
                                         <div
                                             className="more"
@@ -192,6 +201,7 @@ export class DashboardView extends React.Component {
                                             <Button size={Size.SMALL} icon={<FontAwesomeIcon color="black" size="lg" icon="bars" />} />
                                         </div>
                                     </div>
+                                    <div className={'dashboard-status' + (shared ? '-shared' : '')}>{shared ? 'shared' : ''}</div>
                                 </div>
                             );
                         })}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 1d0feec74..44af51341 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,18 +1,16 @@
 import { action, computed, observable } from 'mobx';
 import { DateField } from '../../fields/DateField';
-import { DocListCast, Opt, Doc, ReverseHierarchyMap, HierarchyMapping } from '../../fields/Doc';
+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, DocCast, ScriptCast, StrCast } from '../../fields/Types';
-import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, normalizeEmail, SharingPermissions } from '../../fields/util';
+import { Cast, StrCast } from '../../fields/Types';
+import { distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
 import { InteractionUtils } from '../util/InteractionUtils';
-import { UndoManager } from '../util/UndoManager';
 import { DocumentView } from './nodes/DocumentView';
 import { Touchable } from './Touchable';
-import { SharingManager } from '../util/SharingManager';
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 export interface DocComponentProps {
@@ -184,7 +182,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             if (this.props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
                 return false;
             }
-            const targetDataDoc = this.props.Document[DocData];
+            const targetDataDoc = this.rootDoc[DocData];
             const effectiveAcl = GetEffectiveAcl(targetDataDoc);
 
             if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
@@ -194,52 +192,33 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             if (added.length) {
                 const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
 
-                aclKeys.forEach(key =>
-                    added.forEach(d => {
-                        if (key != 'acl-Me') {
-                            const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]);
-                            const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl;
-                            const permission = HierarchyMapping.get(permissionSymbol)!.name;
-                            distributeAcls(key, permission, Doc.GetProto(d));
-                        }
-                    })
-                );
+                GetEffectiveAcl(this.rootDoc) === AclAdmin &&
+                    aclKeys.forEach(key =>
+                        added.forEach(d => {
+                            if (key != 'acl-Me') {
+                                const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]);
+                                const permissionSymbol = ReverseHierarchyMap.get(permissionString)?.acl;
+                                const permission = permissionSymbol && HierarchyMapping.get(permissionSymbol)?.name;
+                                distributeAcls(key, permission ?? SharingPermissions.Augment, Doc.GetProto(d));
+                            }
+                        })
+                    );
 
                 if (effectiveAcl === AclAugment) {
                     added.map(doc => {
-                        Doc.SetContainer(doc, this.props.Document);
-                        if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
+                        if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
                         Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
-                        const parent = DocCast(doc.embedContainer);
-                        doc.embedContainer && inheritParentAcls(parent, doc);
-                        for (const key of Object.keys(parent)) {
-                            const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
-                            if (symbol && key.startsWith('acl')) {
-                                const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
-                                const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0];
-                                if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc);
-                            }
-                        }
+                        Doc.SetContainer(doc, targetDataDoc);
+                        inheritParentAcls(targetDataDoc, doc);
                     });
                 } else {
                     added
                         .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)))
                         .map(doc => {
-                            // only make a pushpin if we have acl's to edit the document
-                            //DocUtils.LeavePushpin(doc);
                             doc._dragOnlyWithinContainer = undefined;
-                            Doc.SetContainer(doc, this.props.Document);
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
-                            const parent = DocCast(doc.embedContainer);
-                            doc.embedContainer && inheritParentAcls(parent, doc);
-                            for (const key of Object.keys(Doc.GetProto(parent))) {
-                                const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
-                                if (symbol && key.startsWith('acl')) {
-                                    const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
-                                    const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0];
-                                    if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc);
-                                }
-                            }
+                            Doc.SetContainer(doc, this.rootDoc);
+                            inheritParentAcls(targetDataDoc, doc);
                         });
 
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index cdd9e62d8..3522830e5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,7 +5,6 @@ import { IconButton } from 'browndash-components';
 import { action, computed, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
-import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
@@ -13,27 +12,27 @@ import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, GetEffectiveLayoutAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
-import { DocumentType } from '../documents/DocumentTypes';
+import { GetEffectiveAcl } from '../../fields/util';
+import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
+import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
 import { DragManager } from '../util/DragManager';
 import { LinkFollower } from '../util/LinkFollower';
 import { SelectionManager } from '../util/SelectionManager';
-import { SettingsManager } from '../util/SettingsManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
-import { InkStrokeProperties } from './InkStrokeProperties';
+import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
+import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { CollectionFreeFormView } from './collections/collectionFreeForm';
-import { Colors } from './global/globalEnums';
 import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
-import { ImageBox } from './nodes/ImageBox';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
 import _ = require('lodash');
 
@@ -165,7 +164,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     @action onContainerDown = (e: React.PointerEvent): void => {
         const first = SelectionManager.Views()[0];
-        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
         if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
@@ -179,7 +178,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     @action onTitleDown = (e: React.PointerEvent): void => {
         const first = SelectionManager.Views()[0];
-        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
         if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
@@ -200,7 +199,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     @action
     onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
         const first = SelectionManager.Views()[0];
-        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
         if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) {
             return false;
         }
@@ -496,7 +495,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
         const first = SelectionManager.Views()[0];
-        const effectiveAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc);
         if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false;
         if (!first) return false;
         let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
@@ -765,7 +764,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
 
         // sharing
-        const acl = this.showLayoutAcl ? GetEffectiveLayoutAcl(seldocview.rootDoc) : GetEffectiveAcl(seldocview.rootDoc);
+        const acl = GetEffectiveAcl(!this.showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc);
         const docShareMode = HierarchyMapping.get(acl)!.name;
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image;
@@ -773,7 +772,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
         const hideResizers =
-            ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveLayoutAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
+            ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
         const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
         const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 47dcdd2e4..347c40c18 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -285,13 +285,17 @@ export class KeyManager {
                 preventDefault = false;
                 break;
             case 'y':
-                SelectionManager.DeselectAll();
-                UndoManager.Redo();
+                if (Doc.ActivePage !== 'home') {
+                    SelectionManager.DeselectAll();
+                    UndoManager.Redo();
+                }
                 stopPropagation = false;
                 break;
             case 'z':
-                SelectionManager.DeselectAll();
-                UndoManager.Undo();
+                if (Doc.ActivePage !== 'home') {
+                    SelectionManager.DeselectAll();
+                    UndoManager.Undo();
+                }
                 stopPropagation = false;
                 break;
             case 'a':
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 258674d53..b708e2587 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -153,10 +153,12 @@ export class MainView extends React.Component {
         }
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields([
+            DocServer.setPlaygroundFields(['dockingConfig']);
+            DocServer.setLivePlaygroundFields([
                 'dataTransition',
                 'viewTransition',
                 'treeViewOpen',
+                'treeViewExpandedView',
                 'carousel_index',
                 'itemIndex', // for changing slides in presentations
                 'layout_sidebarWidthPercent',
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 14291b537..633401d58 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -395,9 +395,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             <div>
                 <div className={'propertiesView-shareDropDown'}>
                     <div className={`propertiesView-shareDropDown${permission}`}>
-                        <div className="propertiesView-shareDropDown">
-                            {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}
-                        </div>
+                        <div className="propertiesView-shareDropDown">{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}</div>
                     </div>
                 </div>
             </div>
@@ -425,11 +423,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @computed get sharingTable() {
         // all selected docs
-        const docs =
-            SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
+        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
         const target = docs[0];
 
-        const showAdmin = GetEffectiveAcl(target) == AclAdmin
+        const showAdmin = GetEffectiveAcl(target) == AclAdmin;
         const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
@@ -450,13 +447,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         usersAdded.sort(this.sortUsers);
         usersAdded.map(userEmail => {
             const userKey = `acl-${normalizeEmail(userEmail)}`;
-            var permission;
-            if (this.layoutDocAcls){
-                if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
-                else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
-                else permission = StrCast(Doc.GetProto(target)?.[userKey]);
-            }
-            else permission = StrCast(target[userKey]);
+            var permission = StrCast(target[userKey]);
             individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         });
 
@@ -466,12 +457,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (userEmail == 'guest') userEmail = 'Public';
         if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
             var permission;
-            if (this.layoutDocAcls){
+            if (this.layoutDocAcls) {
                 if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
                 else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
                 else permission = StrCast(Doc.GetProto(target)?.[userKey]);
-            }
-            else permission = StrCast(target[userKey]);
+            } else permission = StrCast(target[userKey]);
             individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         }
 
@@ -481,36 +471,29 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // adds groups
         const groupTableEntries: JSX.Element[] = [];
         const groupList = GroupManager.Instance?.allGroups || [];
-        groupList.sort(this.sortGroups)
+        groupList.sort(this.sortGroups);
         groupList.map(group => {
             if (group.title != 'Public' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
                     var permission;
-                    if (this.layoutDocAcls){
-                        if (target[DocAcl][groupKey]){
+                    if (this.layoutDocAcls) {
+                        if (target[DocAcl][groupKey]) {
                             permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name;
-                        }
-                        else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]);
+                        } else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]);
                         else permission = StrCast(Doc.GetProto(target)?.[groupKey]);
-                    }
-                    else permission = StrCast(target[groupKey]);
+                    } else permission = StrCast(target[groupKey]);
                     groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false));
                 }
             }
         });
 
         // public permission
-        let publicPermission = StrCast(target['acl-Public']);
-        if (this.layoutDocAcls){
-            if (target['acl-Public-layout']) publicPermission = StrCast(target['acl-Public-layout']);
-            else if (target['embedContainer']) publicPermission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))['acl-Public']);
-            else StrCast(Doc.GetProto(target)['acl-Public']);
-        }
+        const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Public']);
 
         return (
             <div>
-                <br/>
+                <br />
                 Public / Guest Users
                 <div>{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}</div>
                 <div>
@@ -518,7 +501,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     <br></br> Individual Users with Access to this Document{' '}
                 </div>
                 <div className="propertiesView-sharingTable">{<div> {individualTableEntries}</div>}</div>
-                {groupTableEntries.length>0 ? 
+                {groupTableEntries.length > 0 ? (
                     <div>
                         <div>
                             {' '}
@@ -526,7 +509,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         </div>
                         <div className="propertiesView-sharingTable">{<div> {groupTableEntries}</div>}</div>
                     </div>
-                : null}
+                ) : null}
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ce9eb9f17..0daa3dd92 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,7 +8,7 @@ import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
-import { distributeAcls, inheritParentAcls } from '../../../fields/util';
+import { distributeAcls, GetEffectiveAcl, GetPropAcl, inheritParentAcls } from '../../../fields/util';
 import { emptyFunction, incrementTitleCopy } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
@@ -30,7 +30,7 @@ import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { TabDocView } from './TabDocView';
 import React = require('react');
 import { DocumentManager } from '../../util/DocumentManager';
-import { DocAcl } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit, DocAcl } from '../../../fields/DocSymbols';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
@@ -386,8 +386,13 @@ export class CollectionDockingView extends CollectionSubView() {
                   .map(f => f as Doc);
         const changesMade = this.props.Document.dockingConfig !== json;
         if (changesMade) {
-            this.props.Document.dockingConfig = json;
-            this.props.Document.data = new List<Doc>(docs);
+            if (![AclAdmin, AclEdit].includes(GetEffectiveAcl(this.dataDoc))) {
+                this.layoutDoc.dockingConfig = json;
+                this.layoutDoc.data = new List<Doc>(docs);
+            } else {
+                Doc.SetInPlace(this.rootDoc, 'dockingConfig', json, true);
+                Doc.SetInPlace(this.rootDoc, 'data', new List<Doc>(docs), true);
+            }
         }
         this._flush?.end();
         this._flush = undefined;
@@ -521,7 +526,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     _layout_fitWidth: true,
                     title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                 });
-                this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
+                inheritParentAcls(this.rootDoc, docToAdd);
                 CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
             }
         });
@@ -564,7 +569,7 @@ export class CollectionDockingView extends CollectionSubView() {
                             _freeform_backgroundGrid: true,
                             title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                         });
-                        this.props.Document.isShared && inheritParentAcls(Doc.GetProto(this.props.Document), Doc.GetProto(docToAdd));
+                        inheritParentAcls(this.dataDoc, Doc.GetProto(docToAdd));
                         CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
                     }
                 })
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index a2269075d..7767c5b79 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -392,7 +392,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const innerAdd = (doc: Doc) => {
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
-                dataIsComputed && Doc.SetContainer(doc, this.doc.embedContainer);
+                dataIsComputed && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
                 return added;
             };
             return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -455,7 +455,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     const innerAdd = (doc: Doc) => {
                         const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                         const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                        dataIsComputed && Doc.SetContainer(doc, this.doc.embedContainer);
+                        dataIsComputed && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
                         return added;
                     };
                     return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -563,7 +563,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                !dataIsComputed && added && Doc.SetContainer(doc, this.doc.embedContainer);
+                !dataIsComputed && added && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
 
                 return added;
             };
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index dab269474..a4f5eb62b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -535,7 +535,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             // if this is part of a template, let the event go up to the template root unless right/ctrl clicking
             if (
                 // prettier-ignore
-                this.props.isDocumentActive?.() &&
+                (this.props.isDocumentActive?.() || this.props.isContentActive?.()) &&
                 !this.props.onBrowseClick?.() &&
                 !this.Document.ignoreClick &&
                 e.button === 0 &&
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 9c138d348..28fbdc192 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -481,7 +481,7 @@ export namespace Doc {
         doc.embedContainer = container;
         if (Doc.GetProto(container).author === doc.author) {
             Object.keys(Doc.GetProto(container))
-                .filter(key => key.startsWith('acl'))
+                .filter(key => key.startsWith('acl') && !key.includes(Doc.CurrentUserEmailNormalized))
                 .forEach(key => (doc[key] = Doc.GetProto(container)[key]));
         }
     }
@@ -696,7 +696,7 @@ export namespace Doc {
             Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
         }
         embedding.createdFrom = doc;
-        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = NumCast(Doc.GetProto(doc).proto_embeddingId) + 1;
+        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = DocListCast(Doc.GetProto(doc).proto_embeddings).length + 1;
         embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
         embedding.author = Doc.CurrentUserEmail;
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index fc0057827..9230d9df0 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -14,6 +14,7 @@ import { List } from './List';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
 import { RefField } from './RefField';
+import { RichTextField } from './RichTextField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
@@ -62,8 +63,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     const writeMode = DocServer.getFieldWriteMode(prop as string);
     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) && !DocServer.Control.isReadOnly();
+    const writeToDoc =
+        sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Playground || writeMode === DocServer.WriteMode.LivePlayground || (effectiveAcl === AclAugment && value instanceof RichTextField);
+    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclAugment && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
 
     if (writeToDoc) {
         if (value === undefined) {
@@ -104,7 +106,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
             );
         return true;
     }
-    return false;
+    return true;
 });
 
 let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl;
@@ -128,6 +130,7 @@ export function denormalizeEmail(email: string) {
  * Copies parent's acl fields to the child
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
+    if (GetEffectiveAcl(parent) !== AclAdmin) return;
     for (const key of Object.keys(parent)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
         // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
@@ -168,30 +171,16 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
     return getEffectiveAcl(target, user);
 }, true);
 
-// return layout acl from cache or chache the acl and return.
-const getEffectiveLayoutAclCache = computedFn(function (target: any, user?: string) {
-    return getEffectiveLayoutAcl(target, user);
-}, true);
-
 /**
  * Calculates the effective access right to a document for the current user.
  */
 export function GetEffectiveAcl(target: any, user?: string): symbol {
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
-    return getEffectiveAclCache(Doc.GetProto(target), user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
-}
-
-/**
- * Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set.
- */
-export function GetEffectiveLayoutAcl(target: any, user?: string): symbol {
-    if (!target) return AclPrivate;
-    if (target[UpdatingFromServer]) return AclAdmin;
-    return getEffectiveLayoutAclCache(target, user);
+    return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
 }
 
-function getPropAcl(target: any, prop: string | symbol | number) {
+export function GetPropAcl(target: any, prop: string | symbol | number) {
     if (typeof prop === 'symbol' || target[UpdatingFromServer]) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent
     if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable
     return GetEffectiveAcl(target);
@@ -233,37 +222,6 @@ function getEffectiveAcl(target: any, user?: string): symbol {
     return AclAdmin;
 }
 
-/**
- * Returns the layout acl that is effective on the document passed through as the target. If no layout acls
- * have been set, it returns the regular acls for the document target is contained in.
- */
-function getEffectiveLayoutAcl(target: any, user?: string): symbol {
-    const targetAcls = target[DocAcl];
-
-    const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
-    if (targetAcls && Object.keys(targetAcls).length) {
-        var effectiveAcl;
-        for (const [key, value] of Object.entries(targetAcls)) {
-            const entity = denormalizeEmail(key.substring(4)); // an individual or a group
-            if ((GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') && entity != 'Public') {
-                if (effectiveAcl && HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) {
-                    effectiveAcl = value as symbol;
-                } else {
-                    effectiveAcl = value as symbol;
-                }
-            }
-        }
-
-        if (effectiveAcl) {
-            return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
-        }
-    }
-    // authored documents are private until an ACL is set.
-    const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc or Proxy, so check __fieldTuples.author and .author
-    if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
-    return AclAdmin;
-}
-
 /**
  * Recursively distributes the access right for a user across the children of a document and its annotations.
  * @param key the key storing the access right (e.g. acl-groupname)
@@ -272,11 +230,13 @@ function getEffectiveLayoutAcl(target: any, user?: string): symbol {
  * @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the acls from the collection)
  * inheritingFromCollection is not currently being used but could be used if acl assignment defaults change
  */
-export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[], isDashboard?: boolean) {
+export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[]) {
     if (!visited) visited = [] as Doc[];
     if (!target || visited.includes(target)) return;
     if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
-        target[key] = acl;
+        if (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`) {
+            target[key] = acl;
+        }
         if (target !== Doc.GetProto(target)) {
             //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
             updateCachedAcls(target);
@@ -288,19 +248,16 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
     // if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive
     if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
-        target[key] = acl;
-        layoutDocChanged = true;
-        if (isDashboard) {
-            DocListCastAsync(target[Doc.LayoutFieldKey(target)]).then(docs => {
-                docs?.forEach(d => distributeAcls(key, acl, d, inheritingFromCollection, visited));
-            });
+        if (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`) {
+            target[key] = acl;
+            layoutDocChanged = true;
         }
     }
 
     let dataDocChanged = false;
     const dataDoc = target[DocData];
     if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) {
-        if (GetEffectiveAcl(dataDoc) === AclAdmin) {
+        if (GetEffectiveAcl(dataDoc) === AclAdmin && (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`)) {
             dataDoc[key] = acl;
             dataDocChanged = true;
         }
@@ -327,7 +284,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);
+    const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
     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 06cfe3cbba127d865e788b00561f8a591af1bd81 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 11 Jul 2023 15:32:08 -0400
Subject: more fixes to simplify sharing

---
 src/client/documents/Documents.ts                  | 31 ++++++------
 src/client/util/CurrentUserUtils.ts                |  6 +--
 src/client/util/DocumentManager.ts                 |  2 +-
 src/client/util/SharingManager.tsx                 |  9 ++--
 src/client/views/DashboardView.tsx                 | 15 +++---
 src/client/views/DocComponent.tsx                  | 15 +-----
 src/client/views/PropertiesView.tsx                | 55 ++++++++++------------
 .../views/collections/CollectionDockingView.tsx    |  4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  8 +---
 src/client/views/nodes/button/FontIconBox.tsx      |  2 +-
 src/client/views/search/SearchBox.tsx              |  2 +-
 src/fields/Doc.ts                                  | 16 ++-----
 src/fields/DocSymbols.ts                           |  1 -
 src/fields/util.ts                                 | 39 +++++++--------
 14 files changed, 88 insertions(+), 117 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f1ba852f9..4ebbfbd1c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -169,8 +169,8 @@ export class DocumentOptions {
     _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers');
     _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
 
-    'acl-Public'?: string; // public permissions
-    '_acl-Public'?: string; // public permissions
+    'acl-Guest'?: string; // public permissions
+    '_acl-Guest'?: string; // public permissions
     type?: DTYPEt = new DTypeInfo('type of document', true);
     type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection
     _type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection
@@ -825,8 +825,7 @@ export namespace Docs {
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
             // dataProps['acl-Override'] = SharingPermissions.Unset;
-            dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-
+            dataProps['acl-Guest'] = options['acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
             dataProps.isSystem = viewProps.isSystem;
             dataProps.isDataDoc = true;
             dataProps.author = Doc.CurrentUserEmail;
@@ -848,16 +847,17 @@ export namespace Docs {
                 dataDoc.proto = proto;
             }
 
-            const viewFirstProps: { [id: string]: any } = {};
-            // viewFirstProps['acl-Public'] = options['_acl-Public'] ? options['_acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-            // viewFirstProps['acl-Override'] = SharingPermissions.Unset;
-            viewFirstProps.author = Doc.CurrentUserEmail;
+            const viewFirstProps: { [id: string]: any } = { author: Doc.CurrentUserEmail };
+            viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
             let viewDoc: Doc;
             // determines whether viewDoc should be created using placeholder Doc or default
             if (placeholderDoc) {
                 placeholderDoc._height = options._height !== undefined ? Number(options._height) : undefined;
                 placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined;
                 viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
+                Array.from(Object.keys(placeholderDoc))
+                    .filter(key => key.startsWith('acl'))
+                    .forEach(key => (dataDoc[key] = viewDoc[key] = placeholderDoc[key]));
             } else {
                 viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
             }
@@ -997,7 +997,7 @@ export namespace Docs {
             I.rotation = 0;
             I.defaultDoubleClick = 'click';
             I.author_date = new DateField();
-            I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            I['acl-Guest'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View;
             //I['acl-Override'] = SharingPermissions.Unset;
             I[Initializing] = false;
 
@@ -1169,10 +1169,13 @@ export namespace Docs {
             const doc = DockDocument(
                 configs.map(c => c.doc),
                 JSON.stringify(layoutConfig),
-                options,
+                Doc.CurrentUserEmail === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options },
                 id
             );
-            configs.map(c => Doc.SetContainer(c.doc, doc));
+            configs.map(c => {
+                Doc.SetContainer(c.doc, doc);
+                inheritParentAcls(doc, c.doc, false);
+            });
             return doc;
         }
 
@@ -1332,8 +1335,8 @@ export namespace DocUtils {
                 source,
                 target,
                 {
-                    'acl-Public': SharingPermissions.Augment,
-                    '_acl-Public': SharingPermissions.Augment,
+                    'acl-Guest': SharingPermissions.Augment,
+                    '_acl-Guest': SharingPermissions.Augment,
                     title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
                     link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined,
                     link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined,
@@ -1854,8 +1857,6 @@ export namespace DocUtils {
             Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true);
         }
 
-        if (ndoc && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, ndoc);
-
         return ndoc;
     }
     export function delegateDragFactory(dragFactory: Doc) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 11a8dcaf6..2cee37380 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -765,7 +765,7 @@ export class CurrentUserUtils {
             linkDocs.title = "LINK DATABASE: " + Doc.CurrentUserEmail;
             linkDocs.author = Doc.CurrentUserEmail;
             linkDocs.data = new List<Doc>([]);
-            linkDocs["acl-Public"] = SharingPermissions.Augment;
+            linkDocs["acl-Guest"] = SharingPermissions.Augment;
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
         }
     }
@@ -788,7 +788,7 @@ export class CurrentUserUtils {
             childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
             childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             childContextMenuIcons: new List<string>(["user-plus",]),
-            "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
+            "acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment,
             childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
             // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
             _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
@@ -823,7 +823,7 @@ export class CurrentUserUtils {
             async () => {
                 const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
-                SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
+                SetCachedGroups(["Guest", ...mygroups?.map(g => StrCast(g.title))]);
             }, { fireImmediately: true });
         doc.isSystem ?? (doc.isSystem = true);
         doc.title ?? (doc.title = Doc.CurrentUserEmail);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 3f0848d00..b921b3116 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -194,7 +194,7 @@ export class DocumentManager {
         var containerDocContext = srcContext ? [srcContext, doc] : [doc];
         while (
             containerDocContext.length &&
-            containerDocContext[0]?.embedContainer &&
+            DocCast(containerDocContext[0]?.embedContainer) &&
             DocCast(containerDocContext[0].embedContainer)?._type_collection !== CollectionViewType.Docking &&
             (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0]))
         ) {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 37c813279..407396cb4 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -159,7 +159,7 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const acl = `acl-${normalizeEmail(user.email)}`;
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
-        docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+        docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => {
             distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested ? true : undefined);
             if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc);
             else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
@@ -176,7 +176,7 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${normalizeEmail(StrCast(group.title))}`;
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
-        docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+        docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => {
             distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested ? true : undefined);
 
             if (group instanceof Doc) {
@@ -218,7 +218,7 @@ export class SharingManager extends React.Component<{}> {
      */
     shareFromPropertiesSidebar = undoable((shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => {
         if (layout) this.layoutDocAcls = true;
-        if (shareWith !== 'Public' && shareWith !== 'Override') {
+        if (shareWith !== 'Guest') {
             const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith));
             docs.forEach(doc => {
                 if (user) this.setInternalSharing(user, permission, doc);
@@ -291,7 +291,6 @@ export class SharingManager extends React.Component<{}> {
     private sharingOptions(uniform: boolean, override?: boolean) {
         const dropdownValues: string[] = Object.values(SharingPermissions);
         if (!uniform) dropdownValues.unshift('-multiple-');
-        if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return dropdownValues.map(permission => (
             <option key={permission} value={permission}>
                 {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
@@ -504,7 +503,7 @@ export class SharingManager extends React.Component<{}> {
 
         // the list of groups shared with
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
-        groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
+        groupListMap.unshift({ title: 'Guest' }); //, { title: "ALL" });
         const groupListContents = groupListMap.map(group => {
             let groupKey = `acl-${StrCast(group.title)}`;
             const uniform = docs.every(doc => doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey]);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 123090fcf..9b16ecfa7 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,16 +1,17 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, ColorPicker, FontSize, IconButton, Size } from 'browndash-components';
+import { Button, ColorPicker, Size } from 'browndash-components';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { FaPlus } from 'react-icons/fa';
 import { Doc, DocListCast, DocListCastAsync } from '../../fields/Doc';
-import { AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
+import { AclPrivate, DocAcl } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { listSpec } from '../../fields/Schema';
 import { ScriptField } from '../../fields/ScriptField';
-import { Cast, DocCast, ImageCast, StrCast } from '../../fields/Types';
+import { Cast, ImageCast, StrCast } from '../../fields/Types';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions, DocUtils } from '../documents/Documents';
 import { CollectionViewType } from '../documents/DocumentTypes';
@@ -25,7 +26,6 @@ import './DashboardView.scss';
 import { Colors } from './global/globalEnums';
 import { MainViewModal } from './MainViewModal';
 import { ButtonType } from './nodes/button/FontIconBox';
-import { FaPlus } from 'react-icons/fa';
 
 enum DashboardGroup {
     MyDashboards,
@@ -167,8 +167,8 @@ export class DashboardView extends React.Component {
                         {this.getDashboards(this.selectedDashboardGroup).map(dashboard => {
                             const href = ImageCast(dashboard.thumb)?.url.href;
                             const shared = Object.keys(dashboard[DocAcl])
-                                .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Public'].includes(key))
-                                .some(key => ![AclUnset, AclPrivate].includes(dashboard[DocAcl][key]));
+                                .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]} style={{ background: shared ? 'lightgreen' : '' }} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
                                     <img
@@ -378,9 +378,6 @@ export class DashboardView extends React.Component {
         const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row');
 
-        // switching the tabs from the datadoc to the regular doc
-        const dashboardTabs = DocListCast(dashboardDoc[DocData].data);
-        dashboardDoc.data = new List<Doc>(dashboardTabs);
         dashboardDoc['pane-count'] = 1;
 
         Doc.AddDocToList(dashboards, 'data', dashboardDoc);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 10985c3f1..a41fc8ded 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -190,23 +190,12 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const added = docs;
             if (added.length) {
-                Object.keys(Doc.GetProto(this.rootDoc)[DocAcl]) // apply all collection acls (except pseudo-acl 'Me') to each added doc
-                    .filter(key => key !== 'acl-Me')
-                    .forEach(key =>
-                        added.forEach(d => {
-                            const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]);
-                            const permissionSymbol = ReverseHierarchyMap.get(permissionString)?.acl;
-                            const permission = permissionSymbol && HierarchyMapping.get(permissionSymbol)?.name;
-                            distributeAcls(key, permission ?? SharingPermissions.Augment, d, undefined, false);
-                        })
-                    );
-
                 if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) {
-                    added.map(doc => {
+                    added.forEach(doc => {
                         doc._dragOnlyWithinContainer = undefined;
                         if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
                         Doc.SetContainer(doc, this.rootDoc);
-                        inheritParentAcls(targetDataDoc, doc);
+                        inheritParentAcls(targetDataDoc, doc, true);
                     });
 
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 633401d58..2e10bf346 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -308,21 +308,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     /**
      * @returns the options for the permissions dropdown.
      */
-    getPermissionsSelect(user: string, permission: string) {
-        const dropdownValues: string[] = Object.values(SharingPermissions);
+    getPermissionsSelect(user: string, permission: string, showGuestOptions: boolean) {
+        const dropdownValues: string[] = showGuestOptions ? [SharingPermissions.None, SharingPermissions.View] : Object.values(SharingPermissions);
         if (permission === '-multiple-') dropdownValues.unshift(permission);
-        if (user !== 'Override') {
-            dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
-        }
         return (
             <select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
-                {dropdownValues
-                    .filter(permission => !Doc.noviceMode || ![SharingPermissions.View].includes(permission as any))
-                    .map(permission => (
-                        <option className="propertiesView-permisssions-select" key={permission} value={permission}>
-                            {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
-                        </option>
-                    ))}
+                {dropdownValues.map(permission => (
+                    <option className="propertiesView-permisssions-select" key={permission} value={permission}>
+                        {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
+                    </option>
+                ))}
             </select>
         );
     }
@@ -379,7 +374,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 </div>
                 {/* {name !== "Me" ? this.notifyIcon : null} */}
                 <div className="propertiesView-sharingTable-item-permission">
-                    {this.colorACLDropDown(name, admin, permission, showExpansionIcon)}
+                    {this.colorACLDropDown(name, admin, permission, false)}
                     {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null}
                 </div>
             </div>
@@ -389,13 +384,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     /**
      * @returns a colored dropdown bar reflective of the permission
      */
-    colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
+    colorACLDropDown(name: string, admin: boolean, permission: string, showGuestOptions: boolean) {
         var shareImage = ReverseHierarchyMap.get(permission)?.image;
         return (
             <div>
                 <div className={'propertiesView-shareDropDown'}>
                     <div className={`propertiesView-shareDropDown${permission}`}>
-                        <div className="propertiesView-shareDropDown">{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}</div>
+                        <div className="propertiesView-shareDropDown">{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
                     </div>
                 </div>
             </div>
@@ -430,15 +425,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
+        const seldoc = this.layoutDocAcls || !this.selectedDoc ? this.selectedDoc : Doc.GetProto(this.selectedDoc);
         // adds each user to usersAdded
         SharingManager.Instance.users.forEach(eachUser => {
             var userOnDoc = true;
-            if (this.selectedDoc) {
-                if (this.selectedDoc['acl-' + normalizeEmail(eachUser.user.email)] == '' || this.selectedDoc['acl-' + normalizeEmail(eachUser.user.email)] == undefined) {
+            if (seldoc) {
+                if (Doc.GetT(seldoc, 'acl-' + normalizeEmail(eachUser.user.email), 'string', true) === '' || Doc.GetT(seldoc, 'acl-' + normalizeEmail(eachUser.user.email), 'string', true) === undefined) {
                     userOnDoc = false;
                 }
             }
-            if (userOnDoc && !usersAdded.includes(eachUser.user.email) && eachUser.user.email != 'Public' && eachUser.user.email != target.author) {
+            if (userOnDoc && !usersAdded.includes(eachUser.user.email) && eachUser.user.email !== 'guest' && eachUser.user.email != target.author) {
                 usersAdded.push(eachUser.user.email);
             }
         });
@@ -447,15 +443,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         usersAdded.sort(this.sortUsers);
         usersAdded.map(userEmail => {
             const userKey = `acl-${normalizeEmail(userEmail)}`;
-            var permission = StrCast(target[userKey]);
+            var aclField = Doc.GetT(this.layoutDocAcls ? target : Doc.GetProto(target), userKey, 'string', true);
+            var permission = StrCast(aclField);
             individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         });
 
         // adds current user
         var userEmail = Doc.CurrentUserEmail;
+        if (userEmail == 'guest') userEmail = 'Guest';
         const userKey = `acl-${normalizeEmail(userEmail)}`;
-        if (userEmail == 'guest') userEmail = 'Public';
-        if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
+        if (!usersAdded.includes(userEmail) && userEmail !== 'Guest' && userEmail != target.author) {
             var permission;
             if (this.layoutDocAcls) {
                 if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
@@ -473,7 +470,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const groupList = GroupManager.Instance?.allGroups || [];
         groupList.sort(this.sortGroups);
         groupList.map(group => {
-            if (group.title != 'Public' && this.selectedDoc) {
+            if (group.title != 'Guest' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
                     var permission;
@@ -489,17 +486,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         });
 
         // public permission
-        const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Public']);
+        const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Guest']);
 
         return (
             <div>
                 <br />
-                Public / Guest Users
-                <div>{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}</div>
-                <div>
-                    {' '}
-                    <br></br> Individual Users with Access to this Document{' '}
-                </div>
                 <div className="propertiesView-sharingTable">{<div> {individualTableEntries}</div>}</div>
                 {groupTableEntries.length > 0 ? (
                     <div>
@@ -510,6 +501,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         <div className="propertiesView-sharingTable">{<div> {groupTableEntries}</div>}</div>
                     </div>
                 ) : null}
+                Guest
+                <div>{this.colorACLDropDown('Guest', true, publicPermission!, true)}</div>
+                <div>
+                    {' '}
+                    <br></br> Individual Users with Access to this Document{' '}
+                </div>
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 13d3b2cdc..8d1b46ebb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -517,7 +517,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     _layout_fitWidth: true,
                     title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                 });
-                inheritParentAcls(this.rootDoc, docToAdd);
+                inheritParentAcls(this.rootDoc, docToAdd, false);
                 CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
             }
         });
@@ -560,7 +560,7 @@ export class CollectionDockingView extends CollectionSubView() {
                             _freeform_backgroundGrid: true,
                             title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                         });
-                        inheritParentAcls(this.dataDoc, Doc.GetProto(docToAdd));
+                        inheritParentAcls(this.dataDoc, docToAdd, false);
                         CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
                     }
                 })
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 764b1e08a..e2718b52d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -9,7 +9,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
 import { Cast, DocCast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { GetEffectiveAcl, SharingPermissions } from '../../../../fields/util';
+import { distributeAcls, GetEffectiveAcl, SharingPermissions } from '../../../../fields/util';
 import { intersectRect, returnFalse, Utils } from '../../../../Utils';
 import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
 import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
@@ -390,11 +390,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
         newCollection.layout_fitWidth = true;
-        newCollection['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-        selected.forEach(d => {
-            Doc.SetContainer(d, newCollection);
-            d['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-        });
+        selected.forEach(d => Doc.SetContainer(d, newCollection));
         this.hideMarquee();
         return newCollection;
     });
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 5bba51ec8..a0b0caf98 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -597,7 +597,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
             if (contentFrameNumber !== undefined) {
                 CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color });
             } else {
-                dv.rootDoc['_' + fieldKey] = color;
+                dv.dataDoc[fieldKey] = color;
             }
         });
     } else {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index d13c09443..ab9eebd78 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -222,7 +222,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             'width',
             'layout_autoHeight',
             'acl-Override',
-            'acl-Public',
+            'acl-Guest',
             'embedContainer',
             'zIndex',
             'height',
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 698e09915..6121668e3 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -20,7 +20,6 @@ import {
     AclEdit,
     AclPrivate,
     AclReadonly,
-    AclUnset,
     Animation,
     CachedUpdates,
     DirectLinks,
@@ -134,7 +133,6 @@ export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermis
     [AclAugment,  { level: aclLevel.augmentable,  name: SharingPermissions.Augment, image: '⬟' }],
     [AclEdit,     { level: aclLevel.editable,     name: SharingPermissions.Edit,    image: '⬢' }],
     [AclAdmin,    { level: aclLevel.admin,        name: SharingPermissions.Admin,   image: '⬢' }],
-    [AclUnset,    { level: aclLevel.unset,        name: SharingPermissions.Unset,   image: '▲' }],
 ]);
 export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol; image: string }> = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0], image: value[1].image }]));
 
@@ -380,7 +378,8 @@ export class Doc extends RefField {
         return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
     }
     @computed get __LAYOUT__(): Doc | undefined {
-        const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
+        const self = this[SelfProxy];
+        const templateLayoutDoc = Cast(Doc.LayoutField(self), Doc, null);
         if (templateLayoutDoc) {
             let renderFieldKey: any;
             const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
@@ -389,7 +388,7 @@ export class Doc extends RefField {
             } else {
                 return Cast(layoutField, Doc, null);
             }
-            return Cast(this[SelfProxy][renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
+            return Cast(self[renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
         }
         return undefined;
     }
@@ -480,11 +479,6 @@ export namespace Doc {
 
     export function SetContainer(doc: Doc, container: Doc) {
         doc.embedContainer = container;
-        if (Doc.GetProto(container).author === doc.author) {
-            Object.keys(Doc.GetProto(container))
-                .filter(key => key.startsWith('acl') && !key.includes(Doc.CurrentUserEmailNormalized))
-                .forEach(key => (doc[key] = Doc.GetProto(container)[key]));
-        }
     }
     export function RunCachedUpdate(doc: Doc, field: string) {
         const update = doc[CachedUpdates][field];
@@ -697,7 +691,7 @@ export namespace Doc {
             Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
         }
         embedding.createdFrom = doc;
-        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = DocListCast(Doc.GetProto(doc).proto_embeddings).length + 1;
+        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = DocListCast(Doc.GetProto(doc).proto_embeddings).length - 1;
         embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
         embedding.author = Doc.CurrentUserEmail;
 
@@ -1027,7 +1021,6 @@ export namespace Doc {
             Doc.AddDocToList(Doc.GetProto(copy)[DocData], 'proto_embeddings', copy);
         }
         copy.embedContainer = undefined;
-        Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared');
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
         }
@@ -1087,7 +1080,6 @@ export namespace Doc {
             const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')');
             target.layout_fieldKey = targetKey;
             applied && (Doc.GetProto(applied).type = templateDoc.type);
-            Doc.defaultAclPrivate && (applied['acl-Public'] = 'Not Shared');
             return applied;
         }
         return undefined;
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index eab26ed10..66d1ab094 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -13,7 +13,6 @@ export const DocFields = Symbol('DocFields');
 export const DocCss = Symbol('DocCss');
 export const DocAcl = Symbol('DocAcl');
 export const DirectLinks = Symbol('DocDirectLinks');
-export const AclUnset = Symbol('DocAclUnset');
 export const AclPrivate = Symbol('DocAclOwnerOnly');
 export const AclReadonly = Symbol('DocAclReadOnly');
 export const AclAugment = Symbol('DocAclAugment');
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 034229319..e89cb1fb1 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -131,17 +131,19 @@ export function denormalizeEmail(email: string) {
 /**
  * Copies parent's acl fields to the child
  */
-export function inheritParentAcls(parent: Doc, child: Doc) {
+export function inheritParentAcls(parent: Doc, child: Doc, layoutOnly: boolean) {
     if (GetEffectiveAcl(parent) !== AclAdmin) return;
-    for (const key of Object.keys(parent)) {
-        // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
-        const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
-        if (symbol) {
-            const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
-            key.startsWith('acl') && distributeAcls(key, sharePermission, child);
-        }
-    }
+    Object.keys(parent)
+        .filter(key => key.startsWith('acl'))
+        .forEach(key => {
+            // if the default acl mode is private, then don't inherit the acl-guest permission, but set it to private.
+            // const permission: string = key === 'acl-guest' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
+            const parAcl = ReverseHierarchyMap.get(StrCast(parent[key]))?.acl;
+            if (parAcl) {
+                const sharePermission = HierarchyMapping.get(parAcl)?.name;
+                sharePermission && distributeAcls(key, sharePermission, child, undefined, false, layoutOnly);
+            }
+        });
 }
 
 /**
@@ -160,7 +162,6 @@ export function inheritParentAcls(parent: Doc, child: Doc) {
  * Unset: Remove a sharing permission (eg., used )
  */
 export enum SharingPermissions {
-    Unset = 'None',
     Admin = 'Admin',
     Edit = 'Edit',
     Augment = 'Augment',
@@ -233,7 +234,7 @@ function getEffectiveAcl(target: any, user?: string): symbol {
  * @param allowUpgrade whether permissions can be made less restrictive
  * inheritingFromCollection is not currently being used but could be used if acl assignment defaults change
  */
-export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited?: Doc[], allowUpgrade?: boolean) {
+export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited?: Doc[], allowUpgrade?: boolean, layoutOnly = false) {
     const selfKey = `acl-${Doc.CurrentUserEmailNormalized}`;
     if (!visited) visited = [] as Doc[];
     if (!target || visited.includes(target) || key === selfKey) return;
@@ -243,19 +244,19 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     const dataDoc = target[DocData];
     const curVal = ReverseHierarchyMap.get(StrCast(dataDoc[key]))?.level ?? 0;
     const aclVal = ReverseHierarchyMap.get(acl)?.level ?? 0;
-    if (dataDoc && (allowUpgrade !== false|| !dataDoc[key] || curVal > aclVal)) {
+    if (!layoutOnly && dataDoc && (allowUpgrade !== false || !dataDoc[key] || curVal > aclVal)) {
         // propagate ACLs to links, children, and annotations
 
-        LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade? true: false));
+        LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade ? true : false));
 
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
-            distributeAcls(key, acl, d, visited, allowUpgrade ? true: false);
-            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true: false);
+            distributeAcls(key, acl, d, visited, allowUpgrade ? true : false);
+            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true : false);
         });
 
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
-            distributeAcls(key, acl, d, visited, allowUpgrade? true: false);
-            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade? true: false);
+            distributeAcls(key, acl, d, visited, allowUpgrade ? true : false);
+            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true : false);
         });
 
         if (GetEffectiveAcl(dataDoc) === AclAdmin) {
@@ -266,7 +267,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 
     let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
     // if it is inheriting from a collection, it only inherits if A) allowUpgrade is set B) the key doesn't already exist or c) the right being inherited is more restrictive
-    if (GetEffectiveAcl(target) === AclAdmin && (allowUpgrade || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
+    if (GetEffectiveAcl(target) === AclAdmin && (allowUpgrade || !Doc.GetT(target, key, 'boolean', true) || ReverseHierarchyMap.get(StrCast(target[key]))!.level > aclVal)) {
         target[key] = acl;
         layoutDocChanged = true;
     }
-- 
cgit v1.2.3-70-g09d2


From 54308259a8cd3ac98aaee550ea01ad97f17172e6 Mon Sep 17 00:00:00 2001
From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com>
Date: Mon, 17 Jul 2023 23:11:35 -0400
Subject: so many updates

---
 deploy/index.html                                  |   7 +-
 deploy/loader.css                                  |   5 +-
 package-lock.json                                  | 232 +++----
 package.json                                       |   2 +-
 src/client/documents/Documents.ts                  |   6 +-
 src/client/util/CurrentUserUtils.ts                |  63 +-
 src/client/util/DropConverter.ts                   |   2 +-
 src/client/util/ReportManager.tsx                  |   4 +-
 src/client/util/SettingsManager.scss               |  93 +--
 src/client/util/SettingsManager.tsx                | 186 ++++--
 src/client/views/AntimodeMenu.scss                 |  32 +-
 src/client/views/AntimodeMenu.tsx                  |   5 +
 src/client/views/DashboardView.scss                |  22 +
 src/client/views/DashboardView.tsx                 |  98 ++-
 src/client/views/EditableView.tsx                  |  75 ++-
 src/client/views/GestureOverlay.tsx                |   2 +-
 src/client/views/MainView.scss                     |  20 -
 src/client/views/MainView.tsx                      |  13 +-
 src/client/views/MainViewModal.scss                |   6 +-
 src/client/views/MainViewModal.tsx                 |   7 +-
 src/client/views/PropertiesButtons.scss            |   8 +-
 src/client/views/PropertiesButtons.tsx             |  98 ++-
 .../views/PropertiesDocBacklinksSelector.scss      |   2 +-
 .../views/PropertiesDocBacklinksSelector.tsx       |   2 +-
 src/client/views/PropertiesDocContextSelector.tsx  |   1 +
 src/client/views/PropertiesSection.scss            |  24 +
 src/client/views/PropertiesSection.tsx             |  51 ++
 src/client/views/PropertiesView.scss               | 364 +---------
 src/client/views/PropertiesView.tsx                | 743 ++++++++++-----------
 src/client/views/StyleProvider.tsx                 |  10 +-
 src/client/views/UndoStack.scss                    |   9 +-
 src/client/views/UndoStack.tsx                     |  48 +-
 src/client/views/collections/CollectionMenu.scss   | 651 +-----------------
 src/client/views/collections/CollectionMenu.tsx    |  51 +-
 .../views/collections/CollectionTreeView.tsx       |   2 +
 src/client/views/collections/TabDocView.scss       |  17 -
 src/client/views/collections/TabDocView.tsx        | 103 ++-
 src/client/views/collections/TreeView.scss         |  36 +-
 src/client/views/collections/TreeView.tsx          |  35 +-
 .../collectionFreeForm/MarqueeOptionsMenu.tsx      |  69 +-
 .../collectionLinear/CollectionLinearView.tsx      |  51 +-
 src/client/views/global/globalScripts.ts           |  49 +-
 src/client/views/linking/LinkPopup.scss            |   5 -
 src/client/views/linking/LinkPopup.tsx             |   4 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   2 +-
 src/client/views/nodes/DocumentView.tsx            |   4 +-
 .../views/nodes/FontIconBox/ButtonInterface.ts     |  12 +
 .../views/nodes/FontIconBox/FontIconBadge.scss     |  11 +
 .../views/nodes/FontIconBox/FontIconBadge.tsx      |  37 +
 .../views/nodes/FontIconBox/FontIconBox.scss       | 438 ++++++++++++
 src/client/views/nodes/FontIconBox/FontIconBox.tsx | 412 ++++++++++++
 src/client/views/nodes/button/FontIconBox.scss     | 438 ------------
 src/client/views/nodes/button/FontIconBox.tsx      | 385 -----------
 src/client/views/pdf/AnchorMenu.tsx                | 121 ++--
 src/client/views/search/SearchBox.scss             |   3 +-
 src/client/views/search/SearchBox.tsx              |   7 +-
 src/client/views/selectedDoc/SelectedDocView.scss  |   3 +
 src/client/views/selectedDoc/SelectedDocView.tsx   |  47 ++
 src/client/views/selectedDoc/index.ts              |   1 +
 src/client/views/topbar/TopBar.tsx                 |  36 +-
 60 files changed, 2279 insertions(+), 2991 deletions(-)
 create mode 100644 src/client/views/PropertiesSection.scss
 create mode 100644 src/client/views/PropertiesSection.tsx
 create mode 100644 src/client/views/nodes/FontIconBox/ButtonInterface.ts
 create mode 100644 src/client/views/nodes/FontIconBox/FontIconBadge.scss
 create mode 100644 src/client/views/nodes/FontIconBox/FontIconBadge.tsx
 create mode 100644 src/client/views/nodes/FontIconBox/FontIconBox.scss
 create mode 100644 src/client/views/nodes/FontIconBox/FontIconBox.tsx
 delete mode 100644 src/client/views/nodes/button/FontIconBox.scss
 delete mode 100644 src/client/views/nodes/button/FontIconBox.tsx
 create mode 100644 src/client/views/selectedDoc/SelectedDocView.scss
 create mode 100644 src/client/views/selectedDoc/SelectedDocView.tsx
 create mode 100644 src/client/views/selectedDoc/index.ts

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

diff --git a/deploy/index.html b/deploy/index.html
index f8003f126..c8ad2a8d4 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -5,7 +5,7 @@
     <link rel="preconnect" href="https://fonts.googleapis.com">
     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
     <link
-        href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&family=Lato:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&family=Merriweather:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&family=Roboto:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"
+        href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&family=Lato:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&family=Merriweather:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&family=Roboto:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&family=Nunito:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;0,1000;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900;1,1000&family=Roboto+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
         rel="stylesheet">
     <link rel="shortcut icon" type="image/jpg" href="./assets/favicon.png" />
     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
@@ -67,8 +67,9 @@
         }
 
         .dash-animation-container {
-            width: 10vw;
-            height: 10vw;
+            width: fit-content;
+            height: fit-content;
+            padding: 20px;
             display: flex;
             align-items: center;
             justify-content: center;
diff --git a/deploy/loader.css b/deploy/loader.css
index 4be0cc98c..4d5e04d71 100644
--- a/deploy/loader.css
+++ b/deploy/loader.css
@@ -36,8 +36,9 @@
 }
 
 .dash-animation-container {
-    width: 10vw;
-    height: 10vw;
+    width: fit-content;
+    height: fit-content;
+    padding: 20px;
     display: flex;
     align-items: center;
     justify-content: center;
diff --git a/package-lock.json b/package-lock.json
index 84c1e6219..215468914 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,36 +22,42 @@
             }
         },
         "@babel/compat-data": {
-            "version": "7.22.6",
-            "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz",
-            "integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg=="
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz",
+            "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ=="
         },
         "@babel/core": {
-            "version": "7.22.8",
-            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.8.tgz",
-            "integrity": "sha512-75+KxFB4CZqYRXjx4NlR4J7yGvKumBuZTmV4NV6v09dVXXkuYVYLT68N6HCzLvfJ+fWCxQsntNzKwwIXL4bHnw==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz",
+            "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==",
             "requires": {
                 "@ampproject/remapping": "^2.2.0",
                 "@babel/code-frame": "^7.22.5",
-                "@babel/generator": "^7.22.7",
-                "@babel/helper-compilation-targets": "^7.22.6",
-                "@babel/helper-module-transforms": "^7.22.5",
+                "@babel/generator": "^7.22.9",
+                "@babel/helper-compilation-targets": "^7.22.9",
+                "@babel/helper-module-transforms": "^7.22.9",
                 "@babel/helpers": "^7.22.6",
                 "@babel/parser": "^7.22.7",
                 "@babel/template": "^7.22.5",
                 "@babel/traverse": "^7.22.8",
                 "@babel/types": "^7.22.5",
-                "@nicolo-ribaudo/semver-v6": "^6.3.3",
                 "convert-source-map": "^1.7.0",
                 "debug": "^4.1.0",
                 "gensync": "^1.0.0-beta.2",
-                "json5": "^2.2.2"
+                "json5": "^2.2.2",
+                "semver": "^6.3.1"
             },
             "dependencies": {
-                "@nicolo-ribaudo/semver-v6": {
-                    "version": "6.3.3",
-                    "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz",
-                    "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg=="
+                "@babel/generator": {
+                    "version": "7.22.9",
+                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz",
+                    "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==",
+                    "requires": {
+                        "@babel/types": "^7.22.5",
+                        "@jridgewell/gen-mapping": "^0.3.2",
+                        "@jridgewell/trace-mapping": "^0.3.17",
+                        "jsesc": "^2.5.1"
+                    }
                 },
                 "debug": {
                     "version": "4.3.4",
@@ -70,6 +76,11 @@
                     "version": "2.1.2",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
                     "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+                },
+                "semver": {
+                    "version": "6.3.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+                    "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
                 }
             }
         },
@@ -101,22 +112,17 @@
             }
         },
         "@babel/helper-compilation-targets": {
-            "version": "7.22.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz",
-            "integrity": "sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz",
+            "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==",
             "requires": {
-                "@babel/compat-data": "^7.22.6",
+                "@babel/compat-data": "^7.22.9",
                 "@babel/helper-validator-option": "^7.22.5",
-                "@nicolo-ribaudo/semver-v6": "^6.3.3",
                 "browserslist": "^4.21.9",
-                "lru-cache": "^5.1.1"
+                "lru-cache": "^5.1.1",
+                "semver": "^6.3.1"
             },
             "dependencies": {
-                "@nicolo-ribaudo/semver-v6": {
-                    "version": "6.3.3",
-                    "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz",
-                    "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg=="
-                },
                 "lru-cache": {
                     "version": "5.1.1",
                     "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -125,6 +131,11 @@
                         "yallist": "^3.0.2"
                     }
                 },
+                "semver": {
+                    "version": "6.3.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+                    "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
+                },
                 "yallist": {
                     "version": "3.1.1",
                     "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -133,19 +144,19 @@
             }
         },
         "@babel/helper-create-class-features-plugin": {
-            "version": "7.22.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.6.tgz",
-            "integrity": "sha512-iwdzgtSiBxF6ni6mzVnZCF3xt5qE6cEA0J7nFt8QOAWZ0zjCFceEgpn3vtb2V7WFR6QzP2jmIFOHMTRo7eNJjQ==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz",
+            "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==",
             "requires": {
                 "@babel/helper-annotate-as-pure": "^7.22.5",
                 "@babel/helper-environment-visitor": "^7.22.5",
                 "@babel/helper-function-name": "^7.22.5",
                 "@babel/helper-member-expression-to-functions": "^7.22.5",
                 "@babel/helper-optimise-call-expression": "^7.22.5",
-                "@babel/helper-replace-supers": "^7.22.5",
+                "@babel/helper-replace-supers": "^7.22.9",
                 "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
                 "@babel/helper-split-export-declaration": "^7.22.6",
-                "@nicolo-ribaudo/semver-v6": "^6.3.3"
+                "semver": "^6.3.1"
             },
             "dependencies": {
                 "@babel/helper-annotate-as-pure": {
@@ -156,21 +167,21 @@
                         "@babel/types": "^7.22.5"
                     }
                 },
-                "@nicolo-ribaudo/semver-v6": {
-                    "version": "6.3.3",
-                    "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz",
-                    "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg=="
+                "semver": {
+                    "version": "6.3.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+                    "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
                 }
             }
         },
         "@babel/helper-create-regexp-features-plugin": {
-            "version": "7.22.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.6.tgz",
-            "integrity": "sha512-nBookhLKxAWo/TUCmhnaEJyLz2dekjQvv5SRpE9epWQBcpedWLKt8aZdsuT9XV5ovzR3fENLjRXVT0GsSlGGhA==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz",
+            "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==",
             "requires": {
                 "@babel/helper-annotate-as-pure": "^7.22.5",
-                "@nicolo-ribaudo/semver-v6": "^6.3.3",
-                "regexpu-core": "^5.3.1"
+                "regexpu-core": "^5.3.1",
+                "semver": "^6.3.1"
             },
             "dependencies": {
                 "@babel/helper-annotate-as-pure": {
@@ -181,10 +192,10 @@
                         "@babel/types": "^7.22.5"
                     }
                 },
-                "@nicolo-ribaudo/semver-v6": {
-                    "version": "6.3.3",
-                    "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz",
-                    "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg=="
+                "semver": {
+                    "version": "6.3.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+                    "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
                 }
             }
         },
@@ -264,18 +275,15 @@
             }
         },
         "@babel/helper-module-transforms": {
-            "version": "7.22.5",
-            "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz",
-            "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz",
+            "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==",
             "requires": {
                 "@babel/helper-environment-visitor": "^7.22.5",
                 "@babel/helper-module-imports": "^7.22.5",
                 "@babel/helper-simple-access": "^7.22.5",
-                "@babel/helper-split-export-declaration": "^7.22.5",
-                "@babel/helper-validator-identifier": "^7.22.5",
-                "@babel/template": "^7.22.5",
-                "@babel/traverse": "^7.22.5",
-                "@babel/types": "^7.22.5"
+                "@babel/helper-split-export-declaration": "^7.22.6",
+                "@babel/helper-validator-identifier": "^7.22.5"
             }
         },
         "@babel/helper-optimise-call-expression": {
@@ -292,14 +300,13 @@
             "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ=="
         },
         "@babel/helper-remap-async-to-generator": {
-            "version": "7.22.5",
-            "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz",
-            "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz",
+            "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==",
             "requires": {
                 "@babel/helper-annotate-as-pure": "^7.22.5",
                 "@babel/helper-environment-visitor": "^7.22.5",
-                "@babel/helper-wrap-function": "^7.22.5",
-                "@babel/types": "^7.22.5"
+                "@babel/helper-wrap-function": "^7.22.9"
             },
             "dependencies": {
                 "@babel/helper-annotate-as-pure": {
@@ -313,16 +320,13 @@
             }
         },
         "@babel/helper-replace-supers": {
-            "version": "7.22.5",
-            "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz",
-            "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz",
+            "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==",
             "requires": {
                 "@babel/helper-environment-visitor": "^7.22.5",
                 "@babel/helper-member-expression-to-functions": "^7.22.5",
-                "@babel/helper-optimise-call-expression": "^7.22.5",
-                "@babel/template": "^7.22.5",
-                "@babel/traverse": "^7.22.5",
-                "@babel/types": "^7.22.5"
+                "@babel/helper-optimise-call-expression": "^7.22.5"
             }
         },
         "@babel/helper-simple-access": {
@@ -365,13 +369,12 @@
             "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw=="
         },
         "@babel/helper-wrap-function": {
-            "version": "7.22.5",
-            "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz",
-            "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz",
+            "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==",
             "requires": {
                 "@babel/helper-function-name": "^7.22.5",
                 "@babel/template": "^7.22.5",
-                "@babel/traverse": "^7.22.5",
                 "@babel/types": "^7.22.5"
             }
         },
@@ -1472,12 +1475,12 @@
             }
         },
         "@babel/plugin-transform-typescript": {
-            "version": "7.22.5",
-            "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz",
-            "integrity": "sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz",
+            "integrity": "sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg==",
             "requires": {
                 "@babel/helper-annotate-as-pure": "^7.22.5",
-                "@babel/helper-create-class-features-plugin": "^7.22.5",
+                "@babel/helper-create-class-features-plugin": "^7.22.9",
                 "@babel/helper-plugin-utils": "^7.22.5",
                 "@babel/plugin-syntax-typescript": "^7.22.5"
             },
@@ -1561,12 +1564,12 @@
             }
         },
         "@babel/preset-env": {
-            "version": "7.22.7",
-            "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.7.tgz",
-            "integrity": "sha512-1whfDtW+CzhETuzYXfcgZAh8/GFMeEbz0V5dVgya8YeJyCU6Y/P2Gnx4Qb3MylK68Zu9UiwUvbPMPTpFAOJ+sQ==",
+            "version": "7.22.9",
+            "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz",
+            "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==",
             "requires": {
-                "@babel/compat-data": "^7.22.6",
-                "@babel/helper-compilation-targets": "^7.22.6",
+                "@babel/compat-data": "^7.22.9",
+                "@babel/helper-compilation-targets": "^7.22.9",
                 "@babel/helper-plugin-utils": "^7.22.5",
                 "@babel/helper-validator-option": "^7.22.5",
                 "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5",
@@ -1640,11 +1643,11 @@
                 "@babel/plugin-transform-unicode-sets-regex": "^7.22.5",
                 "@babel/preset-modules": "^0.1.5",
                 "@babel/types": "^7.22.5",
-                "@nicolo-ribaudo/semver-v6": "^6.3.3",
                 "babel-plugin-polyfill-corejs2": "^0.4.4",
                 "babel-plugin-polyfill-corejs3": "^0.8.2",
                 "babel-plugin-polyfill-regenerator": "^0.5.1",
-                "core-js-compat": "^3.31.0"
+                "core-js-compat": "^3.31.0",
+                "semver": "^6.3.1"
             },
             "dependencies": {
                 "@babel/helper-plugin-utils": {
@@ -1652,10 +1655,10 @@
                     "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
                     "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg=="
                 },
-                "@nicolo-ribaudo/semver-v6": {
-                    "version": "6.3.3",
-                    "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz",
-                    "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg=="
+                "semver": {
+                    "version": "6.3.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+                    "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
                 }
             }
         },
@@ -6195,9 +6198,9 @@
             }
         },
         "browndash-components": {
-            "version": "0.0.62",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.62.tgz",
-            "integrity": "sha512-lhIuqOylZF7y4ZQXOdTim2r8Et61AaUROl5UYaooCObHvjJb7WIGpFv9B1RQjlbg8vUJ4jeM3e3qb++Cf6ajPg==",
+            "version": "0.0.76",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.76.tgz",
+            "integrity": "sha512-2ZrDNGxbXnmqRSBifCn6+czz/LqeuyZwSsnRj73Wmo9OOKvYIneGZE17/aFkAl76rXdVmZCcfBdKKBcwEYwNHQ==",
             "requires": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
@@ -6211,9 +6214,9 @@
             },
             "dependencies": {
                 "@babel/cli": {
-                    "version": "7.22.6",
-                    "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.22.6.tgz",
-                    "integrity": "sha512-Be3/RfEDmkMRGT1+ru5nTkfcvWz5jDOYg1V9rXqTz2u9Qt96O1ryboGvxVBp7wOnYWDB8DNHIWb6DThrpudfOw==",
+                    "version": "7.22.9",
+                    "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.22.9.tgz",
+                    "integrity": "sha512-nb2O7AThqRo7/E53EGiuAkMaRbb7J5Qp3RvN+dmua1U+kydm0oznkhqbTEG15yk26G/C3yL6OdZjzgl+DMXVVA==",
                     "requires": {
                         "@jridgewell/trace-mapping": "^0.3.17",
                         "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
@@ -6642,9 +6645,9 @@
                     "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
                 },
                 "postcss": {
-                    "version": "8.4.25",
-                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz",
-                    "integrity": "sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==",
+                    "version": "8.4.26",
+                    "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz",
+                    "integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==",
                     "requires": {
                         "nanoid": "^3.3.6",
                         "picocolors": "^1.0.0",
@@ -6676,9 +6679,9 @@
                     "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="
                 },
                 "styled-components": {
-                    "version": "6.0.3",
-                    "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.3.tgz",
-                    "integrity": "sha512-qEyWvDK4CYCyDckNIruRJIcQSvcUR3dVEw/fwxu1v0LFzUMPr2uf5PhXHp17FkGK+S4TkglOS+XIealo1MssQA==",
+                    "version": "6.0.4",
+                    "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.0.4.tgz",
+                    "integrity": "sha512-lRJt4vg8hKJhlVG+VKz8QEqPCXKyTryZZ59odyK0UC0HHV3u/mshWTfSay8NpkN0Xijw1iN9r0Leld3dcCcp/w==",
                     "requires": {
                         "@babel/cli": "^7.21.0",
                         "@babel/core": "^7.21.0",
@@ -8156,16 +8159,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3": {
             "version": "7.8.4",
             "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
@@ -9546,28 +9539,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.62",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
-            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -9579,7 +9550,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -25311,12 +25281,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/package.json b/package.json
index b137ddd67..e301268dd 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.62",
+        "browndash-components": "0.0.76",
         "browser-assert": "^1.2.1",
         "bson": "^4.6.1",
         "canvas": "^2.9.3",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 934803c16..c925cff0f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -34,7 +34,7 @@ import { ContextMenuProps } from '../views/ContextMenuItem';
 import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss';
 import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke';
 import { AudioBox } from '../views/nodes/AudioBox';
-import { FontIconBox } from '../views/nodes/button/FontIconBox';
+import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
 import { ColorBox } from '../views/nodes/ColorBox';
 import { ComparisonBox } from '../views/nodes/ComparisonBox';
 import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
@@ -324,8 +324,9 @@ export class DocumentOptions {
     badgeValue?: ScriptField;
 
     //LINEAR VIEW
-    linearView_IsExpanded?: BOOLt = new BoolInfo('is linear view expanded');
+    linearView_IsOpen?: BOOLt = new BoolInfo('is linear view open');
     linearView_Expandable?: BOOLt = new BoolInfo('can linear view be expanded');
+    linearView_Dropdown?: BOOLt = new BoolInfo('can linear view be opened as a dropdown');
     linearView_SubMenu?: BOOLt = new BoolInfo('is doc a sub menu of more linear views');
     flexGap?: NUMt = new NumInfo('Linear view flex gap');
     flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse';
@@ -406,6 +407,7 @@ export class DocumentOptions {
 
     clipboard?: Doc;
     hoverBackgroundColor?: string; // background color of a label when hovered
+    userBackgroundColor?: STRt = new StrInfo('background color associated with a Dash user (seen in header fields of shared documents)');
     userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
 }
 export namespace Docs {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 32229f22f..cde3daf5a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -12,7 +12,7 @@ import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types";
 import { nullAudio } from "../../fields/URLField";
 import { SetCachedGroups, SharingPermissions } from "../../fields/util";
 import { GestureUtils } from "../../pen-gestures/GestureUtils";
-import { OmitKeys, Utils } from "../../Utils";
+import { OmitKeys, Utils, addStyleSheetRule } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
 import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
@@ -20,7 +20,7 @@ import { TreeViewType } from "../views/collections/CollectionTreeView";
 import { DashboardView } from "../views/DashboardView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
-import { ButtonType } from "../views/nodes/button/FontIconBox";
+import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox";
 import { OpenWhere } from "../views/nodes/DocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DragManager, dropActionType } from "./DragManager";
@@ -28,7 +28,7 @@ import { MakeTemplate } from "./DropConverter";
 import { FollowLinkScript } from "./LinkFollower";
 import { LinkManager } from "./LinkManager";
 import { ScriptingGlobals } from "./ScriptingGlobals";
-import { ColorScheme } from "./SettingsManager";
+import { ColorScheme, SettingsManager } from "./SettingsManager";
 import { UndoManager } from "./UndoManager";
 
 interface Button {
@@ -373,7 +373,7 @@ export class CurrentUserUtils {
         });
 
         const reqdStackOpts:DocumentOptions ={
-            title: "menuItemPanel", childDragAction: "same", backgroundColor: Colors.DARK_GRAY, layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
+            title: "menuItemPanel", childDragAction: "same", layout_boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
             _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true,
         };
         return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
@@ -609,7 +609,7 @@ export class CurrentUserUtils {
         const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, defaultDoubleClick: 'ignore', undoIgnoreFields: new List<string>(['opacity']), _dragOnlyWithinContainer: true, ...desc.opts}, desc.scripts));
         const dockBtnsReqdOpts:DocumentOptions = {
             title: "docked buttons", _height: 40, flexGap: 0, layout_boxShadow: "standard", childDragAction: 'move',
-            childDontRegisterViews: true, linearView_IsExpanded: true, linearView_Expandable: true, ignoreClick: true
+            childDontRegisterViews: true, linearView_IsOpen: 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 });
@@ -625,12 +625,12 @@ export class CurrentUserUtils {
     }
     static viewTools(): Button[] {
         return [
-            { title: "Snap\xA0Lines", icon: "th",           toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snaplines",  funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
-            { title: "Grid",          icon: "border-all",   toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
-            { title: "View\xA0All",   icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
-            { title: "Clusters",      icon: "braille",      toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
-            { title: "Cards",         icon: "brain",        toolTip: "Flashcards",          btnType: ButtonType.ToggleButton, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
-            { title: "Arrange",icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange",  btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange",    funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Snap", icon: "th",           toolTip: "Show Snap Lines",     btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snaplines",  funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Grid",          icon: "border-all",   toolTip: "Show Grid",           btnType: ButtonType.ToggleButton, expertMode: false, toolType:"grid",       funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "View All",   icon: "object-group", toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll",    funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Clusters",      icon: "braille",      toolTip: "Show Doc Clusters",   btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters",   funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Cards",         icon: "brain",        toolTip: "Flashcards",          btnType: ButtonType.ToggleButton, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+            { title: "Arrange",icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange",  btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange",    funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(self.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
         ]
     }
     static textTools():Button[] {
@@ -645,9 +645,13 @@ export class CurrentUserUtils {
             { title: "Under",    toolTip: "Underline (Ctrl+U)",   btnType: ButtonType.ToggleButton, icon: "underline",   toolType:"underline",                     scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
             { title: "Bullets",  toolTip: "Bullet List",          btnType: ButtonType.ToggleButton, icon: "list",        toolType:"bullet",                        scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
             { title: "#",        toolTip: "Number List",          btnType: ButtonType.ToggleButton, icon: "list-ol",     toolType:"decimal",                       scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Left",     toolTip: "Left align (Cmd-[)",   btnType: ButtonType.ToggleButton, icon: "align-left",  toolType:"left",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }},
-            { title: "Center",   toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",                        scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
-            { title: "Right",    toolTip: "Right align (Cmd-])",  btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right",                         scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+            { title: "Alignment",toolTip: "Alignment",            btnType: ButtonType.MultiToggleButton,                  toolType:"alignment", ignoreClick: true,  scripts: {script:  '{ return setFontAttr(self.toolType, value, _readOnly_);}'},
+              subMenu: [
+                { title: "Left",     toolTip: "Left align (Cmd-[)",   btnType: ButtonType.ToggleButton, icon: "align-left",  toolType:"left",                          scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}' }},
+                { title: "Center",   toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",                        scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+                { title: "Right",    toolTip: "Right align (Cmd-])",  btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right",                         scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'} },
+              ]
+            },
             { title: "Dictate",  toolTip: "Dictate",              btnType: ButtonType.ToggleButton, icon: "microphone",  toolType:"dictation",                     scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}},
             { title: "NoLink",   toolTip: "Auto Link",            btnType: ButtonType.ToggleButton, icon: "link",        toolType:"noAutoLink", expertMode:true,   scripts: {onClick: '{ return toggleCharStyle(self.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
             // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}},
@@ -695,17 +699,17 @@ export class CurrentUserUtils {
               title: "Perspective",                    toolTip: "View",                 btnType: ButtonType.DropdownList,                                                                                                                                    ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
             { title: "Pin",     icon: "map-pin",       toolTip: "Pin View to Trail",    btnType: ButtonType.ClickButton,         expertMode: false,                                                                                                                             width: 30,  scripts: { onClick: 'pinWithView(altKey)'}},
             { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",btnType: ButtonType.ColorButton,         expertMode: false,                                                                                                          ignoreClick: true, width: 30,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected
-            { 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: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         expertMode: true,                                                                                                          ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'}},
+            { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'},       scripts: { onClick: 'return { 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: "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
-            { title: "Doc",     icon: "Doc",           toolTip: "Freeform Doc tools",   subMenu: CurrentUserUtils.freeTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
-            { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
-            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected
+            { title: "Text",    icon: "Text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   expertMode: false, toolType:DocumentType.RTF,            funcs: {                                                                             linearView_IsOpen: `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_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
+            { title: "Doc",     icon: "Doc",           toolTip: "Freeform Doc tools",   subMenu: CurrentUserUtils.freeTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
+            { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
+            { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected
+            { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema,   funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected
         ];
     }
 
@@ -713,7 +717,6 @@ export class CurrentUserUtils {
     static setupContextMenuButton(params:Button, btnDoc?:Doc) {
         const reqdOpts:DocumentOptions = {
             ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
-            backgroundColor: params.backgroundColor ??"transparent", ///  a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
             color: Colors.WHITE, isSystem: true,
             _nativeWidth: params.width ?? 30, _width: params.width ?? 30,
             _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
@@ -722,21 +725,20 @@ export class CurrentUserUtils {
         };
         const reqdFuncs:{[key:string]:any} = {
             ...params.funcs,
-            backgroundColor: params.btnType === ButtonType.ToggleButton ? params.scripts?.onClick:undefined /// a bit hacky.  if onClick is set, then we assume it returns a color value when queried with '_readOnly_'.  This will be true for toggle buttons, but not generally
         }
         return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
     }
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
-        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsExpanded"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true,  linearView_IsExpanded: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
+        const reqdCtxtOpts:DocumentOptions = { title: "context menu buttons", undoIgnoreFields:new List<string>(['width', "linearView_IsOpen"]), flexGap: 0, childDragAction: 'embed', childDontRegisterViews: true,  linearView_IsOpen: true, ignoreClick: true, linearView_Expandable: false, _height: 35 };
         const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
-            if (!params.subMenu) {
+            if (!params.subMenu) { // button does not have a sub menu
                 return this.setupContextMenuButton(params, menuBtnDoc);
-            } else {
-                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsExpanded"]),
+            } else { // linear view
+                const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsOpen"]),
                     childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true,
                     linearView_SubMenu: true, linearView_Expandable: true, };
                 const items = params.subMenu?.map(sub =>
@@ -847,6 +849,11 @@ export class CurrentUserUtils {
         doc.fontHighlight ?? (doc.fontHighlight = "");
         doc.defaultAclPrivate ?? (doc.defaultAclPrivate = false);
         doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
+        doc.userBackgroundColor ?? (doc.userBackgroundColor = Colors.DARK_GRAY);
+        addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${doc.userBackgroundColor} !important` });
+        doc.userVariantColor ?? (doc.userVariantColor = Colors.MEDIUM_BLUE);
+        doc.userColor ?? (doc.userColor = Colors.LIGHT_GRAY);
+        doc.userTheme ?? (doc.userTheme = ColorScheme.Dark);
         doc.filterDocCount = 0;
         doc.treeViewFreezeChildren = "remove|add";
         this.setupLinkDocs(doc, linkDatabaseId);
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 47997cc5c..f235be192 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -9,7 +9,7 @@ import { RichTextField } from '../../fields/RichTextField';
 import { ImageField } from '../../fields/URLField';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { listSpec } from '../../fields/Schema';
-import { ButtonType } from '../views/nodes/button/FontIconBox';
+import { ButtonType } from '../views/nodes/FontIconBox/FontIconBox';
 
 export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined, templateField: string = '') {
     if (templateField) Doc.GetProto(doc).title = templateField; /// the title determines which field is being templated
diff --git a/src/client/util/ReportManager.tsx b/src/client/util/ReportManager.tsx
index 4c1020455..89c17e42f 100644
--- a/src/client/util/ReportManager.tsx
+++ b/src/client/util/ReportManager.tsx
@@ -11,7 +11,7 @@ import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager
 import { DocServer } from '../DocServer';
 import { Networking } from '../Network';
 import { MainViewModal } from '../views/MainViewModal';
-import { FontIconBox } from '../views/nodes/button/FontIconBox';
+import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox';
 import { DragManager } from './DragManager';
 import { GroupManager } from './GroupManager';
 import './SettingsManager.scss';
@@ -290,7 +290,7 @@ export class ReportManager extends React.Component<{}> {
                 isDisplayed={this.isOpen}
                 interactive={true}
                 closeOnExternalClick={this.close}
-                dialogueBoxStyle={{ width: 'auto', height: '500px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
+                dialogueBoxStyle={{ width: 'auto', height: '500px', background: Cast(Doc.UserDoc().userColor, 'string', null) }}
             />
         );
     }
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss
index c41adfdd7..bca649bc3 100644
--- a/src/client/util/SettingsManager.scss
+++ b/src/client/util/SettingsManager.scss
@@ -2,41 +2,20 @@
 
 .settings-interface {
     //background-color: whitesmoke !important;
-    color: grey;
     width: 450px;
-
-    button {
-        background: #315a96;
-        outline: none;
-        border-radius: 5px;
-        border: 0px;
-        color: #fcfbf7;
-        text-transform: uppercase;
-        letter-spacing: 2px;
-        font-size: 75%;
-        padding: 10px;
-        margin: 10px;
-        transition: transform 0.2s;
-        margin: 2px;
-    }
 }
 
 .settings-title {
     font-size: 25px;
     font-weight: bold;
     padding-right: 10px;
-    color: black;
 }
 
 .settings-username {
     font-size: 12px;
     padding-right: 15px;
-    color: black;
     margin-top: 10px;
     margin-bottom: 10px;
-    /* right: 135; */
-    // position: absolute;
-    // left: 243;
 }
 
 .settings-section {
@@ -49,7 +28,6 @@
         font-size: 16;
         font-weight: bold;
         text-align: left;
-        color: black;
         width: 80;
         margin-right: 50px;
     }
@@ -61,45 +39,10 @@
 
 .password-content {
     display: flex;
+    width: 100%;
     flex-direction: column;
     align-items: flex-start;
-
-    .password-content-inputs {
-        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: 15px;
-            width: 130;
-            color: $medium-gray;
-            border-radius: 5px;
-            padding: 7px;
-        }
-    }
-
-    .password-content-buttons {
-        display: flex;
-        gap: 5px;
-        justify-content: center;
-        align-items: flex-start;
-        flex-direction: column;
-
-        .password-submit {
-            //margin-left: 85px;
-            margin-top: 5px;
-        }
-
-        .password-forgot {
-            //margin-left: 65px;
-            //margin-top: -20px;
-            font-size: 12px;
-            white-space: nowrap;
-        }
-    }
+    gap: 5px;
 }
 
 .accounts-content {
@@ -115,7 +58,6 @@
         width: 80%;
         height: 35px;
         margin-right: 10px;
-        color: black;
         border-radius: 5px;
 
         &:hover {
@@ -139,12 +81,6 @@
             }
         }
 
-        .playground-text {
-            color: black;
-            margin-right: 10px;
-            margin-top: 2;
-        }
-
         .acl-text {
             color: black;
             margin-top: 2;
@@ -176,7 +112,6 @@
 
 .appearances-content {
     display: flex;
-    color: black;
     font-size: 10px;
 
     .preferences-color {
@@ -201,7 +136,6 @@
         margin-top: 2px;
 
         .preferences-font-text {
-            color: black;
             margin-top: 4;
             margin-right: 4;
             margin-bottom: 2px;
@@ -216,7 +150,6 @@
 
         .font-select {
             height: 35px;
-            color: black;
             font-size: 9;
             margin-right: 6;
             border-radius: 5px;
@@ -229,7 +162,6 @@
 
         .size-select {
             height: 35px;
-            color: black;
             font-size: 9;
             border-radius: 5px;
             width: 30%;
@@ -241,7 +173,6 @@
     }
 
     .preferences-check {
-        color: black;
         margin-right: 4;
         margin-bottom: -3;
         margin-left: 5;
@@ -265,7 +196,6 @@
 
 
     .settings-content {
-        background: #e4e4e4;
         padding: 10px;
         width: 500px;
         flex: 1 1 auto;
@@ -296,7 +226,6 @@
     }
 
     h1 {
-        color: #121721;
         text-transform: uppercase;
         letter-spacing: 2px;
         font-size: 19;
@@ -325,7 +254,6 @@
 
         .padding {
             padding: 0 0 0 20px;
-            color: black;
         }
     }
 }
@@ -336,16 +264,11 @@
     min-height: 250px;
     height: 100%;
     width: 100%;
-
-    .settings-content {
-        background-color: $off-white;
-    }
 }
 
 .settings-panel {
     position: relative;
     min-width: 150px;
-    background-color: $light-blue;
 
     .settings-user {
         position: absolute;
@@ -370,16 +293,11 @@
 .settings-tabs {
     // font-size: 16px;
     font-weight: 600;
-    color: black;
 
     .tab-control {
         padding: 10px;
         border-bottom: 1px solid #9f9e9e;
         cursor: pointer;
-
-        &.active {
-            background-color: #fdfdfd;
-        }
     }
 }
 
@@ -398,16 +316,18 @@
 
 .tab-content {
     display: flex;
+    flex-flow: row wrap;
+    gap: 10px;
+    overflow: hidden;
 
     .tab-column {
-        flex: 0 0 50%;
+        flex: 0 0 calc(50% - 10px);
         flex-direction: column;
         display: flex;
         justify-content: flex-start;
         align-items: flex-start;
 
         .tab-column-title {
-            color: $dark-gray;
             font-size: 16px;
             font-weight: bold;
             margin-bottom: 10px;
@@ -420,6 +340,7 @@
             align-items: flex-start;
             flex-direction: column;
             gap: 10px;
+            width: 100%;
         }
     }
 }
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 09d6d034f..b6df5f26a 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -11,12 +11,12 @@ import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager
 import { DocServer } from '../DocServer';
 import { Networking } from '../Network';
 import { MainViewModal } from '../views/MainViewModal';
-import { FontIconBox } from '../views/nodes/button/FontIconBox';
+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, Group, Size, Toggle, ToggleType, Type } from 'browndash-components';
+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');
@@ -24,9 +24,10 @@ export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
 export enum ColorScheme {
-    Dark = '-Dark',
-    Light = '-Light',
-    System = '-MatchSystem',
+    Dark = 'Dark',
+    Light = 'Light',
+    System = 'Match System',
+    Custom = 'Custom'
 }
 
 export enum freeformScrollMode {
@@ -53,8 +54,8 @@ export class SettingsManager extends React.Component<{}> {
     @computed get backgroundColor() {
         return Doc.UserDoc().activeCollectionBackground;
     }
-    @computed get colorScheme() {
-        return Doc.ActiveDashboard?.colorScheme;
+    @computed get userTheme() {
+        return Doc.UserDoc().userTheme;
     }
 
     constructor(props: {}) {
@@ -76,14 +77,31 @@ export class SettingsManager extends React.Component<{}> {
         }
     };
 
+    @computed get userColor() {
+        return StrCast(Doc.UserDoc().userColor)
+    }
+
+    @computed get userVariantColor() {
+        return StrCast(Doc.UserDoc().userVariantColor)
+    }
+
+    @computed get userBackgroundColor() {
+        return StrCast(Doc.UserDoc().userBackgroundColor)
+    }
+
     @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((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 switchUserBackgroundColor = action((color: string) => {
+        Doc.UserDoc().userBackgroundColor = color;
+        addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${color} !important` });
+    });
     @undoBatch switchUserColor = action((color: string) => {
-        Doc.SharingDoc().userColor = undefined;
-        Doc.GetProto(Doc.SharingDoc()).userColor = color;
+        Doc.UserDoc().userColor = color;
+    });
+    @undoBatch switchUserVariantColor = action((color: string) => {
+        Doc.UserDoc().userVariantColor = color;
     });
     @undoBatch playgroundModeToggle = action(() => {
         this.playgroundMode = !this.playgroundMode;
@@ -96,22 +114,24 @@ export class SettingsManager extends React.Component<{}> {
     @undoBatch
     @action
     changeColorScheme = action((scheme: string) => {
-        const activeDashboard = Doc.ActiveDashboard;
-        if (!activeDashboard) return;
+        Doc.UserDoc().userTheme = scheme;
         switch (scheme) {
             case ColorScheme.Light:
-                activeDashboard.colorScheme = ColorScheme.Light
-                addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: '#d3d3d3 !important' });
+                this.switchUserColor("#323232")
+                this.switchUserBackgroundColor("#DFDFDF")
+                this.switchUserVariantColor("#BDDDF5")
                 break;
             case ColorScheme.Dark:
-                activeDashboard.colorScheme = ColorScheme.Dark;
-                addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: 'black !important' });
+                this.switchUserColor("#DFDFDF")
+                this.switchUserBackgroundColor("#323232")
+                this.switchUserVariantColor("#4476F7")
+                break;
+            case ColorScheme.Custom:
                 break;
             case ColorScheme.System:
             default:
-                activeDashboard.colorScheme = ColorScheme.System;
                 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
-                    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)
+                    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;
         }
@@ -120,18 +140,16 @@ export class SettingsManager extends React.Component<{}> {
     @computed get colorsContent() {
 
 
-        const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System];
-        const schemeMap = ['Light', 'Dark', 'Match System'];
-
+        const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.Custom, ColorScheme.System];
+        const schemeMap = ['Light', 'Dark', 'Custom', 'Match System'];
+        const userTheme = StrCast(Doc.UserDoc().userTheme);
         return (
-            <div className="colors-content">
-                <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}/>
+            <div style={{width: '100%'}}>
                 <Dropdown 
                     formLabel='Theme'
                     size={Size.SMALL}
                     type={Type.TERT}
-                    selectedVal={StrCast(Doc.ActiveDashboard?.colorScheme)}
+                    selectedVal={userTheme}
                     setSelectedVal={(scheme) => this.changeColorScheme(scheme as string)}
                     items={colorSchemes.map((scheme, i) => (
                         {
@@ -140,8 +158,14 @@ export class SettingsManager extends React.Component<{}> {
                         }
                     ))} 
                     dropdownType={DropdownType.SELECT}
-                    color={StrCast(Doc.SharingDoc().userColor)}
+                    color={this.userColor}
+                    fillWidth
                 />
+                {userTheme === ColorScheme.Custom && <Group formLabel='Custom Theme'>
+                    <ColorPicker tooltip={'User Color'} color={this.userColor} type={Type.SEC} icon={<FaFillDrip/>} selectedColor={this.userColor} setSelectedColor={this.switchUserColor}/>
+                    <ColorPicker tooltip={'User Background Color'} color={this.userColor} type={Type.SEC} icon={<FaPalette/>} selectedColor={this.userBackgroundColor} setSelectedColor={this.switchUserBackgroundColor}/>
+                    <ColorPicker tooltip={'User Variant Color'} color={this.userColor} type={Type.SEC} icon={<FaPalette/>} selectedColor={this.userVariantColor} setSelectedColor={this.switchUserVariantColor}/>
+                </Group>}
             </div>
         );
     }
@@ -155,7 +179,7 @@ export class SettingsManager extends React.Component<{}> {
                     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)}
+                    color={this.userColor}
 
                 />
                 <Toggle 
@@ -165,7 +189,7 @@ export class SettingsManager extends React.Component<{}> {
                     onClick={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} 
                     toggleStatus={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} 
                     size={Size.XSMALL}
-                    color={StrCast(Doc.SharingDoc().userColor)}
+                    color={this.userColor}
 
                 />
                 <Toggle 
@@ -175,7 +199,7 @@ export class SettingsManager extends React.Component<{}> {
                     onClick={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} 
                     toggleStatus={FontIconBox.GetShowLabels()} 
                     size={Size.XSMALL}
-                    color={StrCast(Doc.SharingDoc().userColor)}
+                    color={this.userColor}
 
                 />
                 <Toggle 
@@ -185,7 +209,7 @@ export class SettingsManager extends React.Component<{}> {
                     onClick={e => FontIconBox.SetRecognizeGestures(!FontIconBox.GetRecognizeGestures())} 
                     toggleStatus={FontIconBox.GetRecognizeGestures()} 
                     size={Size.XSMALL}
-                    color={StrCast(Doc.SharingDoc().userColor)}
+                    color={this.userColor}
 
                 />
                 <Toggle 
@@ -195,7 +219,7 @@ export class SettingsManager extends React.Component<{}> {
                     onClick={e => (Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels)} 
                     toggleStatus={BoolCast(Doc.UserDoc().activeInkHideTextLabels)} 
                     size={Size.XSMALL}
-                    color={StrCast(Doc.SharingDoc().userColor)}
+                    color={this.userColor}
 
                 />
                 <Toggle 
@@ -205,7 +229,7 @@ export class SettingsManager extends React.Component<{}> {
                     onClick={e => (Doc.UserDoc().openInkInLightbox = !Doc.UserDoc().openInkInLightbox)} 
                     toggleStatus={BoolCast(Doc.UserDoc().openInkInLightbox)} 
                     size={Size.XSMALL}
-                    color={StrCast(Doc.SharingDoc().userColor)}
+                    color={this.userColor}
 
                 />
             </div>
@@ -238,6 +262,15 @@ export class SettingsManager extends React.Component<{}> {
                     <div className="tab-column-content">
                         {/* <NumberInput/> */}
                         <Group formLabel={'Default Font'}>
+                            <NumberDropdown 
+                                color={this.userColor} 
+                                numberDropdownType={'input'} 
+                                min={0} max={50} step={2}
+                                type={Type.TERT} 
+                                number={0}
+                                unit={"px"}
+                                setNumber={() => {}}
+                            />
                             <Dropdown 
                                 items={fontFamilies.map((val) => {
                                     return {
@@ -252,7 +285,8 @@ export class SettingsManager extends React.Component<{}> {
                                 type={Type.TERT}
                                 selectedVal={StrCast(Doc.UserDoc().fontFamily)}
                                 setSelectedVal={(val) => {this.changeFontFamily(val as string)}}
-                                color={StrCast(Doc.SharingDoc().userColor)}
+                                color={this.userColor}
+                                fillWidth
                             />
                         </Group>
                     </div>
@@ -262,8 +296,7 @@ export class SettingsManager extends React.Component<{}> {
     }
 
     @action
-    changeVal = (e: React.ChangeEvent, pass: string) => {
-        const value = (e.target as any).value;
+    changeVal = (value: string, pass: string) => {
         switch (pass) {
             case 'curr':
                 this.curr_password = value;
@@ -280,16 +313,33 @@ export class SettingsManager extends React.Component<{}> {
     @computed get passwordContent() {
         return (
             <div className="password-content">
-                <div className="password-content-inputs">
-                    <input className="password-inputs" type="password" placeholder="current password" onChange={e => this.changeVal(e, 'curr')} />
-                    <input className="password-inputs" type="password" placeholder="new password" onChange={e => this.changeVal(e, 'new')} />
-                    <input className="password-inputs" type="password" placeholder="confirm new password" onChange={e => this.changeVal(e, 'conf')} />
-                </div>
-                <div className="password-content-buttons">
-                    {!this.passwordResultText ? null : <div className={`${this.passwordResultText.startsWith('Error') ? 'error' : 'success'}-text`}>{this.passwordResultText}</div>}
-                    <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>
+                <EditableText placeholder="Current password" 
+                    type={Type.SEC} 
+                    color={this.userColor} 
+                    val={""}
+                    setVal={val => this.changeVal(val as string, 'curr')} 
+                    fillWidth
+                    password
+                />
+                <EditableText placeholder="New password" 
+                    type={Type.SEC} 
+                    color={this.userColor} 
+                    val={""}
+                    setVal={val => this.changeVal(val as string, 'new')} 
+                    fillWidth
+                    password
+                />
+                <EditableText placeholder="Confirm new password" 
+                    type={Type.SEC} 
+                    color={this.userColor} 
+                    val={""}
+                    setVal={val => this.changeVal(val as string, 'conf')} 
+                    fillWidth
+                    password
+                />
+                {!this.passwordResultText ? null : <div className={`${this.passwordResultText.startsWith('Error') ? 'error' : 'success'}-text`}>{this.passwordResultText}</div>}
+                <Button type={Type.SEC} text={'Forgot Password'} color={this.userColor}/>
+                <Button type={Type.TERT} text={'Submit'} onClick={this.changePassword} color={this.userColor}/>
             </div>
         );
     }
@@ -346,14 +396,15 @@ export class SettingsManager extends React.Component<{}> {
                             dropdownType={DropdownType.SELECT}
                             type={Type.TERT}
                             placement='bottom-start'
-                            color={StrCast(Doc.SharingDoc().userColor)}
+                            color={this.userColor}
+                            fillWidth
                         />
                         <Toggle 
                             formLabel={'Playground Mode'} 
                             toggleType={ToggleType.SWITCH} 
                             toggleStatus={this.playgroundMode} 
                             onClick={this.playgroundModeToggle}
-                            color={StrCast(Doc.SharingDoc().userColor)}
+                            color={this.userColor}
                         />
                     </div>
                     <div className="tab-column-title" style={{ marginTop: 20, marginBottom: 10 }}>
@@ -379,7 +430,7 @@ export class SettingsManager extends React.Component<{}> {
                             dropdownType={DropdownType.SELECT}
                             type={Type.TERT}
                             placement='bottom-start'
-                            color={StrCast(Doc.SharingDoc().userColor)}
+                            color={this.userColor}
                         />
                     </div>
                 </div>
@@ -390,12 +441,12 @@ export class SettingsManager extends React.Component<{}> {
                             text={"Manage Groups"}
                             type={Type.TERT}
                             onClick={() => GroupManager.Instance?.open()}
-                            color={StrCast(Doc.SharingDoc().userColor)}
+                            color={this.userColor}
                         />
                         <Toggle 
                             toggleType={ToggleType.SWITCH} 
                             formLabel={"Default access private"}
-                            color={StrCast(Doc.SharingDoc().userColor)}
+                            color={this.userColor}
                             toggleStatus={BoolCast(Doc.defaultAclPrivate)} 
                             onClick={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))}/>
                     </div>
@@ -414,23 +465,35 @@ export class SettingsManager extends React.Component<{}> {
             { title: 'Appearance', ele: this.appearanceContent },
             { title: 'Text', ele: this.textContent },
         ];
-
         return (
             <div className="settings-interface">
-                <div className="settings-panel">
+                <div className="settings-panel" style={{ background: this.userColor }}>
                     <div className="settings-tabs">
-                        {tabs.map(tab => (
-                            <div key={tab.title} className={'tab-control ' + (this.activeTab === tab.title ? 'active' : 'inactive')} onClick={action(() => (this.activeTab = tab.title))}>
-                                {tab.title}
-                            </div>
-                        ))}
+                        {tabs.map(tab => {
+                            const isActive = this.activeTab === tab.title
+                            return (
+                                <div key={tab.title}
+                                    style={{
+                                        background: isActive ? this.userBackgroundColor : this.userColor,
+                                        color: isActive ? this.userColor : this.userBackgroundColor,
+                                    }}
+                                    className={'tab-control ' + (isActive ? 'active' : 'inactive')} 
+                                    onClick={action(() => (this.activeTab = tab.title))
+                                }>
+                                    {tab.title}
+                                </div>
+                            )
+                        })}
                     </div>
 
                     <div className="settings-user">
-                        <div className="settings-username">{Doc.CurrentUserEmail}</div>
+                        <div className="settings-username"
+                            style={{color: this.userBackgroundColor}}
+                        >{Doc.CurrentUserEmail}</div>
                         <Button 
                             text={Doc.GuestDashboard ? 'Exit' : 'Log Out'}
                             type={Type.TERT}
+                            color={this.userVariantColor}
                             onClick={() => window.location.assign(Utils.prepend('/logout'))}
                         />
                     </div>
@@ -439,12 +502,13 @@ export class SettingsManager extends React.Component<{}> {
                             
                 <div className="close-button">
                     <Button
-                        icon={<FontAwesomeIcon icon={'times'} color="black" size={'lg'} />}
+                        icon={<FontAwesomeIcon icon={'times'} size={'lg'} />}
                         onClick={this.close}
+                        color={this.userColor}
                     />
                 </div>
 
-                <div className="settings-content">
+                <div className="settings-content" style={{color: this.userColor, background: this.userBackgroundColor}}>
                     {tabs.map(tab => (
                         <div key={tab.title} className={'tab-section ' + (this.activeTab === tab.title ? 'active' : 'inactive')}>
                             {tab.ele}
@@ -462,7 +526,7 @@ export class SettingsManager extends React.Component<{}> {
                 isDisplayed={this.isOpen}
                 interactive={true}
                 closeOnExternalClick={this.close}
-                dialogueBoxStyle={{ width: 'fit-content', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
+                dialogueBoxStyle={{ width: 'fit-content', height: '300px', background: Cast(Doc.UserDoc().userColor, 'string', null) }}
             />
         );
     }
diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss
index 8a0e5480e..b205a0f1e 100644
--- a/src/client/views/AntimodeMenu.scss
+++ b/src/client/views/AntimodeMenu.scss
@@ -5,11 +5,15 @@
     position: absolute;
     z-index: 10001;
     height: $antimodemenu-height;
-    background: $dark-gray;
-    border-bottom: $standard-border;
+    width: fit-content;
+    border-radius: $standard-border-radius;
+    overflow: hidden;
     // box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
     // border-radius: 0px 6px 6px 6px;
     display: flex;
+    justify-content: center;
+    align-items: center;
+    gap: 3px;
 
     &.with-rows {
         flex-direction: column
@@ -20,30 +24,6 @@
         height: 35px;
     }
 
-    .antimodeMenu-button {
-        background-color: transparent;
-        width: 35px;
-        height: 35px;
-        padding: 5;
-        text-align: center;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        position: relative;
-
-        .svg {
-            margin: 0;
-        }
-
-        &.active {
-            background-color: #121212;
-        }
-    }
-
-    .antimodeMenu-button:hover {
-        background-color: rgba(0, 0, 0, 0.4);
-    }
-
     .antimodeMenu-dragger {
         height: 100%;
         transition: width .2s;
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index de1207ce4..c41ea7053 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -1,6 +1,8 @@
 import React = require('react');
 import { observable, action, runInAction } from 'mobx';
 import './AntimodeMenu.scss';
+import { StrCast } from '../../fields/Types';
+import { Doc } from '../../fields/Doc';
 export interface AntimodeMenuProps {}
 
 /**
@@ -148,6 +150,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
                     left: this._left,
                     top: this._top,
                     opacity: this._opacity,
+                    background: StrCast(Doc.UserDoc().userBackgroundColor),
                     transitionProperty: this._transitionProperty,
                     transitionDuration: this._transitionDuration,
                     transitionDelay: this._transitionDelay,
@@ -173,6 +176,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
                     height: 'inherit',
                     width: 200,
                     opacity: this._opacity,
+                    background: StrCast(Doc.UserDoc().userBackgroundColor),
                     transitionProperty: this._transitionProperty,
                     transitionDuration: this._transitionDuration,
                     transitionDelay: this._transitionDelay,
@@ -195,6 +199,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends React.Co
                     left: this._left,
                     top: this._top,
                     opacity: this._opacity,
+                    background: StrCast(Doc.UserDoc().userBackgroundColor),
                     transitionProperty: this._transitionProperty,
                     transitionDuration: this._transitionDuration,
                     transitionDelay: this._transitionDelay,
diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
index b8a6f6c05..b95312ea0 100644
--- a/src/client/views/DashboardView.scss
+++ b/src/client/views/DashboardView.scss
@@ -14,6 +14,7 @@
             flex-direction: column;
             width: 250px;
             min-width: 250px;
+            gap: 5px;
       }
 
       .all-dashboards {
@@ -56,15 +57,26 @@
     display: flex;
     justify-content: center;
     align-items: center;
+    position: relative;
 
     &:hover {
         color: $light-blue;
         border: solid 2px $light-blue;
     }
+
+    .background {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        left: 0;
+        top: 0;
+        z-index: -1;
+      }
 }
 
 .dashboard-container {
       border-radius: 10px;
+      position: relative;
       cursor: pointer;
       width: 250px;
       height: 200px;
@@ -99,10 +111,20 @@
       .more {
             z-index: 100;
       }
+
+      .background {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        left: 0;
+        top: 0;
+        z-index: -1;
+      }
 }
 
 .new-dashboard {
     color: $dark-gray;
+    padding: 10px;
     display: flex;
     width: 100%;
     height: 100%;
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 3bd6ac61c..dd1079d88 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, ColorPicker, FontSize, IconButton, Size, Type } from 'browndash-components';
+import { Button, ColorPicker, EditableText, FontSize, IconButton, Size, Type } from 'browndash-components';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -24,7 +24,7 @@ import { ContextMenu } from './ContextMenu';
 import './DashboardView.scss';
 import { Colors } from './global/globalEnums';
 import { MainViewModal } from './MainViewModal';
-import { ButtonType } from './nodes/button/FontIconBox';
+import { ButtonType } from './nodes/FontIconBox/FontIconBox';
 import { FaPlus } from 'react-icons/fa';
 
 enum DashboardGroup {
@@ -43,13 +43,16 @@ export class DashboardView extends React.Component {
     @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
 
     @observable private newDashboardName: string | undefined = undefined;
-    @observable private newDashboardColor: string | undefined = undefined;
+    @observable private newDashboardColor: string | undefined = "#AFAFAF";
     @action abortCreateNewDashboard = () => {
         this.newDashboardName = undefined;
     };
     @action setNewDashboardName(name: string) {
         this.newDashboardName = name;
     }
+    @action setNewDashboardColor(color: string) {
+        this.newDashboardColor = color;
+    }
 
     @action
     selectDashboardGroup = (group: DashboardGroup) => {
@@ -95,23 +98,33 @@ export class DashboardView extends React.Component {
         const dashboardCount = DocListCast(Doc.MyDashboards.data).length + 1;
         const placeholder = `Dashboard ${dashboardCount}`;
         return (
-            <div className="new-dashboard">
+            <div className="new-dashboard"
+                style={{ 
+                    background: StrCast(Doc.UserDoc().userBackgroundColor),
+                    color: StrCast(Doc.UserDoc().userColor)
+                }}
+            >
                 <div className="header">Create New Dashboard</div>
-                <div className="title-input">
-                    Title
-                    <input className="input" placeholder={placeholder} onChange={e => this.setNewDashboardName((e.target as any).value)} />
-                </div>
-                <div className="color-picker">
-                    Background
-                    <ColorPicker
-                        onChange={color => {
-                            this.newDashboardColor = color;
-                        }}
-                    />
-                </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);
+                    }}
+                />
                 <div className="button-bar">
-                    <Button text="Cancel" onClick={this.abortCreateNewDashboard} />
-                    <Button text="Create" onClick={() => this.createNewDashboard(this.newDashboardName!, this.newDashboardColor)} />
+                    <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)} />
                 </div>
             </div>
         );
@@ -150,32 +163,49 @@ export class DashboardView extends React.Component {
     };
 
     render() {
+        const color = StrCast(Doc.UserDoc().userColor)
+        const variant = StrCast(Doc.UserDoc().userVariantColor)
         return (
             <>
                 <div className="dashboard-view">
                     <div className="left-menu">
-                        <div className="new-dashboard-button">
-                            <Button icon={<FaPlus />} size={Size.LARGE} text="New" type={Type.SEC} onClick={() => this.setNewDashboardName('')} />
-                        </div>
-                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}>
-                            My Dashboards
-                        </div>
-                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>
-                            Shared Dashboards
-                        </div>
+                        <Button 
+                            text={'My Dashboards'}
+                            active={this.selectedDashboardGroup === DashboardGroup.MyDashboards}
+                            color={color}
+                            align={'flex-start'}
+                            onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}
+                            fillWidth
+                        />
+                        <Button 
+                            text={'Shared Dashboards'}
+                            active={this.selectedDashboardGroup === DashboardGroup.SharedDashboards}
+                            color={color}
+                            align={'flex-start'}
+                            onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}
+                            fillWidth
+                        />
+                        <Button icon={<FaPlus />} color={variant} iconPlacement="left" text="New Dashboard" type={Type.TERT} onClick={() => this.setNewDashboardName('')} />
                     </div>
                     <div className="all-dashboards">
                         {this.getDashboards().map(dashboard => {
                             const href = ImageCast(dashboard.thumb)?.url.href;
                             return (
-                                <div className="dashboard-container" key={dashboard[Id]} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
+                                <div 
+                                    className="dashboard-container" key={dashboard[Id]} 
+                                    onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
                                     <img
                                         src={
                                             href ?? 'https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU='
                                         }
                                     />
                                     <div className="info">
-                                        <input style={{ border: 'unset' }} className="input" onClick={e => e.stopPropagation()} defaultValue={StrCast(dashboard.title)} onChange={e => (Doc.GetProto(dashboard).title = (e.target as any).value)} />
+                                        <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"
@@ -188,9 +218,13 @@ export class DashboardView extends React.Component {
                                                 e.stopPropagation();
                                                 this.onContextMenu(dashboard, e);
                                             }}>
-                                            <Button size={Size.SMALL} icon={<FontAwesomeIcon color="black" size="lg" icon="bars" />} />
+                                            <Button size={Size.SMALL} color={color} icon={<FontAwesomeIcon color={color} size="lg" icon="bars" />} />
                                         </div>
                                     </div>
+                                    <div className={`background`} style={{
+                                        background: StrCast(Doc.UserDoc().userColor),
+                                        filter: 'opacity(0.2)'
+                                    }}/>
                                 </div>
                             );
                         })}
@@ -200,6 +234,10 @@ export class DashboardView extends React.Component {
                                 this.setNewDashboardName('');
                             }}>
                             +
+                            <div className={`background`} style={{
+                                background: StrCast(Doc.UserDoc().userColor),
+                                filter: 'opacity(0.2)'
+                            }}/>
                         </div>
                     </div>
                 </div>
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 7043edcee..4deed5ed9 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -6,6 +6,7 @@ import { ObjectField } from '../../fields/ObjectField';
 import './EditableView.scss';
 import { DocumentIconContainer } from './nodes/DocumentIcon';
 import { OverlayView } from './OverlayView';
+import { EditableText } from 'browndash-components';
 
 export interface EditableProps {
     /**
@@ -231,39 +232,49 @@ export class EditableView extends React.Component<EditableProps> {
                     onChange: this.props.autosuggestProps.onChange,
                 }}
             />
-        ) : this.props.oneLine !== false && this.props.GetValue()?.toString().indexOf('\n') === -1 ? (
-            <input
-                className="editableView-input"
-                ref={r => (this._inputref = r)}
-                style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minWidth: 20, background: this.props.background }}
-                placeholder={this.props.placeholder}
-                onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
-                defaultValue={this.props.GetValue()}
-                autoFocus={true}
-                onChange={this.onChange}
-                onKeyDown={this.onKeyDown}
-                onKeyPress={this.stopPropagation}
-                onPointerDown={this.stopPropagation}
-                onClick={this.stopPropagation}
-                onPointerUp={this.stopPropagation}
+        ) : <EditableText
+                // oneLine={this.props.oneLine && this.props.GetValue()?.toString().indexOf('\n') === -1}
+                editing={true} 
+                onEdit={function (newText: string): void {
+                    throw new Error('Function not implemented.');
+                } } 
+                setEditing={function (editing: boolean): void {
+                    throw new Error('Function not implemented.');
+                } }                
+                // onEdit={this.onChange}
             />
-        ) : (
-            <textarea
-                className="editableView-input"
-                ref={r => (this._inputref = r)}
-                style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minHeight: `min(100%, ${(this.props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20, background: this.props.background }}
-                placeholder={this.props.placeholder}
-                onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
-                defaultValue={this.props.GetValue()}
-                autoFocus={true}
-                onChange={this.onChange}
-                onKeyDown={this.onKeyDown}
-                onKeyPress={this.stopPropagation}
-                onPointerDown={this.stopPropagation}
-                onClick={this.stopPropagation}
-                onPointerUp={this.stopPropagation}
-            />
-        );
+        //     // <input
+        //     //     className="editableView-input"
+        //     //     ref={r => (this._inputref = r)}
+        //     //     style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minWidth: 20, background: this.props.background }}
+        //     //     placeholder={this.props.placeholder}
+        //     //     onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
+        //     //     defaultValue={this.props.GetValue()}
+        //     //     autoFocus={true}
+        //     //     onChange={this.onChange}
+        //     //     onKeyDown={this.onKeyDown}
+        //     //     onKeyPress={this.stopPropagation}
+        //     //     onPointerDown={this.stopPropagation}
+        //     //     onClick={this.stopPropagation}
+        //     //     onPointerUp={this.stopPropagation}
+        //     // />
+        // ) : (
+        //     <textarea
+        //         className="editableView-input"
+        //         ref={r => (this._inputref = r)}
+        //         style={{ display: this.props.display, overflow: 'auto', fontSize: this.props.fontSize, minHeight: `min(100%, ${(this.props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20, background: this.props.background }}
+        //         placeholder={this.props.placeholder}
+        //         onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)}
+        //         defaultValue={this.props.GetValue()}
+        //         autoFocus={true}
+        //         onChange={this.onChange}
+        //         onKeyDown={this.onKeyDown}
+        //         onKeyPress={this.stopPropagation}
+        //         onPointerDown={this.stopPropagation}
+        //         onClick={this.stopPropagation}
+        //         onPointerUp={this.stopPropagation}
+        //     />
+        // );
     }
 
     render() {
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 54ad519de..4e6d5c2e9 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -31,7 +31,7 @@ import {
     SetActiveInkWidth,
 } from './InkingStroke';
 import { InkTranscription } from './InkTranscription';
-import { checkInksToGroup } from './nodes/button/FontIconBox';
+import { checkInksToGroup } from './nodes/FontIconBox/FontIconBox';
 import { DocumentView } from './nodes/DocumentView';
 import { RadialMenu } from './nodes/RadialMenu';
 import { Touchable } from './Touchable';
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index b95ce0e99..e33054c8e 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -68,10 +68,6 @@ h1,
     left: 0;
     z-index: 1;
     touch-action: none;
-
-    .searchBox-container {
-        background: $light-gray;
-    }
 }
 
 .mainView-container,
@@ -118,10 +114,6 @@ h1,
         background: $light-gray;
     }
 
-    .searchBox-container {
-        background: $dark-gray;
-    }
-
     .contextMenu-cont,
     .contextMenu-item {
         background: $dark-gray;
@@ -193,17 +185,6 @@ h1,
             background: $light-gray;
         }
     }
-
-    .propertiesView {
-        left: 0;
-        position: absolute;
-        z-index: 2;
-        background-color: $light-gray;
-
-        .editable-title {
-            background-color: $light-gray;
-        }
-    }
 }
 
 .mainView-libraryHandle {
@@ -250,7 +231,6 @@ h1,
 
 .mainView-leftMenuPanel {
     min-width: var(--menuPanelWidth);
-    background-color: $dark-gray;
     border-right: $standard-border;
 
     .collectionStackingView {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ec8f5989f..a7164f88b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -754,7 +754,8 @@ export class MainView extends React.Component {
 
     @computed get leftMenuPanel() {
         return (
-            <div key="menu" className="mainView-leftMenuPanel" style={{ display: LightboxView.LightboxDoc ? 'none' : undefined }}>
+            <div key="menu" className="mainView-leftMenuPanel" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor),
+                display: LightboxView.LightboxDoc ? 'none' : undefined }}>
                 <DocumentView
                     Document={Doc.MyLeftSidebarMenu}
                     DataDoc={undefined}
@@ -809,8 +810,8 @@ export class MainView extends React.Component {
                 {this._hideUI ? null : this.leftMenuPanel}
                 <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
                     {this.flyout}
-                    <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} onPointerDown={this.onFlyoutPointerDown}>
-                        <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? 'white' : 'black'} style={{ opacity: '50%' }} size="sm" />
+                    <div className="mainView-libraryHandle" style={{ background: StrCast(Doc.UserDoc().userBackgroundColor), left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} onPointerDown={this.onFlyoutPointerDown}>
+                        <FontAwesomeIcon icon="chevron-left" color={StrCast(Doc.UserDoc().userColor)} style={{ opacity: '50%' }} size="sm" />
                     </div>
                     <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
                         {this.dockingContent}
@@ -964,7 +965,11 @@ export class MainView extends React.Component {
     render() {
         return (
             <div
-                className={`mainView-container${this.colorScheme}`}
+                className={`mainView-container ${this.colorScheme}`}
+                style={{
+                    color: StrCast(Doc.UserDoc().userColor),
+                    background: StrCast(Doc.UserDoc().userBackgroundColor),
+                }}
                 onScroll={() => (ele => (ele.scrollTop = ele.scrollLeft = 0))(document.getElementById('root')!)}
                 ref={r => {
                     r &&
diff --git a/src/client/views/MainViewModal.scss b/src/client/views/MainViewModal.scss
index 0648e31c5..4bf9eb79f 100644
--- a/src/client/views/MainViewModal.scss
+++ b/src/client/views/MainViewModal.scss
@@ -4,22 +4,23 @@
     z-index: 10000;
     width: 100%;
     height: 100%;
+    box-shadow: #00000044 5px 5px 10px;
 
     .dialogue-box {
-        padding: 10px;
         position: absolute;
         z-index: 1000;
         text-align: center;
         justify-content: center;
         align-self: center;
         align-content: center;
-        background: white;
         // border-radius: 10px;
         box-shadow: #00000044 5px 5px 10px;
         transform: translate(-50%, -50%);
         top: 50%;
         left: 50%;
         transition: 0.5s all ease;
+        border-radius: 10px;
+        overflow: hidden;
     }
 
     .overlay {
@@ -28,6 +29,7 @@
         position: absolute;
         z-index: 999;
         transition: 0.5s all ease;
+        backdrop-filter: blur(5px);
     }
 
 }
\ No newline at end of file
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index 32997a944..42df99864 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -1,6 +1,10 @@
 import * as React from 'react';
 import './MainViewModal.scss';
 import { observer } from 'mobx-react';
+import { Doc } from '../../fields/Doc';
+import { StrCast } from '../../fields/Types';
+import { isDark } from 'browndash-components';
+import { Colors } from './global/globalEnums';
 
 export interface MainViewOverlayProps {
     isDisplayed: boolean;
@@ -41,9 +45,8 @@ export class MainViewModal extends React.Component<MainViewOverlayProps> {
                     className="overlay"
                     onClick={this.props?.closeOnExternalClick}
                     style={{
-                        backgroundColor: 'black',
+                        backgroundColor: isDark(StrCast(Doc.UserDoc().userColor)) ? "#DFDFDF30" : "#32323230",
                         ...(p.overlayStyle || {}),
-                        opacity: p.isDisplayed ? overlayOpacity : 0,
                     }}
                 />
             </div>
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
index 36b2df73e..db30745fc 100644
--- a/src/client/views/PropertiesButtons.scss
+++ b/src/client/views/PropertiesButtons.scss
@@ -57,14 +57,13 @@ $linkGap : 3px;
 }
 
 .propertiesButtons {
-    margin-top: 3px;
-    grid-column: 1/4;
     width: 100%;
     height: auto;
     display: flex;
     flex-direction: row;
     flex-wrap: wrap;
     padding-bottom: 5.5px;
+    gap: 5px;
 }
 
 .onClickFlyout-editScript {
@@ -81,10 +80,7 @@ $linkGap : 3px;
 .propertiesButtons-button {
     pointer-events: auto;
     padding-right: 5px;
-    width: 25px;
-    border-radius: 5px;
-    margin-right: 20px;
-    margin-bottom: 8px;
+    width: 100%;
 }
 
 .propertiesButton-dropdownList {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 74dd1c2f7..3148d9ddd 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -21,6 +21,7 @@ import { DocumentView, OpenWhere } from './nodes/DocumentView';
 import { pasteImageBitmap } from './nodes/WebBoxRenderer';
 import './PropertiesButtons.scss';
 import React = require('react');
+import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -44,24 +45,23 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     propertyToggleBtn = (label: string, property: string, tooltip: (on?: any) => string, icon: (on: boolean) => string, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
         const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedDoc;
         const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => ((dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true);
-        return !targetDoc ? null : (
-            <Tooltip title={<div className={`dash-tooltip`}>{tooltip(targetDoc?.[property])} </div>} placement="top">
-                <div>
-                    <div
-                        className={`propertiesButtons-linkButton-empty toggle-${StrCast(targetDoc[property]).includes(':hover') ? 'hover' : targetDoc[property] ? 'on' : 'off'}`}
-                        onPointerDown={e => e.stopPropagation()}
-                        onClick={undoable(() => {
-                            if (SelectionManager.Views().length > 1) {
-                                SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property));
-                            } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
-                        }, property)}>
-                        <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon={icon(BoolCast(targetDoc?.[property])) as any} />
-                    </div>
-                    <div className="propertiesButtons-title">{label}</div>
-                </div>
-            </Tooltip>
-        );
+        return !targetDoc ? null : <Toggle
+            toggleStatus={BoolCast(targetDoc[property])}
+            text={tooltip(targetDoc?.[property])}
+            color={StrCast(Doc.UserDoc().userColor)}
+            icon={<FontAwesomeIcon className="documentdecorations-icon" size="lg" icon={icon(BoolCast(targetDoc?.[property])) as any} />}
+            iconPlacement={'left'}
+            align={'flex-start'}
+            fillWidth={true}
+            toggleType={ToggleType.BUTTON}
+            onClick={undoable(() => {
+                if (SelectionManager.Views().length > 1) {
+                    SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property));
+                } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
+            }, property)}
+        />
     };
+
     @computed get lockButton() {
         return this.propertyToggleBtn(
             'No\xA0Drag',
@@ -244,21 +244,61 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         );
     }
 
+    @computed get onClickVal() {
+        const linkButton = IsFollowLinkScript(this.selectedDoc.onClick);
+        const followLoc = this.selectedDoc._followLinkLocation;
+        const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox);
+
+        if (followLoc === OpenWhere.lightbox && !linkedToLightboxView()) return 'linkInPlace'
+        else if (linkButton && followLoc === OpenWhere.addRight) return 'linkOnRight'
+        else if (linkButton && this.selectedDoc._followLinkLocation === OpenWhere.lightbox && linkedToLightboxView()) return 'enterPortal'
+        else if (ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('toggleDetail')) return 'toggleDetail'
+        else return 'nothing'
+    }
+
     @computed
     get onClickButton() {
+        const buttonList = [
+            ['nothing', 'Select Document'],
+            ['enterPortal', 'Enter Portal'],
+            ['toggleDetail', 'Toggle Detail'],
+            ['linkInPlace', 'Open Link in Lightbox'],
+            ['linkOnRight', 'Open Link on Right'],
+        ];
+        
+        const items: IListItemProps[] = buttonList.map(value => {
+            return (
+                {
+                    text: value[1],
+                    val: value[1],
+                }
+            );
+        });
+        console.log("click val: ", this.onClickVal)
         return !this.selectedDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">Choose onClick behavior</div>} placement="top">
-                <div>
-                    <div className="propertiesButtons-linkFlyout">
-                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onClickFlyout}>
-                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
-                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                            </div>
-                        </Flyout>
-                    </div>
-                    <div className="propertiesButtons-title"> onclick </div>
-                </div>
-            </Tooltip>
+            <Dropdown
+                tooltip={'Choose onClick behavior'} 
+                items={items}
+                selectedVal={this.onClickVal}
+                setSelectedVal={(val) => this.handleOptionChange(val as string)}
+                title={'Choose onClick behaviour'}
+                color={StrCast(Doc.UserDoc().userColor)}
+                dropdownType={DropdownType.SELECT}
+                type={Type.SEC}
+                fillWidth
+            />
+            // <Tooltip title={<div className="dash-tooltip">Choose onClick behavior</div>} placement="top">
+            //     <div>
+            //         <div className="propertiesButtons-linkFlyout">
+            //             <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onClickFlyout}>
+            //                 <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
+            //                     <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
+            //                 </div>
+            //             </Flyout>
+            //         </div>
+            //         <div className="propertiesButtons-title"> onclick </div>
+            //     </div>
+            // </Tooltip>
         );
     }
     @computed
diff --git a/src/client/views/PropertiesDocBacklinksSelector.scss b/src/client/views/PropertiesDocBacklinksSelector.scss
index 4d2a61c3b..5c53acf48 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.scss
+++ b/src/client/views/PropertiesDocBacklinksSelector.scss
@@ -1,4 +1,4 @@
-.propertiesView-contexts-content {
+.propertiesDocBacklinksSelector {
     .linkMenu {
         position: relative;
     }
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index 3e69bcba6..da4438481 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -31,7 +31,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
 
     render() {
         return !SelectionManager.Views().length ? null : (
-            <div className="preroptiesDocBacklinksSelector">
+            <div className="propertiesDocBacklinksSelector">
                 {this.props.hideTitle ? null : <p key="contexts">Contexts:</p>}
                 <LinkMenu docView={SelectionManager.Views().lastElement()} clearLinkEditor={undefined} itemHandler={this.getOnClick} style={{ left: 0, top: 0 }} />
             </div>
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index e1279c9a7..6cac7b0fe 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -53,6 +53,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
     };
 
     render() {
+        if (this._docs.length < 1) return undefined
         return (
             <div>
                 {this.props.hideTitle ? null : <p key="contexts">Contexts:</p>}
diff --git a/src/client/views/PropertiesSection.scss b/src/client/views/PropertiesSection.scss
new file mode 100644
index 000000000..79479a4ce
--- /dev/null
+++ b/src/client/views/PropertiesSection.scss
@@ -0,0 +1,24 @@
+@import './global/globalCssVariables.scss';
+
+.propertiesView-section {
+    
+    .propertiesView-content {
+        padding: 10px;
+    }
+    
+    .propertiesView-sectionTitle {
+        text-align: center;
+        display: flex;
+        padding: 10px;
+        font-size: 14px;
+        font-weight: bold;
+        justify-content: space-between;
+        align-items: center;
+    
+        .propertiesView-sectionTitle-icon {
+            width: 20px;
+            height: 20px;
+            align-items: flex-end;
+        }
+    }
+}
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
new file mode 100644
index 000000000..b900d17ca
--- /dev/null
+++ b/src/client/views/PropertiesSection.tsx
@@ -0,0 +1,51 @@
+import React = require('react');
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
+import { action, computed } 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 
+}
+
+@observer
+export class PropertiesSection extends React.Component<PropertiesSectionProps> {
+    @computed get color() {
+        return StrCast(Doc.UserDoc().userColor);
+    }
+
+    @computed get backgroundColor() {
+        return StrCast(Doc.UserDoc().userBackgroundColor);
+    }
+
+    @computed get variantColor() {
+        return StrCast(Doc.UserDoc().userVariantColor);
+    }
+    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" onPointerDown={action(() => (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.scss b/src/client/views/PropertiesView.scss
index 897be9a32..889f5d5cf 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -6,122 +6,31 @@
     font-family: 'Roboto';
     font-size: 12px;
     cursor: auto;
+    border-left: $standard-border;
 
     .slider-text {
         font-size: 8px;
     }
 
-    overflow-x: hidden;
-    overflow-y: auto;
-
     .propertiesView-title {
-        text-align: center;
-        padding-top: 12px;
-        padding-bottom: 12px;
-        display: flex;
-        font-size: 18px;
+        padding: 10px;
+        font-size: 24px;
         font-weight: bold;
-        justify-content: center;
-
-        .propertiesView-title-icon {
-            width: 20px;
-            height: 20px;
-            padding-left: 38px;
-            margin-top: -5px;
-            align-items: flex-end;
-            margin-left: auto;
-            margin-right: 10px;
-
-            &:hover {
-                color: grey;
-                cursor: pointer;
-            }
-        }
     }
 
-    .propertiesView-name {
-        border-bottom: 1px solid black;
-        padding: 8.5px;
-        font-size: 12.5px;
+    overflow-x: hidden;
+    overflow-y: auto;
 
-        &:hover {
-            cursor: text;
-        }
+    .propertiesView-name,
+    .propertiesView-type {
+        padding: 0px 10px;
     }
 
-    .propertiesView-settings {
-        //border-bottom: 1px solid black;
-        //padding: 8.5px;
-        font-size: 12.5px;
-        font-weight: bold;
-
-        .propertiesView-settings-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-settings-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-settings-content {
-            margin-left: 12px;
-            padding-bottom: 10px;
-            padding-top: 8px;
-        }
-    }
 
     .propertiesView-sharing {
         //border-bottom: 1px solid black;
         //padding: 8.5px;
 
-        .propertiesView-sharing-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-sharing-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-sharing-content {
-            font-size: 10px;
-            padding: 10px;
-            margin-left: 5px;
 
             .propertiesView-buttonContainer {
                 float: right;
@@ -157,40 +66,13 @@
                     width: 100%;
                 }
             }
-        }
     }
 
     .propertiesView-filters {
         //border-bottom: 1px solid black;
         //padding: 8.5px;
 
-        .propertiesView-filters-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-filters-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-filters-content {
+        .propertiesView-content {
             font-size: 10px;
             padding: 10px;
             margin-left: 5px;
@@ -219,85 +101,10 @@
         }
     }
 
-    .propertiesView-appearance {
-        //border-bottom: 1px solid black;
-        //padding: 8.5px;
-
-        .propertiesView-appearance-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-appearance-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-appearance-content {
-            font-size: 10px;
-            padding: 10px;
-            margin-left: 5px;
-        }
-    }
-
-    .propertiesView-transform {
-        //border-bottom: 1px solid black;
-        //padding: 8.5px;
-
-        .propertiesView-transform-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-transform-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-transform-content {
-            font-size: 10px;
-            padding: 10px;
-            margin-left: 5px;
-        }
-    }
-
     .notify-button {
         padding: 2px;
         width: 12px;
         height: 12px;
-        background-color: black;
         border-radius: 10px;
         padding-left: 2px;
         padding-right: 2px;
@@ -393,58 +200,6 @@
         }
     }
 
-    .propertiesView-fields {
-        //border-bottom: 1px solid black;
-        //padding: 8.5px;
-
-        .propertiesView-fields-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-fields-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-fields-checkbox {
-            float: right;
-            height: 20px;
-            margin-top: -9px;
-
-            .propertiesView-fields-checkbox-text {
-                font-size: 7px;
-                margin-top: -10px;
-                margin-left: 6px;
-            }
-        }
-
-        .propertiesView-fields-content {
-            font-size: 10px;
-            margin-left: 2px;
-            padding: 10px;
-
-            &:hover {
-                cursor: pointer;
-            }
-        }
-    }
 
     .propertiesView-field {
         display: flex;
@@ -466,109 +221,14 @@
         }
     }
 
-    .propertiesView-contexts {
-        .propertiesView-contexts-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-contexts-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-contexts-content {
-            overflow: hidden;
-            padding: 10px;
-        }
-    }
-
-    .propertiesView-layout {
-        .propertiesView-layout-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-layout-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-layout-content {
-            overflow: hidden;
-            padding: 10px;
-        }
     }
 
     .propertiesView-presTrails {
         //border-bottom: 1px solid black;
         //padding: 8.5px;
 
-        .propertiesView-presTrails-title {
-            font-weight: bold;
-            font-size: 12.5px;
-            padding: 4px;
-            display: flex;
-            color: white;
-            padding-left: 8px;
-            background-color: rgb(51, 51, 51);
-
-            &:hover {
-                cursor: pointer;
-            }
-
-            .propertiesView-presTrails-title-icon {
-                float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
-
-                &:hover {
-                    cursor: pointer;
-                }
-            }
-        }
-
-        .propertiesView-presTrails-content {
-            font-size: 10px;
-            padding: 10px;
-            margin-left: 5px;
-        }
+      
     }
-}
 
 .inking-button {
     display: flex;
@@ -847,10 +507,6 @@
     padding: 5%;
 }
 
-.propertiesView-section {
-    padding-left: 20px;
-}
-
 .propertiesView-input {
     padding: 4px 8px;
 
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 09aac053a..e86506f92 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -35,6 +35,8 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+import { Button, EditableText, NumberDropdown, Size, Slider, Type } from 'browndash-components';
+import { PropertiesSection } from './PropertiesSection';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -332,7 +334,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" color="white" size="sm" />
+                    <FontAwesomeIcon className="notify-button-icon" icon="bell"  size="sm" />
                 </div>
             </Tooltip>
         );
@@ -435,29 +437,57 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @action
     toggleCheckbox = () => (this.layoutFields = !this.layoutFields);
 
+    @computed get color() {
+        return StrCast(Doc.UserDoc().userColor);
+    }
+
+    @computed get backgroundColor() {
+        return StrCast(Doc.UserDoc().userBackgroundColor);
+    }
+
+    @computed get variantColor() {
+        return StrCast(Doc.UserDoc().userVariantColor);
+    }
+
     @computed get editableTitle() {
         const titles = new Set<string>();
-        SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
         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 (
-            <div className="editable-title">
-                <EditableView key="editableView" contents={title} height={25} fontSize={14} GetValue={() => title} SetValue={this.setTitle} />
-            </div>
+            <EditableText
+                val={title}
+                setVal={this.setTitle}
+                color={this.color}
+                type={Type.SEC}
+                formLabel={"Title"}
+                fillWidth
+            />
+        );
+    }
+
+    @computed get type() {
+        const type = StrCast(this.selectedDoc?.type);
+        return (
+            <Button
+                formLabel={"Type"}
+                text={type}
+                color={this.color}
+                align='flex-start'
+                fillWidth
+            />
         );
     }
 
     @undoBatch
     @action
-    setTitle = (value: string) => {
+    setTitle = (value: string | number) => {
+        console.log(value)
         if (SelectionManager.Views().length > 1) {
             SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true));
-            return true;
         } else if (this.dataDoc) {
             if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true);
-            else KeyValueBox.SetField(this.dataDoc, 'title', value, true);
-            return true;
+            else KeyValueBox.SetField(this.dataDoc, 'title', value as string, true);
         }
-        return false;
     };
 
     @undoBatch
@@ -515,7 +545,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" color="white" size="lg" />
+                        <FontAwesomeIcon icon="bezier-curve"  size="lg" />
                     </div>
                 </Tooltip>
             </div>
@@ -534,10 +564,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" color="white" 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" color="white" size="sm" />
+                        <FontAwesomeIcon icon="caret-down"  size="sm" />
                     </div>
                 </div>
             </div>
@@ -791,10 +821,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" color="white" 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" color="white" size="sm" />
+                        <FontAwesomeIcon icon="caret-down"  size="sm" />
                     </div>
                 </div>
             </div>
@@ -884,6 +914,29 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return (
             <div className="transform-editor">
                 {this.isInk ? this.controlPointsButton : null}
+                <Slider 
+                    formLabel={'Height'} 
+                    multithumb={false}
+                    color={this.color}
+                    size={Size.XSMALL} 
+                    min={0} 
+                    max={1000} 
+                    number={Number(this.shapeHgt)}
+                    setNumber={undoable((val: string) => !isNaN(Number(val)) && (this.shapeHgt = val), 'set height')}
+                    fillWidth
+                />
+                <Slider 
+                    formLabel={'Width'} 
+                    multithumb={false}
+                    color={this.color}
+                    size={Size.XSMALL} 
+                    min={0} 
+                    max={1000} 
+                    number={Number(this.shapeWid)}
+                    setNumber={undoable((val: string) => !isNaN(Number(val)) && (this.shapeWid = val), 'set width')}
+                    fillWidth
+                />
+
                 {this.hgtInput}
                 {this.XpsInput}
             </div>
@@ -891,52 +944,38 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get optionsSubMenu() {
-        return (
-            <div className="propertiesView-settings" onPointerEnter={action(() => (this.inOptions = true))} onPointerLeave={action(() => (this.inOptions = false))}>
-                <div className="propertiesView-settings-title" onPointerDown={action(() => (this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}>
-                    Options
-                    <div className="propertiesView-settings-title-icon">
-                        <FontAwesomeIcon icon={this.openOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                    </div>
-                </div>
-                {!this.openOptions ? null : (
-                    <div className="propertiesView-settings-content">
-                        <PropertiesButtons />
-                    </div>
-                )}
-            </div>
-        );
+        return <PropertiesSection
+            title="Options"
+            content={<PropertiesButtons />}
+            inSection={this.inOptions}
+            isOpen={this.openOptions}
+            setInSection={(bool) => this.inOptions = bool}
+            setIsOpen={(bool) => this.openOptions = bool}
+        />
     }
 
     @computed get sharingSubMenu() {
-        return (
-            <div className="propertiesView-sharing">
-                <div className="propertiesView-sharing-title" onPointerDown={action(() => (this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}>
-                    Sharing {'&'} Permissions
-                    <div className="propertiesView-sharing-title-icon">
-                        <FontAwesomeIcon icon={this.openSharing ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                    </div>
-                </div>
-                {!this.openSharing ? null : (
-                    <div className="propertiesView-sharing-content">
-                        <div className="propertiesView-buttonContainer">
-                            {!Doc.noviceMode ? (
-                                <div className="propertiesView-acls-checkbox">
-                                    <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
-                                    <div className="propertiesView-acls-checkbox-text">Layout</div>
-                                </div>
-                            ) : null}
-                            {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
-                                        <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
-                                            <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
-                                        </button>
-                                    </Tooltip> */}
+        return <PropertiesSection
+            title="Sharing & Permissions"
+            content={<>
+            <div className="propertiesView-buttonContainer">
+                {!Doc.noviceMode ? (
+                        <div className="propertiesView-acls-checkbox">
+                            <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
+                            <div className="propertiesView-acls-checkbox-text">Layout</div>
                         </div>
-                        {this.sharingTable}
-                    </div>
-                )}
-            </div>
-        );
+                    ) : null}
+                    {/* <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}
+        />
     }
 
     /**
@@ -964,111 +1003,71 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     };
 
     @computed get filtersSubMenu() {
-        return (
-            <div className="propertiesView-filters">
-                <div className="propertiesView-filters-title" onPointerDown={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
-                    Filters
-                    <div className="propertiesView-filters-title-icon">
-                        <FontAwesomeIcon icon={this.openFilters ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                    </div>
-                </div>
-                {!this.openFilters ? null : (
-                    <div className="propertiesView-filters-content" style={{ position: 'relative', height: 'auto' }}>
-                        <FilterPanel rootDoc={this.selectedDoc ?? Doc.ActiveDashboard!} />
-                    </div>
-                )}
-            </div>
-        );
+        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}
+        />
     }
 
     @computed get inkSubMenu() {
         return (
             <>
-                {!this.isInk ? null : (
-                    <div className="propertiesView-appearance">
-                        <div className="propertiesView-appearance-title" onPointerDown={action(() => (this.openAppearance = !this.openAppearance))} style={{ backgroundColor: this.openAppearance ? 'black' : '' }}>
-                            Appearance
-                            <div className="propertiesView-appearance-title-icon">
-                                <FontAwesomeIcon icon={this.openAppearance ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                            </div>
-                        </div>
-                        {!this.openAppearance ? null : <div className="propertiesView-appearance-content">{this.appearanceEditor}</div>}
-                    </div>
-                )}
-
-                <div className="propertiesView-transform">
-                    <div className="propertiesView-transform-title" onPointerDown={action(() => (this.openTransform = !this.openTransform))} style={{ backgroundColor: this.openTransform ? 'black' : '' }}>
-                        Transform
-                        <div className="propertiesView-transform-title-icon">
-                            <FontAwesomeIcon icon={this.openTransform ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                        </div>
-                    </div>
-                    {this.openTransform ? <div className="propertiesView-transform-content">{this.transformEditor}</div> : null}
-                </div>
+                <PropertiesSection
+                    title="Appearance"
+                    content={this.isInk ? this.appearanceEditor : null}
+                    isOpen={this.openAppearance}
+                    setIsOpen={(bool) => this.openAppearance = bool}
+                />
+                <PropertiesSection
+                    title="Transform"
+                    content={this.transformEditor}
+                    isOpen={this.openTransform}
+                    setIsOpen={(bool) => this.openTransform = bool}
+                />
             </>
         );
     }
 
     @computed get fieldsSubMenu() {
-        return (
-            <div className="propertiesView-fields">
-                <div className="propertiesView-fields-title" onPointerDown={action(() => (this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}>
-                    Fields {'&'} Tags
-                    <div className="propertiesView-fields-title-icon">
-                        <FontAwesomeIcon icon={this.openFields ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                    </div>
-                </div>
-                {!Doc.noviceMode && this.openFields ? (
-                    <div className="propertiesView-fields-checkbox">
-                        {this.fieldsCheckbox}
-                        <div className="propertiesView-fields-checkbox-text">Layout</div>
-                    </div>
-                ) : null}
-                {!this.openFields ? null : <div className="propertiesView-fields-content">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>}
-            </div>
-        );
+        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}
+        />
     }
 
     @computed get contextsSubMenu() {
-        return (
-            <div className="propertiesView-contexts">
-                <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}>
-                    Other Contexts
-                    <div className="propertiesView-contexts-title-icon">
-                        <FontAwesomeIcon icon={this.openContexts ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                    </div>
-                </div>
-                {this.openContexts ? <div className="propertiesView-contexts-content">{this.contexts}</div> : null}
-            </div>
-        );
+        return <PropertiesSection
+            title="Other Contexts"
+            content={this.contexts}
+            isOpen={this.openContexts}
+            setIsOpen={(bool) => this.openContexts = bool}
+        />
     }
 
     @computed get linksSubMenu() {
-        return (
-            <div className="propertiesView-contexts">
-                <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
-                    Linked To
-                    <div className="propertiesView-contexts-title-icon">
-                        <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                    </div>
-                </div>
-                {this.openLinks ? <div className="propertiesView-contexts-content">{this.links}</div> : null}
-            </div>
-        );
+        return <PropertiesSection
+            title="Linked To"
+            content={this.links}
+            isOpen={this.openLinks}
+            setIsOpen={(bool) => this.openLinks = bool}
+        />
     }
 
     @computed get layoutSubMenu() {
-        return (
-            <div className="propertiesView-layout">
-                <div className="propertiesView-layout-title" onPointerDown={action(() => (this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}>
-                    Layout
-                    <div className="propertiesView-layout-title-icon">
-                        <FontAwesomeIcon icon={this.openLayout ? 'caret-down' : 'caret-right'} size="lg" color="white" />
-                    </div>
-                </div>
-                {this.openLayout ? <div className="propertiesView-layout-content">{this.layoutPreview}</div> : null}
-            </div>
-        );
+        return <PropertiesSection
+            title="Layout"
+            content={this.layoutPreview}
+            isOpen={this.openLayout}
+            setIsOpen={(bool) => this.openLayout = bool}
+        />
     }
 
     @computed get description() {
@@ -1269,6 +1268,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.fullScreen}>Overlaying current 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>
+                {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>
+        )}
+    </>
+    }
 
     /**
      * Handles adding and removing members from the sharing panel
@@ -1283,9 +1502,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     render() {
         const isNovice = Doc.noviceMode;
-        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!);
         if (!this.selectedDoc && !this.isPres) {
             return (
@@ -1301,243 +1517,26 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     <div
                         className="propertiesView"
                         style={{
+                            background: StrCast(Doc.UserDoc().userBackgroundColor),
+                            color: StrCast(Doc.UserDoc().userColor),
                             width: this.props.width,
                             minWidth: this.props.width,
-                            //overflowY: this.scrolling ? "scroll" : "visible"
                         }}>
                         <div className="propertiesView-title" style={{ width: this.props.width }}>
                             Properties
                         </div>
                         <div className="propertiesView-name">{this.editableTitle}</div>
-
+                        <div className="propertiesView-type">{this.type}</div>
                         {this.contextsSubMenu}
-
                         {this.linksSubMenu}
                         {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : (
-                            <>
-                                <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.fullScreen}>Overlaying current 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>
-                                        {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>
-                                )}
-                            </>
+                            this.linkProperties
                         )}
-
                         {this.inkSubMenu}
-
                         {this.optionsSubMenu}
-
                         {this.fieldsSubMenu}
-
                         {isNovice ? null : this.sharingSubMenu}
-
                         {isNovice ? null : this.filtersSubMenu}
-
                         {isNovice ? null : this.layoutSubMenu}
                     </div>
                 );
@@ -1549,7 +1548,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     : PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type;
                 return (
                     <div className="propertiesView" style={{ width: this.props.width }}>
-                        <div className="propertiesView-title" style={{ width: this.props.width }}>
+                        <div className="propertiesView-sectionTitle" style={{ width: this.props.width }}>
                             Presentation
                         </div>
                         <div className="propertiesView-name" style={{ borderBottom: 0 }}>
@@ -1564,7 +1563,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" color="white" />
+                                        <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg"  />
                                     </div>
                                 </div>
                                 {this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
@@ -1578,7 +1577,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" color="white" />
+                                        <FontAwesomeIcon icon={this.openPresVisibilityAndDuration ? 'caret-down' : 'caret-right'} size="lg"  />
                                     </div>
                                 </div>
                                 {this.openPresVisibilityAndDuration ? <div className="propertiesView-presTrails-content">{PresBox.Instance.visibiltyDurationDropdown}</div> : null}
@@ -1589,7 +1588,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" color="white" />
+                                        <FontAwesomeIcon icon={this.openPresProgressivize ? 'caret-down' : 'caret-right'} size="lg"  />
                                     </div>
                                 </div>
                                 {this.openPresProgressivize ? <div className="propertiesView-presTrails-content">{PresBox.Instance.progressivizeDropdown}</div> : null}
@@ -1600,7 +1599,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" color="white" />
+                                        <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 96502115d..472eefa6a 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -160,7 +160,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 ''
             );
         case StyleProp.Color:
-            if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
+            if (MainView.Instance.LastButton === doc) return Doc.UserDoc().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));
             if (docColor) return docColor;
             const docView = props?.DocumentView?.();
@@ -196,7 +198,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 ? 15
                 : 0;
         case StyleProp.BackgroundColor: {
-            if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
+            if (MainView.Instance.LastButton === doc) return StrCast(Doc.UserDoc().userColor); // hack to indicate active menu panel item
             let docColor: Opt<string> = StrCast(doc?.[fieldKey + '_backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
             // prettier-ignore
             switch (doc?.type) {
@@ -221,9 +223,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)
-                            ? darkScheme()
-                                ? undefined
-                                : undefined // system docs (seen in treeView) get a grayish background
+                            ? StrCast(Doc.UserDoc().userBackgroundColor)
                             : doc.annotationOn
                             ? '#00000010' // faint interior for collections on PDFs, images, etc
                             : doc?._isGroup
diff --git a/src/client/views/UndoStack.scss b/src/client/views/UndoStack.scss
index ab21e6d7e..192b99a12 100644
--- a/src/client/views/UndoStack.scss
+++ b/src/client/views/UndoStack.scss
@@ -2,9 +2,10 @@
     height: 100%;
     display: flex;
     flex-direction: column;
+    justify-content: center;
+    align-items: center;
     position: relative;
     pointer-events: all;
-    padding-left: 4px;
 }
 
 .undoStack-resultContainer {
@@ -21,9 +22,11 @@
 }
 
 .undoStack-commandsContainer {
-    background-color: whitesmoke;
     flex: 1 1 auto;
     overflow-y: scroll;
-    height: 30px;
+    height: fit-content;
+    width: 200px;
     border-radius: 5px;
+    padding: 5px;
+    max-height: 200px;
 }
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index f5af09e5b..28f1b8bd9 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -3,6 +3,9 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { UndoManager } from '../util/UndoManager';
 import './UndoStack.scss';
+import { StrCast } from '../../fields/Types';
+import { Doc } from '../../fields/Doc';
+import { Popup, Type } from 'browndash-components';
 
 interface UndoStackProps {
     width?: number;
@@ -15,32 +18,31 @@ export class UndoStack extends React.Component<UndoStackProps> {
     @observable static Expand: boolean;
     render() {
         return this.props.inline && UndoStack.HideInline ? null : (
-            <div
-                className="undoStack-outerContainer"
-                style={{ width: this.props.width, height: this.props.height ? (UndoStack.Expand ? 4 : 1) * this.props.height : undefined, top: UndoStack.Expand && this.props.height ? -this.props.height * 3 : undefined }}
-                onClick={action(e => (UndoStack.Expand = !UndoStack.Expand))}
-                onDoubleClick={action(e => (UndoStack.Expand = UndoStack.HideInline = false))}>
-                <div className="undoStack-commandsContainer" ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })} style={{ background: UndoManager.batchCounter.get() ? 'yellow' : undefined }}>
-                    <div className="undoStack-resultContainer" key={0}>
-                        <div className="undoStack-commandString" style={{ fontWeight: 'bold', textAlign: 'center' }}>
-                            Undo/Redo Stack
-                        </div>
-                    </div>
-                    {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-outerContainer">
+                <Popup 
+                    text={'Undo/Redo Stack'}
+                    color={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: UndoManager.batchCounter.get() ? 'yellow' : undefined }}>
+                        {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/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index 9db8fe532..6eeccc94e 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -10,650 +10,13 @@
     border-bottom: $standard-border;
     padding: 0 10px;
     align-items: center;
+    overflow-x: scroll;
+    &::-webkit-scrollbar {
+        display: none;
+    }
 
-    .collectionMenu-hardCodedButton {
-        cursor: pointer;
-        color: $white;
-        width: 25px;
-        height: 25px;
-        padding: 5;
-        text-align: center;
+    .hardCodedButtons {
         display: flex;
-        justify-content: center;
-        align-items: center;
-        position: relative;
-        transition: 0.2s;
-        border-radius: 3px;
-
-        &:hover {
-            background-color: rgba(0, 0, 0, 0.2);
-        }
+        flex-direction: row;
     }
-}
-
-// .collectionMenu-cont {
-//     position: relative;
-//     display: inline-flex;
-//     width: 100%;
-//     opacity: 0.9;
-//     z-index: 901;
-//     transition: top .5s;
-//     background: $dark-gray;
-//     color: $white;
-//     transform-origin: top left;
-//     top: 0;
-//     width: 100%;
-
-//     .recordButtonOutline {
-//         border-radius: 100%;
-//         width: 18px;
-//         height: 18px;
-//         border: solid 1px $white;
-//         display: flex;
-//         align-items: center;
-//         justify-content: center;
-//     }
-
-//     .recordButtonInner {
-//         border-radius: 100%;
-//         width: 70%;
-//         height: 70%;
-//         background: $white;
-//     }
-
-//     .collectionMenu {
-//         display: flex;
-//         height: 100%;
-//         overflow: visible;
-//         z-index: 901;
-//         border: unset;
-
-//         .collectionMenu-divider {
-//             height: 100%;
-//             margin-left: 3px;
-//             margin-right: 3px;
-//             width: 2px;
-//             background-color: $medium-gray;
-//         }
-
-//         .collectionViewBaseChrome {
-//             display: flex;
-//             align-items: center;
-
-//             .collectionViewBaseChrome-viewPicker {
-//                 font-size: $small-text;
-//                 outline-color: $black;
-//                 color: $white;
-//                 border: none;
-//                 background: $dark-gray;               
-//             }
-
-//             .collectionViewBaseChrome-viewPicker:focus {
-//                 outline: none;
-//                 border: none;
-//             }
-
-//             .collectionViewBaseChrome-viewPicker:active {
-//                 outline-color: $black;
-//             }
-
-//             .collectionViewBaseChrome-button {
-//                 font-size: $small-text;
-//                 text-transform: uppercase;
-//                 letter-spacing: 2px;
-//                 background: $white;
-//                 color: $pink;
-//                 outline-color: $black;
-//                 border: none;
-//                 padding: 12px 10px 11px 10px;
-//                 margin-left: 10px;
-//             }
-
-//             .collectionViewBaseChrome-cmdPicker {
-//                 margin-left: 3px;
-//                 margin-right: 0px;
-//                 font-size: $small-text;
-//                 text-transform: capitalize;
-//                 color: $white;
-//                 border: none;
-//                 background: $dark-gray;     
-//             }
-
-//             .collectionViewBaseChrome-cmdPicker:focus {
-//                 border: none;
-//                 outline: none;
-//             }
-
-//             .commandEntry-outerDiv {
-//                 pointer-events: all;
-//                 background-color: transparent;
-//                 display: flex;
-//                 flex-direction: row;
-//                 align-items: center;
-//                 justify-content: center;
-//                 height: 100%;
-//                 overflow: hidden;
-
-//                 .commandEntry-drop {
-//                     color: $white;
-//                     width: 30px;
-//                     margin-top: auto;
-//                     margin-bottom: auto;
-//                 }
-//             }
-
-//             .commandEntry-outerDiv:hover{
-//                 background-color: $drop-shadow;
-
-//                 .collectionViewBaseChrome-viewPicker,
-//                 .collectionViewBaseChrome-cmdPicker{
-//                     background: $dark-gray;
-//                 }
-//             }
-
-//             .collectionViewBaseChrome-collapse {
-//                 transition: all .5s, opacity 0.3s;
-//                 position: absolute;
-//                 width: 30px;
-//                 transform-origin: top left;
-//                 pointer-events: all;
-//                 // margin-top: 10px;
-//             }
-
-//             @media only screen and (max-device-width: 480px) {
-//                 .collectionViewBaseChrome-collapse {
-//                     display: none;
-//                 }
-//             }
-
-//             .collectionViewBaseChrome-template,
-//             .collectionViewBaseChrome-viewModes {
-//                 align-items: center;
-//                 height: 100%;
-//                 display: flex;
-//                 background: transparent;
-//                 color: $medium-gray;
-//                 justify-content: center;
-//             }
-
-//             .collectionViewBaseChrome-viewSpecs {
-//                 margin-left: 5px;
-//                 display: grid;
-//                 border: none;
-//                 border-right: solid $medium-gray 1px;
-
-//                 .collectionViewBaseChrome-filterIcon {
-//                     position: relative;
-//                     display: flex;
-//                     margin: auto;
-//                     background: $dark-gray;
-//                     color: $white;
-//                     width: 30px;
-//                     height: 30px;
-//                     align-items: center;
-//                     justify-content: center;
-//                     border: none;
-//                     border-right: solid $medium-gray 1px;
-//                 }
-
-//                 .collectionViewBaseChrome-viewSpecsInput {
-//                     padding: 12px 10px 11px 10px;
-//                     border: 0px;
-//                     color: $medium-gray;
-//                     text-align: center;
-//                     letter-spacing: 2px;
-//                     outline-color: $black;
-//                     font-size: $small-text;
-//                     background: $white;
-//                     height: 100%;
-//                     width: 75px;
-//                 }
-
-//                 .collectionViewBaseChrome-viewSpecsMenu {
-//                     overflow: hidden;
-//                     transition: height .5s, display .5s;
-//                     position: absolute;
-//                     top: 60px;
-//                     z-index: 100;
-//                     display: flex;
-//                     flex-direction: column;
-//                     background: $white;
-//                     box-shadow: $medium-gray 2px 2px 4px;
-
-//                     .qs-datepicker {
-//                         left: unset;
-//                         right: 0;
-//                     }
-
-//                     .collectionViewBaseChrome-viewSpecsMenu-row {
-//                         display: grid;
-//                         grid-template-columns: 150px 200px 150px;
-//                         margin-top: 10px;
-//                         margin-right: 10px;
-
-//                         .collectionViewBaseChrome-viewSpecsMenu-rowLeft,
-//                         .collectionViewBaseChrome-viewSpecsMenu-rowMiddle,
-//                         .collectionViewBaseChrome-viewSpecsMenu-rowRight {
-//                             font-size: $small-text;
-//                             letter-spacing: 2px;
-//                             color: $medium-gray;
-//                             margin-left: 10px;
-//                             padding: 5px;
-//                             border: none;
-//                             outline-color: $black;
-//                         }
-//                     }
-
-//                     .collectionViewBaseChrome-viewSpecsMenu-lastRow {
-//                         display: grid;
-//                         grid-template-columns: 1fr 1fr 1fr;
-//                         grid-gap: 10px;
-//                         margin: 10px;
-//                     }
-//                 }
-//             }
-//         }
-
-//         .collectionStackingViewChrome-cont,
-//         .collectionTreeViewChrome-cont,
-//         .collection3DCarouselViewChrome-cont {
-//             display: flex;
-//             justify-content: space-between;
-//         }
-
-//         .collectionGridViewChrome-cont {
-//             display: flex;
-//             margin-left: 10;
-
-//             .collectionGridViewChrome-viewPicker {
-//                 font-size: $small-text;
-//                 //text-transform: uppercase;
-//                 //letter-spacing: 2px;
-//                 background: $dark-gray;
-//                 color: $white;
-//                 outline-color: $black;
-//                 color: $white;
-//                 border: none;
-//                 border-right: solid $medium-gray 1px;
-//             }
-
-//             .collectionGridViewChrome-viewPicker:active {
-//                 outline-color: $black;
-//             }
-
-//             .grid-control {
-//                 align-self: center;
-//                 display: flex;
-//                 flex-direction: row;
-//                 margin-right: 5px;
-
-//                 .grid-icon {
-//                     margin-right: 5px;
-//                     align-self: center;
-//                 }
-
-//                 .flexLabel {
-//                     margin-bottom: 0;
-//                 }
-
-//                 .collectionGridViewChrome-entryBox {
-//                     width: 50%;
-//                     color: $black;
-//                 }
-
-//                 .collectionGridViewChrome-columnButton {
-//                     color: $black;
-//                 }
-//             }
-//         }
-
-//         .collectionStackingViewChrome-sort,
-//         .collectionTreeViewChrome-sort {
-//             display: flex;
-//             align-items: center;
-//             justify-content: space-between;
-
-//             .collectionStackingViewChrome-sortIcon,
-//             .collectionTreeViewChrome-sortIcon {
-//                 transition: transform .5s;
-//                 margin-left: 10px;
-//             }
-//         }
-
-//         button:hover {
-//             transform: scale(1);
-//         }
-
-
-//         .collectionStackingViewChrome-pivotField-cont,
-//         .collectionTreeViewChrome-pivotField-cont,
-//         .collection3DCarouselViewChrome-scrollSpeed-cont {
-//             justify-self: right;
-//             align-items: center;
-//             display: flex;
-//             grid-auto-columns: auto;
-//             font-size: $small-text;
-//             letter-spacing: 2px;
-
-//             .collectionStackingViewChrome-pivotField-label,
-//             .collectionTreeViewChrome-pivotField-label,
-//             .collection3DCarouselViewChrome-scrollSpeed-label {
-//                 grid-column: 1;
-//                 margin-right: 7px;
-//                 user-select: none;
-//                 font-family: $sans-serif;
-//                 letter-spacing: normal;
-//             }
-
-//             .collectionStackingViewChrome-sortIcon {
-//                 transition: transform .5s;
-//                 grid-column: 3;
-//                 text-align: center;
-//                 display: flex;
-//                 justify-content: center;
-//                 align-items: center;
-//                 cursor: pointer;
-//                 width: 25px;
-//                 height: 25px;
-//                 border-radius: 100%;
-//             }
-
-//             .collectionStackingViewChrome-sortIcon:hover {
-//                 background-color: $drop-shadow;
-//             }
-
-//             .collectionStackingViewChrome-pivotField,
-//             .collectionTreeViewChrome-pivotField,
-//             .collection3DCarouselViewChrome-scrollSpeed {
-//                 color: $white;
-//                 grid-column: 2;
-//                 grid-row: 1;
-//                 width: 90%;
-//                 min-width: 100px;
-//                 display: flex;
-//                 height: 80%;
-//                 border-radius: 7px;
-//                 align-items: center;
-//                 background: $white;
-
-//                 .editable-view-input,
-//                 input,
-//                 .editableView-container-editing-oneLine,
-//                 .editableView-container-editing {
-//                     margin: auto;
-//                     border: 0px;
-//                     color: $light-gray !important;
-//                     text-align: center;
-//                     letter-spacing: 2px;
-//                     outline-color: $black;
-//                     height: 100%;
-//                 }
-
-//                 .react-autosuggest__container {
-//                     margin: 0;
-//                     color: $medium-gray;
-//                     padding: 0px;
-//                 }
-//             }
-//         }
-
-//         .collectionStackingViewChrome-pivotField:hover,
-//         .collectionTreeViewChrome-pivotField:hover,
-//         .collection3DCarouselViewChrome-scrollSpeed:hover {
-//             cursor: text;
-//         }
-
-//     }
-// }
-
-// .collectionMenu-webUrlButtons {
-//     margin-left: 44;
-//     background: lightGray;
-//     display: flex;
-// }
-
-// .webBox-urlEditor {
-//     position: relative;
-//     opacity: 0.9;
-//     z-index: 901;
-//     transition: top .5s;
-
-//     .urlEditor {
-//         display: grid;
-//         grid-template-columns: 1fr auto;
-//         padding-bottom: 10px;
-//         overflow: hidden;
-//         margin-top: 5px;
-//         height: 35px;
-
-//         .editorBase {
-//             display: flex;
-
-//             .editor-collapse {
-//                 transition: all .5s, opacity 0.3s;
-//                 position: absolute;
-//                 width: 40px;
-//                 transform-origin: top left;
-//             }
-
-//             .switchToText {
-//                 color: $medium-gray;
-//             }
-
-//             .switchToText:hover {
-//                 color: $dark-gray;
-//             }
-//         }
-
-//         button:hover {
-//             transform: scale(1);
-//         }
-//     }
-// }
-
-// .collectionMenu-urlInput {
-//     padding: 12px 10px 11px 10px;
-//     border: 0px;
-//     color: $black;
-//     font-size: $small-text;
-//     letter-spacing: 2px;
-//     outline-color: $black;
-//     background: $white;
-//     width: 100%;
-//     min-width: 350px;
-//     margin-right: 10px;
-//     height: 100%;
-// }
-
-// .collectionFreeFormMenu-cont {
-//     display: inline-flex;
-//     position: relative;
-//     align-items: center;
-//     height: 100%;
-
-//     .color-previewI {
-//         width: 60%;
-//         top: 80%;
-//         position: absolute;
-//         height: 4px;
-//     }
-
-//     .color-previewII {
-//         width: 80%;
-//         height: 80%;
-//         margin-left: 10%;
-//         position: absolute;
-//         bottom: 5;
-//     }
-
-//     .btn-group {
-//         display: grid;
-//         grid-template-columns: auto auto auto auto;
-//         margin: auto;
-//         /* Make the buttons appear below each other */
-//     }
-
-//     .btn-draw {
-//         display: inline-flex;
-//         margin: auto;
-//         /* Make the buttons appear below each other */
-//     }
-
-//     .fwdKeyframe,
-//     .numKeyframe,
-//     .backKeyframe {
-//         cursor: pointer;
-//         position: relative;
-//         width: 20;
-//         height: 30;
-//         bottom: 0;
-//         background: $dark-gray;
-//         display: inline-flex;
-//         align-items: center;
-//         color: $white;
-//     }
-
-//     .backKeyframe {
-//         svg {
-//             display: block;
-//             margin: auto;
-//         }
-//     }
-
-
-//     .numKeyframe {
-//         flex-direction: column;
-//         padding-top: 5px;
-//     }
-
-//     .fwdKeyframe {
-//         svg {
-//             display: block;
-//             margin: auto;
-//         }
-
-//         border-right: solid $medium-gray 1px;
-//     }
-// }
-
-// .collectionSchemaViewChrome-cont {
-//     display: flex;
-//     font-size: $small-text;
-
-//     .collectionSchemaViewChrome-toggle {
-//         display: flex;
-//         margin-left: 10px;
-//     }
-
-//     .collectionSchemaViewChrome-label {
-//         text-transform: uppercase;
-//         letter-spacing: 2px;
-//         margin-right: 5px;
-//         display: flex;
-//         flex-direction: column;
-//         justify-content: center;
-//     }
-
-//     .collectionSchemaViewChrome-toggler {
-//         width: 100px;
-//         height: 35px;
-//         background-color: $black;
-//         position: relative;
-//     }
-
-//     .collectionSchemaViewChrome-togglerButton {
-//         width: 47px;
-//         height: 30px;
-//         background-color: $light-gray;
-//         // position: absolute;
-//         transition: all 0.5s ease;
-//         // top: 3px;
-//         margin-top: 3px;
-//         color: $medium-gray;
-//         letter-spacing: 2px;
-//         text-transform: uppercase;
-//         display: flex;
-//         flex-direction: column;
-//         justify-content: center;
-//         text-align: center;
-
-//         &.on {
-//             margin-left: 3px;
-//         }
-
-//         &.off {
-//             margin-left: 50px;
-//         }
-//     }
-// }
-
-
-// .commandEntry-outerDiv {
-//     display: flex;
-//     flex-direction: column;
-//     height: 40px;
-// }
-
-// .commandEntry-inputArea {
-//     display: flex;
-//     flex-direction: row;
-//     width: 150px;
-//     margin: auto auto auto auto;
-// }
-
-// .react-autosuggest__container {
-//     position: relative;
-//     width: 100%;
-//     margin-left: 5px;
-//     margin-right: 5px;
-// }
-
-// .react-autosuggest__input {
-//     border: 1px solid $light-gray;
-//     border-radius: 4px;
-//     width: 100%;
-// }
-
-// .react-autosuggest__input--focused {
-//     outline: none;
-// }
-
-// .react-autosuggest__input--open {
-//     border-bottom-left-radius: 0;
-//     border-bottom-right-radius: 0;
-// }
-
-// .react-autosuggest__suggestions-container {
-//     display: none;
-// }
-
-// .react-autosuggest__suggestions-container--open {
-//     display: block;
-//     position: fixed;
-//     overflow-y: auto;
-//     max-height: 400px;
-//     width: 180px;
-//     border: 1px solid $light-gray;
-//     background-color: $white;
-//     font-family: $sans-serif;
-//     font-weight: 300;
-//     font-size: $large-header;
-//     border-bottom-left-radius: 4px;
-//     border-bottom-right-radius: 4px;
-//     z-index: 2;
-// }
-
-// .react-autosuggest__suggestions-list {
-//     margin: 0;
-//     padding: 0;
-//     list-style-type: none;
-// }
-
-// .react-autosuggest__suggestion {
-//     cursor: pointer;
-//     padding: 10px 20px;
-// }
-
-// .react-autosuggest__suggestion--highlighted {
-//     background-color: $light-gray;
-// }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 38692b924..9eb716763 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -2,23 +2,23 @@ import React = require('react');
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx';
+import { Toggle, ToggleType, Type } from 'browndash-components';
+import { Lambda, action, computed, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState } from 'react-color';
+import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Document } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { RichTextField } from '../../../fields/RichTextField';
 import { listSpec } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
+import { Document } from '../../../fields/documentSchemas';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
-import { Docs } from '../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -28,21 +28,19 @@ import { undoBatch } from '../../util/UndoManager';
 import { AntimodeMenu } from '../AntimodeMenu';
 import { EditableView } from '../EditableView';
 import { GestureOverlay } from '../GestureOverlay';
-import { Colors } from '../global/globalEnums';
 import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
 import { LightboxView } from '../LightboxView';
+import { MainView } from '../MainView';
+import { DefaultStyleProvider } from '../StyleProvider';
 import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
 import { DocumentView, OpenWhereMod } from '../nodes/DocumentView';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { DefaultStyleProvider } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionLinearView } from './collectionLinear';
 import './CollectionMenu.scss';
 import { COLLECTION_BORDER_WIDTH } from './CollectionView';
 import { TabDocView } from './TabDocView';
 import { CollectionFreeFormView } from './collectionFreeForm';
-import { Group, IconButton, Toggle, ToggleType, Type } from 'browndash-components';
+import { CollectionLinearView } from './collectionLinear';
 
 interface CollectionMenuProps {
     panelHeight: () => number;
@@ -96,6 +94,15 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
         }
     };
 
+    @action
+    toggleProperties = () => {
+        if (MainView.Instance.propertiesWidth() > 0) {
+            SettingsManager.propertiesWidth = 0;
+        } else {
+            SettingsManager.propertiesWidth = 300;
+        }
+    };
+
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
@@ -140,18 +147,30 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
     }
 
     render() {
-        const propIcon = SettingsManager.headerBarHeight > 0 ? 'angle-double-up' : 'angle-double-down';
-        const propTitle = SettingsManager.headerBarHeight > 0 ? 'Close Header Bar' : 'Open Header Bar';
+        const headerIcon = SettingsManager.headerBarHeight > 0 ? 'angle-double-up' : 'angle-double-down';
+        const headerTitle = SettingsManager.headerBarHeight > 0 ? 'Close Header Bar' : 'Open Header Bar';
+        const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
+        const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Properties' : 'Open Properties';
 
         const hardCodedButtons = (
             <div className={`hardCodedButtons`}>
                 <Toggle 
                     toggleType={ToggleType.BUTTON} 
                     type={Type.PRIM} 
-                    color={Colors.LIGHT_GRAY} 
+                    color={StrCast(Doc.UserDoc().userColor)} 
                     onClick={this.toggleTopBar} 
                     toggleStatus={SettingsManager.headerBarHeight > 0} 
+                    icon={<FontAwesomeIcon icon={headerIcon} size="lg" />}
+                    tooltip={headerTitle}
+                />
+                <Toggle 
+                    toggleType={ToggleType.BUTTON} 
+                    type={Type.PRIM}
+                    color={StrCast(Doc.UserDoc().userColor)} 
+                    onClick={this.toggleProperties} 
+                    toggleStatus={SettingsManager.propertiesWidth > 0} 
                     icon={<FontAwesomeIcon icon={propIcon} size="lg" />}
+                    tooltip={propTitle}
                 />
             </div>
         );
@@ -159,7 +178,11 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
         // NEW BUTTONS
         //dash col linear view buttons
         const contMenuButtons = (
-            <div className="collectionMenu-container">
+            <div className="collectionMenu-container" 
+            style={{
+                background: StrCast(Doc.UserDoc().userBackgroundColor),
+                // borderColor: StrCast(Doc.UserDoc().userColor)
+            }} >
                 {this.contMenuButtons}
                 {hardCodedButtons}
             </div>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 4cd3885f5..09be9dc76 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -386,6 +386,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _headerHeight = 0;
     @computed get content() {
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
+        const color = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.Color);
         const pointerEvents = () => (this.props.isContentActive() === false ? 'none' : undefined);
         const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
         return (
@@ -402,6 +403,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}
                     style={{
                         ...(!titleBar ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
+                        color: color(),
                         overflow: 'auto',
                         width: '100%',
                         height: '100%',
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index 58605c3f4..13bb3a577 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -65,11 +65,7 @@ input.lm_title {
 }
 
 .miniMap {
-    position: absolute;
     overflow: hidden;
-    right: 15;
-    bottom: 15;
-    border: solid 1px;
     width: 100%;
     height: 100%;
     transition: all 0.5s;
@@ -90,18 +86,5 @@ input.lm_title {
     cursor: pointer;
     position: absolute;
     bottom: 5;
-    display: flex;
     right: 5;
-    width: 25px;
-    height: 25px;
-    border-radius: 3px;
-    padding: 2px;
-    justify-content: center;
-    align-items: center;
-    align-content: center;
-    background-color: $light-gray;
-
-    &:hover {
-        box-shadow: none;
-    }
 }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 4d780f46b..714619a74 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -34,6 +34,7 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
 import React = require('react');
+import { Popup, Toggle, Type } from 'browndash-components';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface TabDocViewProps {
@@ -573,63 +574,53 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
         const miniTop = 50 + ((NumCast(this.props.document._freeform_panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2;
         const miniSize = this.returnMiniSize();
         return (
-            <>
-                {' '}
-                {this.props.document?.layout_hideMinimap ? null : (
-                    <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
-                        <CollectionFreeFormView
-                            Document={this.props.document}
-                            docViewPath={returnEmptyDoclist}
-                            childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
-                            noOverlay={true} // don't render overlay Docs since they won't scale
-                            setHeight={returnFalse}
-                            isContentActive={emptyFunction}
-                            isAnyChildContentActive={returnFalse}
-                            select={emptyFunction}
-                            isSelected={returnFalse}
-                            dontRegisterView={true}
-                            fieldKey={Doc.LayoutFieldKey(this.props.document)}
-                            bringToFront={emptyFunction}
-                            rootSelected={returnTrue}
-                            addDocument={returnFalse}
-                            moveDocument={returnFalse}
-                            removeDocument={returnFalse}
-                            PanelWidth={this.returnMiniSize}
-                            PanelHeight={this.returnMiniSize}
-                            ScreenToLocalTransform={Transform.Identity}
-                            renderDepth={0}
-                            whenChildContentsActiveChanged={emptyFunction}
-                            focus={emptyFunction}
-                            styleProvider={TabMinimapView.miniStyleProvider}
-                            addDocTab={this.props.addDocTab}
-                            pinToPres={TabDocView.PinDoc}
-                            childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
-                            childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
-                            searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
-                            fitContentsToBox={returnTrue}
-                        />
-                        <div className="miniOverlay" onPointerDown={this.miniDown}>
-                            <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} />
+            <div className="miniMap-hidden">
+                <Popup
+                    icon={<FontAwesomeIcon icon="globe-asia" size="lg" />}
+                    color={StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}
+                    type={Type.TERT}
+                    onPointerDown={e => e.stopPropagation()}
+                    placement={'top-end'}
+                    popup={
+                        <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
+                            <CollectionFreeFormView
+                                Document={this.props.document}
+                                docViewPath={returnEmptyDoclist}
+                                childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
+                                noOverlay={true} // don't render overlay Docs since they won't scale
+                                setHeight={returnFalse}
+                                isContentActive={emptyFunction}
+                                isAnyChildContentActive={returnFalse}
+                                select={emptyFunction}
+                                isSelected={returnFalse}
+                                dontRegisterView={true}
+                                fieldKey={Doc.LayoutFieldKey(this.props.document)}
+                                bringToFront={emptyFunction}
+                                rootSelected={returnTrue}
+                                addDocument={returnFalse}
+                                moveDocument={returnFalse}
+                                removeDocument={returnFalse}
+                                PanelWidth={this.returnMiniSize}
+                                PanelHeight={this.returnMiniSize}
+                                ScreenToLocalTransform={Transform.Identity}
+                                renderDepth={0}
+                                whenChildContentsActiveChanged={emptyFunction}
+                                focus={emptyFunction}
+                                styleProvider={TabMinimapView.miniStyleProvider}
+                                addDocTab={this.props.addDocTab}
+                                pinToPres={TabDocView.PinDoc}
+                                childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
+                                childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+                                searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
+                                fitContentsToBox={returnTrue}
+                            />
+                            <div className="miniOverlay" onPointerDown={this.miniDown}>
+                                <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} />
+                            </div>
                         </div>
-                    </div>
-                )}
-                <Tooltip key="ttip" title={<div className="dash-tooltip">{this.props.document.layout_hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}>
-                    <div
-                        className="miniMap-hidden"
-                        style={{
-                            color: this.props.document.layout_hideMinimap ? Colors.BLACK : Colors.WHITE,
-                            backgroundColor: this.props.document.layout_hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
-                            boxShadow: this.props.document.layout_hideMinimap ? Shadows.STANDARD_SHADOW : undefined,
-                        }}
-                        onPointerDown={e => e.stopPropagation()}
-                        onClick={action(e => {
-                            e.stopPropagation();
-                            this.props.document!.layout_hideMinimap = !this.props.document!.layout_hideMinimap;
-                        })}>
-                        <FontAwesomeIcon icon="globe-asia" size="lg" />
-                    </div>
-                </Tooltip>
-            </>
+                    }
+                />
+            </div>
         );
     }
 }
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 7eab03e1d..85f99b9c2 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -24,34 +24,18 @@
         // width: $TREE_BULLET_WIDTH;
         width: 100%;
         height: 100%;
-        position: absolute;
-
-        .treeView-expandIcon {
-            display: none;
-            left: -10px;
-            position: absolute;
-        }
-
-        .treeView-checkIcon {
-            left: 3.5px;
-            top: 2px;
-            position: absolute;
-        }
-
-        &:hover {
-            .treeView-expandIcon {
-                display: unset;
-            }
-        }
+        position: relative;
+        display: flex;
+        flex-direction: row;
     }
 
-    .treeView-bulletIcons:hover img {
-        left: 14px;
-        position: absolute;
-        transform-origin: center left;
-        transform: scale(6);
-        pointer-events: none;
-    }
+    // .treeView-bulletIcons:hover img {
+    //     left: 14px;
+    //     position: absolute;
+    //     transform-origin: center left;
+    //     transform: scale(6);
+    //     pointer-events: none;
+    // }
 
     .bullet {
         grid-column: 1;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 8d8d895c6..55f62cdf2 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -34,6 +34,7 @@ import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
 import { CollectionView } from './CollectionView';
 import './TreeView.scss';
 import React = require('react');
+import { IconButton, Size } from 'browndash-components';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -541,6 +542,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         TraceMobx();
         const expandKey = this.treeViewExpandedView;
         const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } }) ?? {};
+        const color = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Color)
+        console.log("tree view", color, this.doc.title, Doc.IsSystem(this.doc))
         if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) {
             const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
             const sortKeys = Object.keys(sortings);
@@ -580,7 +583,9 @@ export class TreeView extends React.Component<TreeViewProps> {
                 );
             }
             return (
-                <>
+                <div style={{ 
+                    color: color
+                 }}>
                     {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : (
                         <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }}>
                             {sortings[sorting]?.label}
@@ -640,17 +645,18 @@ export class TreeView extends React.Component<TreeViewProps> {
                                   this._renderCount
                               )}
                     </ul>
-                </>
+                </div>
             );
         } else if (this.treeViewExpandedView === 'fields') {
             return (
-                <ul key={this.doc[Id] + this.doc.title} style={{ cursor: 'inherit' }}>
+                <ul key={this.doc[Id] + this.doc.title} style={{ cursor: 'inherit', color: color }}>
                     <div>{this.expandedField}</div>
                 </ul>
             );
         }
         return (
             <ul
+                style={{ color: color }}
                 onPointerDown={e => {
                     e.preventDefault();
                     e.stopPropagation();
@@ -685,6 +691,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get renderBullet() {
         TraceMobx();
         const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : !this.childDocs.length ? ':empty' : '')) || 'question';
+        const color = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Color)
         const checked = this.onCheckedClick ? this.doc.treeViewChecked ?? 'unchecked' : undefined;
         return (
             <div
@@ -705,14 +712,26 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }>
                 {this.props.treeView.outlineMode ? (
                     !(this.doc.text as RichTextField)?.Text ? null : (
-                        <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />
+                        <IconButton
+                            color={color} 
+                            icon={<FontAwesomeIcon icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />}
+                            size={Size.XSMALL}
+                        />
                     )
                 ) : (
                     <div className="treeView-bulletIcons" style={{ color: Doc.IsSystem(DocCast(this.doc.proto)) ? 'red' : undefined }}>
-                        <div className={`treeView-${this.onCheckedClick ? 'checkIcon' : 'expandIcon'}`}>
-                            <FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />
-                        </div>
-                        {this.onCheckedClick ? null : typeof iconType === 'string' ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
+                        <IconButton
+                            color={color} 
+                            icon={<FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />}
+                            size={Size.XSMALL}
+                        />
+                        {this.onCheckedClick ? null : 
+                        <IconButton
+                            color={color} 
+                            icon={<FontAwesomeIcon icon={iconType as IconProp} />}
+                            size={Size.XSMALL}
+                        />
+                        }
                     </div>
                 )}
             </div>
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index c9168d40a..0f51fe6ff 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -4,6 +4,10 @@ import { Tooltip } from '@material-ui/core';
 import { observer } from 'mobx-react';
 import { unimplementedFunction } from '../../../../Utils';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { IconButton } from 'browndash-components';
+import { StrCast } from '../../../../fields/Types';
+import { Doc } from '../../../../fields/Doc';
+import { computed } from 'mobx';
 
 @observer
 export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -22,39 +26,44 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
         MarqueeOptionsMenu.Instance = this;
     }
 
+    @computed get userColor() {
+        return StrCast(Doc.UserDoc().userColor)
+    }
+
     render() {
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 19, transform: 'translate(-2px, -2px)' }} />;
+        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ width: 19 }} />;
         const buttons = (
             <>
-                <Tooltip key="collect" title={<div className="dash-tooltip">Create a Collection</div>} placement="bottom">
-                    <button className="antimodeMenu-button" onPointerDown={this.createCollection}>
-                        <FontAwesomeIcon icon="object-group" size="lg" />
-                    </button>
-                </Tooltip>
-                ,
-                <Tooltip key="group" title={<div className="dash-tooltip">Create a Grouping</div>} placement="bottom">
-                    <button className="antimodeMenu-button" onPointerDown={e => this.createCollection(e, true)}>
-                        <FontAwesomeIcon icon="layer-group" size="lg" />
-                    </button>
-                </Tooltip>
-                ,
-                <Tooltip key="summarize" title={<div className="dash-tooltip">Summarize Documents</div>} placement="bottom">
-                    <button className="antimodeMenu-button" onPointerDown={this.summarize}>
-                        <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
-                    </button>
-                </Tooltip>
-                ,
-                <Tooltip key="delete" title={<div className="dash-tooltip">Delete Documents</div>} placement="bottom">
-                    <button className="antimodeMenu-button" onPointerDown={this.delete}>
-                        <FontAwesomeIcon icon="trash-alt" size="lg" />
-                    </button>
-                </Tooltip>
-                ,
-                <Tooltip key="pinWithView" title={<div className="dash-tooltip">Pin selected region to trail</div>} placement="bottom">
-                    <button className="antimodeMenu-button" onPointerDown={this.pinWithView}>
-                        {presPinWithViewIcon}
-                    </button>
-                </Tooltip>
+                <IconButton 
+                    tooltip={"Create a Collection"}
+                    onPointerDown={this.createCollection}
+                    icon={<FontAwesomeIcon icon="object-group"/>}
+                    color={this.userColor}
+                />
+                <IconButton 
+                    tooltip={"Create a Grouping"}
+                    onPointerDown={e => this.createCollection(e, true)}
+                    icon={<FontAwesomeIcon icon="layer-group"/>}
+                    color={this.userColor}
+                />
+                <IconButton 
+                    tooltip={"Summarize Documents"}
+                    onPointerDown={this.summarize}
+                    icon={<FontAwesomeIcon icon="compress-arrows-alt"/>}
+                    color={this.userColor}
+                />
+                <IconButton 
+                    tooltip={"Delete Documents"}
+                    onPointerDown={this.delete}
+                    icon={<FontAwesomeIcon icon="trash-alt"/>}
+                    color={this.userColor}
+                />
+                <IconButton 
+                    tooltip={"Pin selected region"}
+                    onPointerDown={this.pinWithView}
+                    icon={presPinWithViewIcon}
+                    color={this.userColor}
+                />
             </>
         );
         return this.getElement(buttons);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 446e720e8..56b8366d0 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -20,7 +20,8 @@ import { UndoStack } from '../../UndoStack';
 import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
 import { CollectionSubView } from '../CollectionSubView';
 import './CollectionLinearView.scss';
-import { Button, Type } from 'browndash-components';
+import { Button, Toggle, ToggleType, Type } from 'browndash-components';
+import { Colors } from '../../global/globalEnums';
 
 /**
  * CollectionLinearView is the class for rendering the horizontal collection
@@ -47,7 +48,7 @@ export class CollectionLinearView extends CollectionSubView() {
 
     componentDidMount() {
         this._widthDisposer = reaction(
-            () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[Width]() || this.dimension()) + tot + 4, 0) : 0),
+            () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[Width]() || this.dimension()) + tot + 4, 0) : 0),
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
@@ -199,36 +200,48 @@ export class CollectionLinearView extends CollectionSubView() {
     render() {
         const flexDir = StrCast(this.Document.flexDirection); // Specify direction of linear view content
         const flexGap = NumCast(this.Document.flexGap); // Specify the gap between linear view content
-        const isExpanded = BoolCast(this.layoutDoc.linearView_IsExpanded);
+        const isExpanded = BoolCast(this.layoutDoc.linearView_IsOpen);
 
         const menuOpener = (
-            <Button 
+            <Toggle
                 text={Cast(this.props.Document.icon, 'string', null)} 
-                icon={<FontAwesomeIcon icon={isExpanded ? 'minus' : 'plus'} />}
+                icon={Cast(this.props.Document.icon, 'string', null) ? undefined : <FontAwesomeIcon icon={isExpanded ? 'minus' : 'plus'} />}
                 type={Type.TERT}
+                color={StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}
                 onPointerDown={e => e.stopPropagation()}
+                toggleType={ToggleType.BUTTON}
+                toggleStatus={BoolCast(this.layoutDoc.linearView_IsOpen)}
                 onClick={() => {
-                        this.layoutDoc.linearView_IsExpanded = !isExpanded;
+                        this.layoutDoc.linearView_IsOpen = !isExpanded;
                 }}
                 tooltip={isExpanded ? 'Close' : 'Open'}
+                fillWidth={true}
+                align={'center'}
             />
         );
 
         return (
-            <div className={`collectionLinearView-outer ${this.layoutDoc.linearView_SubMenu}`} style={{ backgroundColor: this.layoutDoc.linearView_IsExpanded ? undefined : 'transparent' }}>
+            <div className={`collectionLinearView-outer ${this.layoutDoc.linearView_SubMenu}`} style={{ backgroundColor: this.layoutDoc.linearView_IsOpen ? undefined : 'transparent' }}>
                 <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension(), pointerEvents: 'all' }}>
-                    {!this.props.Document.linearView_Expandable ? null : menuOpener}
-                    {!this.layoutDoc.linearView_IsExpanded ? null : (
-                        <div
-                            className="collectionLinearView-content"
-                            style={{
-                                height: this.dimension(),
-                                flexDirection: flexDir as any,
-                                gap: flexGap,
-                            }}>
-                            {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
-                        </div>
-                    )}
+                    {this.props.Document.linearView_Dropdown ? 
+                        <div>Hello World!</div>
+                        : 
+                        <>
+                        {!this.props.Document.linearView_Expandable ? null : menuOpener}
+                        {!this.layoutDoc.linearView_IsOpen ? null : (
+                            <div
+                                className="collectionLinearView-content"
+                                style={{
+                                    height: this.dimension(),
+                                    flexDirection: flexDir as any,
+                                    gap: flexGap,
+                                }}>
+                                {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
+                            </div>
+                        )}
+                        </>
+                    }
+                    
                 </div>
             </div>
         );
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 8f6d898fe..d22b6b4d9 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -66,19 +66,20 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
     if (checkResult) {
-        return Doc.SharingDoc().userColor;
+        return Doc.SharingDoc().headingColor;
     }
-    Doc.SharingDoc().userColor = undefined;
-    Doc.GetProto(Doc.SharingDoc()).userColor = color;
+    Doc.SharingDoc().headingColor = undefined;
+    Doc.GetProto(Doc.SharingDoc()).headingColor = 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) {
+    console.log(checkResult);
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     if (checkResult) {
-        if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE;
-        return 'transparent';
+        if (NumCast(selected?.Document.z) >= 1) return true;
+        return false;
     }
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
 });
@@ -88,45 +89,47 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snapli
     // 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,
+            checkResult: (doc:Doc) => BoolCast(doc._freeform_backgroundGrid, false),
             setDoc: (doc:Doc) => doc._freeform_backgroundGrid = !doc._freeform_backgroundGrid,
         }],
         ['snaplines', {
-            checkResult: (doc:Doc) => doc._freeform_snapLines,
+            checkResult: (doc:Doc) => BoolCast(doc._freeform_snapLines, false),
             setDoc: (doc:Doc) => doc._freeform_snapLines = !doc._freeform_snapLines,
         }],
         ['viewAll', {
-            checkResult: (doc:Doc) => doc._freeform_fitContentsToBox,
+            checkResult: (doc:Doc) => BoolCast(doc._freeform_fitContentsToBox, false),
             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,
+            checkResult: (doc:Doc) => BoolCast(doc._freeform_useClusters, false),
             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,
+            checkResult: (doc:Doc) => BoolCast(doc._autoArrange, false),
             setDoc: (doc:Doc) => doc._autoArrange = !doc._autoArrange,
         }],
         ['flashcards', {
-            checkResult: (doc:Doc) => Doc.UserDoc().defaultToFlashcards,
+            checkResult: (doc:Doc) => BoolCast(Doc.UserDoc().defaultToFlashcards, false),
             setDoc: (doc:Doc) => Doc.UserDoc().defaultToFlashcards = !Doc.UserDoc().defaultToFlashcards,
         }],
    ]);
 
     if (checkResult) {
-        return map.get(attr)?.checkResult(selected) ? Colors.MEDIUM_BLUE : 'transparent';
+        console.log(attr, map.get(attr)?.checkResult(selected))
+        return map.get(attr)?.checkResult(selected);
     }
     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) {
+
+ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', 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([
+    const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => any; setDoc: () => void;}> = new Map([
         ['font', {
             checkResult: () => RichTextMenu.Instance?.fontFamily,
             setDoc: () => value && RichTextMenu.Instance.setFontFamily(value),
@@ -139,6 +142,10 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
             checkResult: () => RichTextMenu.Instance?.fontColor,
             setDoc: () => value && RichTextMenu.Instance.setColor(value),
         }],
+        ['alignment', {
+            checkResult: () => RichTextMenu.Instance.textAlign,
+            setDoc: () => value && editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, value):(Doc.UserDoc().textAlign = value),
+        }],
         ['fontSize', {
             checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
             setDoc: () => {
@@ -157,6 +164,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
 
 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;
@@ -182,7 +190,10 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
                         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';
+    if (checkResult) {
+        console.log(charStyle, checkResult, map.get(charStyle)?.checkResult());
+        return map.get(charStyle)?.checkResult();
+    }
     map.get(charStyle)?.toggle();
 });
 
@@ -270,9 +281,9 @@ function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean,
     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';
+                ? true
+                : true
+            : false;
     }
     runInAction(() => {
         if (GestureOverlay.Instance) {
@@ -308,7 +319,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
     // 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'),
+            checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.stroke_isInkMask) : ActiveIsInkMask())),
             setInk: (doc: Doc) => (doc.stroke_isInkMask = !doc.stroke_isInkMask),
             setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()),
         }],
diff --git a/src/client/views/linking/LinkPopup.scss b/src/client/views/linking/LinkPopup.scss
index b20ad9476..4bfb4b0b9 100644
--- a/src/client/views/linking/LinkPopup.scss
+++ b/src/client/views/linking/LinkPopup.scss
@@ -4,7 +4,6 @@
     top: 0;
     height: 200px;
     width: 200px;
-    position: absolute;
     // padding: 15px;
     border-radius: 3px;
 
@@ -37,8 +36,4 @@
             margin: auto;
         }
     }
-
-    .searchBox-container {
-        background: pink;
-    }
 }
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 745efea08..6895c0746 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -14,7 +14,6 @@ import React = require('react');
 import { OpenWhere } from '../nodes/DocumentView';
 
 interface LinkPopupProps {
-    showPopup: boolean;
     linkFrom?: () => Doc | undefined;
     linkCreateAnchor?: () => Doc | undefined;
     linkCreated?: (link: Doc) => void;
@@ -44,10 +43,9 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
     getPHeight = () => 500;
 
     render() {
-        const popupVisibility = this.props.showPopup ? 'block' : 'none';
         const linkDoc = this.props.linkFrom ? this.props.linkFrom : undefined;
         return (
-            <div className="linkPopup-container" style={{ display: popupVisibility }}>
+            <div className="linkPopup-container">
                 {/* <div className="linkPopup-url-container">
                     <input autoComplete="off" type="text" value={this.linkURL} placeholder="Enter URL..." onChange={this.onLinkChange} />
                     <button onPointerDown={e => this.makeLinkToURL(this.linkURL, "add:right")}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e954d0484..7e8eef0a5 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -18,7 +18,7 @@ import { SearchBox } from '../search/SearchBox';
 import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo';
 import { YoutubeBox } from './../../apis/youtube/YoutubeBox';
 import { AudioBox } from './AudioBox';
-import { FontIconBox } from './button/FontIconBox';
+import { FontIconBox } from './FontIconBox/FontIconBox';
 import { ColorBox } from './ColorBox';
 import { ComparisonBox } from './ComparisonBox';
 import { DataVizBox } from './DataVizBox/DataVizBox';
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8a35ca2a4..0b8ea1150 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1111,8 +1111,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         );
         const targetDoc = layout_showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
         const background = StrCast(
-            SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.userColor,
-            Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)'
+            SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor,
+            Doc.UserDoc().layout_showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().headingColor) : 'rgba(0,0,0,0.4)'
         );
         const layout_sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
         const titleView = !layout_showTitle ? null : (
diff --git a/src/client/views/nodes/FontIconBox/ButtonInterface.ts b/src/client/views/nodes/FontIconBox/ButtonInterface.ts
new file mode 100644
index 000000000..0aa2ac8e1
--- /dev/null
+++ b/src/client/views/nodes/FontIconBox/ButtonInterface.ts
@@ -0,0 +1,12 @@
+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/FontIconBox/FontIconBadge.scss b/src/client/views/nodes/FontIconBox/FontIconBadge.scss
new file mode 100644
index 000000000..78f506e57
--- /dev/null
+++ b/src/client/views/nodes/FontIconBox/FontIconBadge.scss
@@ -0,0 +1,11 @@
+.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/FontIconBox/FontIconBadge.tsx b/src/client/views/nodes/FontIconBox/FontIconBadge.tsx
new file mode 100644
index 000000000..b50588ce2
--- /dev/null
+++ b/src/client/views/nodes/FontIconBox/FontIconBadge.tsx
@@ -0,0 +1,37 @@
+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/FontIconBox/FontIconBox.scss b/src/client/views/nodes/FontIconBox/FontIconBox.scss
new file mode 100644
index 000000000..9d9fa26b0
--- /dev/null
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.scss
@@ -0,0 +1,438 @@
+@import '../../global/globalCssVariables';
+
+.menuButton {
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 80%;
+    border-radius: $standard-border-radius;
+    transition: 0.15s;
+
+    .menuButton-wrap {
+        grid-column: 1;
+        justify-content: center;
+        align-items: center;
+        text-align: center;
+    }
+
+    .fontIconBox-label {
+        color: $white;
+        bottom: -1;
+        position: absolute;
+        text-align: center;
+        font-size: 7px;
+        letter-spacing: normal;
+        background-color: inherit;
+        border-radius: 8px;
+        padding: 0;
+        width: 100%;
+        font-family: 'system-ui';
+        text-transform: uppercase;
+        font-weight: bold;
+        transition: 0.15s;
+    }
+
+    .fontIconBox-icon {
+        width: 80%;
+        height: 80%;
+    }
+
+    &.clickBtn,
+    &.clickBtnLabel {
+        cursor: pointer;
+        flex-direction: column;
+
+        svg {
+            width: 50% !important;
+            height: 50%;
+        }
+    }
+
+    &.clickBtnLabel {
+        svg {
+            margin-top: -4px;
+        }
+    }
+
+    &.textBtn {
+        display: grid;
+        /* grid-row: auto; */
+        grid-auto-flow: column;
+        cursor: pointer;
+        width: 100%;
+        justify-content: center;
+        align-items: center;
+        justify-items: center;
+    }
+
+    &.tglBtn,
+    &.tglBtnLabel {
+        cursor: pointer;
+        flex-direction: column;
+
+        &.switch {
+            //TOGGLE
+
+            .switch {
+                position: relative;
+                display: inline-block;
+                width: 100%;
+                height: 25px;
+                margin: 0;
+            }
+
+            .switch input {
+                opacity: 0;
+                width: 0;
+                height: 0;
+            }
+
+            .slider {
+                position: absolute;
+                cursor: pointer;
+                top: 0;
+                left: 0;
+                right: 0;
+                bottom: 0;
+                background-color: lightgrey;
+                -webkit-transition: 0.4s;
+                transition: 0.4s;
+            }
+
+            .slider:before {
+                position: absolute;
+                content: '';
+                height: 21px;
+                width: 21px;
+                left: 2px;
+                bottom: 2px;
+                background-color: $white;
+                -webkit-transition: 0.4s;
+                transition: 0.4s;
+            }
+
+            input:checked + .slider {
+                background-color: $medium-blue;
+            }
+
+            input:focus + .slider {
+                box-shadow: 0 0 1px $medium-blue;
+            }
+
+            input:checked + .slider:before {
+                -webkit-transform: translateX(26px);
+                -ms-transform: translateX(26px);
+                transform: translateX(26px);
+            }
+
+            /* Rounded sliders */
+            .slider.round {
+                border-radius: $standard-border-radius;
+            }
+
+            .slider.round:before {
+                border-radius: $standard-border-radius;
+            }
+        }
+
+        svg {
+            width: 50% !important;
+            height: 50%;
+        }
+
+        &:hover {
+            background-color: rgba(0, 0, 0, 0.3);
+        }
+    }
+
+    &.tglBtnLabel {
+        svg {
+            margin-top: -4px;
+        }
+    }
+
+    &.toolBtn,
+    &.toolBtnLabel {
+        cursor: pointer;
+        width: 100%;
+        border-radius: 100%;
+        flex-direction: column;
+        // margin-top: -4px;
+
+        svg {
+            width: 60% !important;
+            height: 60%;
+        }
+    }
+
+    &.toolBtnLabel {
+        svg {
+            margin-top: -4px;
+        }
+    }
+
+    &.menuBtn {
+        cursor: pointer !important;
+        border-radius: 0px;
+        flex-direction: column;
+
+        svg {
+            width: 45% !important;
+            height: 45%;
+        }
+
+        &:hover {
+            filter: brightness(0.85);
+        }
+    }
+
+    &.colorBtn,
+    &.colorBtnLabel {
+        color: black;
+        cursor: pointer;
+        flex-direction: column;
+        background: transparent;
+
+        .colorButton-color {
+            margin-top: 3px;
+            width: 80%;
+            height: 3px;
+        }
+
+        .menuButton-dropdownBox {
+            position: absolute;
+            width: fit-content;
+            height: fit-content;
+            color: black;
+            top: 100%;
+            left: 0;
+            z-index: 21;
+            background-color: #e3e3e3;
+            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+            border-radius: 3px;
+        }
+    }
+
+    &.colorBtnLabel {
+        svg {
+            margin-top: -4px;
+        }
+    }
+
+    &.drpdownList {
+        width: 100%;
+        display: grid;
+        grid-auto-columns: 80px 20px;
+        justify-items: center;
+        font-family: 'Roboto';
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        font-size: 13;
+        font-weight: 600;
+        overflow: hidden;
+        cursor: pointer;
+        background: transparent;
+        align-content: center;
+        align-items: center;
+
+        .menuButton-dropdownList {
+            position: absolute;
+            width: 150px;
+            height: fit-content;
+            top: 100%;
+            z-index: 21;
+            background-color: $white;
+            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+            padding: 1px;
+
+            .list-item {
+                color: $black;
+                width: 100%;
+                height: 25px;
+                font-weight: 400;
+                display: flex;
+                justify-content: left;
+                align-items: center;
+                padding-left: 5px;
+            }
+
+            .list-item:hover {
+                background-color: lightgrey;
+            }
+        }
+    }
+
+    &.numBtn {
+        cursor: pointer;
+        background: transparent;
+
+        &.slider {
+            color: $white;
+            cursor: pointer;
+            flex-direction: column;
+            background: transparent;
+
+            .menu-slider {
+                width: 100px;
+            }
+
+            .menuButton-dropdownBox {
+                position: absolute;
+                width: fit-content;
+                height: fit-content;
+                top: 100%;
+                z-index: 21;
+                background-color: #e3e3e3;
+                box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+                border-radius: $standard-border-radius;
+
+                .menu-slider {
+                    height: 10px;
+                }
+                input[type='range']::-webkit-slider-runnable-track {
+                    background: gray;
+                    height: 3px;
+                }
+
+                input[type='range']::-webkit-slider-thumb {
+                    box-shadow: 1px 1px 1px #000000;
+                    border: 1px solid #000000;
+                    height: 10px;
+                    width: 10px;
+                    border-radius: 5px;
+                    background: #ffffff;
+                    cursor: pointer;
+                    -webkit-appearance: none;
+                    margin-top: -4px;
+                }
+            }
+        }
+
+        .button {
+            width: 25%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            &.number {
+                width: 50%;
+
+                .button-input {
+                    background: none;
+                    border: none;
+                    text-align: right;
+                    width: 100%;
+                    color: $white;
+                    height: 100%;
+                    text-align: center;
+                }
+
+                .button-input:focus {
+                    outline: none;
+                }
+            }
+        }
+
+        &.list {
+            width: 100%;
+            justify-content: space-around;
+            border: $standard-border;
+
+            .menuButton-dropdownList {
+                position: absolute;
+                width: fit-content;
+                height: fit-content;
+                min-width: 50%;
+                max-height: 50vh;
+                overflow-y: scroll;
+                top: 100%;
+                z-index: 21;
+                background-color: $white;
+                box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+                padding: 1px;
+
+                .list-item {
+                    color: $black;
+                    width: 100%;
+                    height: 25px;
+                    font-weight: 400;
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                }
+
+                .list-item:hover {
+                    background-color: lightgrey;
+                }
+            }
+        }
+    }
+
+    &.editableText {
+        cursor: text;
+        display: flex;
+        flex-direction: row;
+        gap: 5px;
+        padding-left: 10px;
+        justify-content: flex-start;
+        color: black;
+        background-color: $light-gray;
+        padding: 5px;
+        padding-left: 10px;
+        width: 100%;
+        height: 100%;
+    }
+
+    &.drpDownBtn {
+        cursor: pointer;
+        background: transparent;
+        border: solid 0.5px grey;
+
+        &.true {
+            background: rgba(0, 0, 0, 0.3);
+        }
+
+        .menuButton-dropdownBox {
+            position: absolute;
+            width: 150px;
+            height: 250px;
+            top: 100%;
+            background-color: #e3e3e3;
+            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+            border-radius: $standard-border-radius;
+        }
+    }
+
+    .menuButton-dropdown {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        font-size: 15px;
+        grid-column: 2;
+        border-radius: 0px 7px 7px 0px;
+        width: 13px;
+        height: 100%;
+        right: 0;
+    }
+
+    .menuButton-dropdown-header {
+        width: 100%;
+        font-weight: 300;
+        padding: 5px;
+        overflow: hidden;
+        font-size: 12px;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+    }
+
+    .dropbox-background {
+        width: 200vw;
+        height: 200vh;
+        top: -100vh;
+        z-index: 20;
+        left: -100vw;
+        background: transparent;
+        position: fixed;
+    }
+}
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
new file mode 100644
index 000000000..2958fa737
--- /dev/null
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -0,0 +1,412 @@
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
+import { ScriptField } from '../../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { undoable, UndoManager } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import { DocComponent } from '../../DocComponent';
+import { EditableView } from '../../EditableView';
+import { Colors } from '../../global/globalEnums';
+import { StyleProp } from '../../StyleProvider';
+import { FieldView, FieldViewProps } from '../FieldView';
+import { OpenWhere } from '../DocumentView';
+import { RichTextMenu } from '../formattedText/RichTextMenu';
+import './FontIconBox.scss';
+import { SelectedDocView } from '../../selectedDoc';
+
+export enum ButtonType {
+    TextButton = 'textBtn',
+    MenuButton = 'menuBtn',
+    DropdownList = 'dropdownList',
+    DropdownButton = 'dropdownBtn',
+    ClickButton = 'clickBtn',
+    ToggleButton = 'toggleBtn',
+    ColorButton = 'colorBtn',
+    ToolButton = 'toolBtn',
+    MultiToggleButton = 'multiToggleBtn',
+    NumberSliderButton = 'numSliderBtn',
+    NumberDropdownButton = 'numDropdownBtn',
+    NumberInlineButton = 'numInlineBtn',
+    EditableText = 'editableText',
+}
+
+export interface ButtonProps extends FieldViewProps {
+    type?: ButtonType;
+}
+@observer
+export class FontIconBox extends DocComponent<ButtonProps>() {
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(FontIconBox, fieldKey);
+    }
+    @observable noTooltip = false;
+    showTemplate = (): void => {
+        const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
+        dragFactory && this.props.addDocTab(dragFactory, OpenWhere.addRight);
+    };
+    dragAsTemplate = (): void => {
+        this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
+    };
+    useAsPrototype = (): void => {
+        this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)');
+    };
+
+    specificContextMenu = (): void => {
+        if (!Doc.noviceMode) {
+            const cm = ContextMenu.Instance;
+            cm.addItem({ description: 'Show Template', event: this.showTemplate, icon: 'tag' });
+            cm.addItem({ description: 'Use as Render Template', event: this.dragAsTemplate, icon: 'tag' });
+            cm.addItem({ description: 'Use as Prototype', event: this.useAsPrototype, icon: 'tag' });
+        }
+    };
+
+    static GetShowLabels() {
+        return BoolCast(Doc.UserDoc()._showLabel);
+    }
+    static SetShowLabels(show: boolean) {
+        Doc.UserDoc()._showLabel = show;
+    }
+    static GetRecognizeGestures() {
+        return BoolCast(Doc.UserDoc()._recognizeGestures);
+    }
+    static SetRecognizeGestures(show: boolean) {
+        Doc.UserDoc()._recognizeGestures = show;
+    }
+
+    // Determining UI Specs
+    @computed get label() {
+        return StrCast(this.rootDoc.icon_label, StrCast(this.rootDoc.title));
+    }
+    Icon = (color: string, iconFalse?: boolean) => {
+        let icon;
+        if (iconFalse ) {
+            icon = StrCast(this.dataDoc[this.fieldKey ?? 'iconFalse'] ?? this.dataDoc.icon, 'user') as any;
+            if (icon) return <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />
+            else return null
+        }
+        icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any;
+        const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />;
+        return !icon ? null : icon === 'pres-trail' ? trailsIcon() : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
+    };
+    @computed get dropdown() {
+        return BoolCast(this.rootDoc.dropDownOpen);
+    }
+    @computed get buttonList() {
+        return StrListCast(this.rootDoc.btnList);
+    }
+    @computed get type() {
+        return StrCast(this.rootDoc.btnType);
+    }
+
+    /**
+     * Types of buttons in dash:
+     * - Main menu button (LHS)
+     * - Tool button
+     * - Expandable button (CollectionLinearView)
+     * - Button inside of CollectionLinearView vs. outside of CollectionLinearView
+     * - Action button
+     * - Dropdown button
+     * - Color button
+     * - Dropdown list
+     * - Number button
+     **/
+
+    _batch: UndoManager.Batch | undefined = undefined;
+    /**
+     * Number button
+     */
+    @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(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));
+        const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>;
+
+        return <NumberDropdown 
+            color={color} 
+            numberDropdownType={type} 
+            showPlusMinus={false} 
+            tooltip={this.label}
+            type={Type.PRIM}
+            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`)}
+            fillWidth
+        />
+    }
+
+    /**
+     * Dropdown button
+     */
+    @computed get dropdownButton() {
+        const active: string = StrCast(this.rootDoc.dropDownOpen);
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        return (
+            <div
+                className={`menuButton ${this.type} ${active}`}
+                style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                onClick={action(() => {
+                    this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
+                    this.noTooltip = this.rootDoc.dropDownOpen;
+                    Doc.UnBrushAllDocs();
+                })}>
+                {this.Icon(color)}
+                {!this.label || !FontIconBox.GetShowLabels() ? null : (
+                    <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
+                        {' '}
+                        {this.label}{' '}
+                    </div>
+                )}
+                <div className="menuButton-dropdown" style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
+                    <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
+                </div>
+                {this.rootDoc.dropDownOpen ? <div className="menuButton-dropdownBox">{/* DROPDOWN BOX CONTENTS */}</div> : null}
+            </div>
+        );
+    }
+
+    /**
+     * Dropdown list
+     */
+    @computed get dropdownListButton() {
+        const active: string = StrCast(this.rootDoc.dropDownOpen);
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+
+        const script = ScriptCast(this.rootDoc.script);
+
+        let noviceList: string[] = [];
+        let text: string | undefined;
+        let dropdown = true;
+        let getStyle: (val: string) => any = () => {};
+        let icon: IconProp = 'caret-down';
+        let isViewDropdown: boolean = script?.script.originalScript.startsWith('setView')
+        try {
+            if (isViewDropdown) {
+                const selectedDocs: Doc[] = SelectionManager.Docs();
+                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;
+                        if (selectedDocs.length > 1) {
+                            text = selectedDocs.length + " documents selected"
+                        } else {
+                            text = selected.type === DocumentType.RTF ? 'Text' : StrCast(selected.type);
+                            icon = Doc.toIcon(selected);
+                        }
+                        return <Popup
+                            icon={<FontAwesomeIcon size={'1x'} icon={icon} />}
+                            text={text}
+                            type={Type.TERT}
+                            color={color}
+                            popup={<SelectedDocView selectedDocs={selectedDocs}/>}
+                            fillWidth
+                        />
+                    }
+                } else {
+                    dropdown = false;
+                    return <Button 
+                        text={`None Selected`} 
+                        type={Type.TERT}
+                        color={color}
+                        fillWidth
+                        inactive
+                    />
+                }
+                noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
+            } else { 
+                text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+                getStyle = (val: string) => { return { fontFamily: val } }
+            }
+        } catch (e) {
+            console.log(e);
+        }
+
+        console.log("current item: ", text);
+
+        // 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.charAt(0).toUpperCase() + value.slice(1),
+                    val: value,
+                    style: getStyle(value),
+                    onClick: undoable(() => script.script.run({ self: this.rootDoc, value }), value)
+                    // shortcut: '#',
+                }
+            ));
+
+
+        return (
+            <Dropdown 
+                selectedVal={text}
+                setSelectedVal={undoable((val) => script.script.run({ self: this.rootDoc, val }), `dropdown select ${this.label}`)}
+                color={color} 
+                type={isViewDropdown ? Type.TERT : Type.PRIM} 
+                dropdownType={DropdownType.SELECT} 
+                items={list}
+                tooltip={this.label}
+                fillWidth
+            />
+        )
+    }
+
+
+    @computed get colorScript() {
+        return ScriptCast(this.rootDoc.script);
+    }
+
+    /**
+     * Color button
+     */
+    @computed get colorButton() {
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        const curColor = this.colorScript?.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent';
+        const tooltip: string = StrCast(this.rootDoc.toolTip);
+
+        return (
+            <ColorPicker 
+                setSelectedColor={(value) => {
+                    const s = this.colorScript;
+                    s && undoable(() => s.script.run({ self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)();
+                }}
+                selectedColor={curColor}
+                type={Type.PRIM}
+                color={color}
+                icon={this.Icon(color)!}
+                tooltip={tooltip}
+                label={this.label}
+            />
+        )
+    }
+
+    @computed get toggleButton() {
+        // Determine the type of toggle button
+        const buttonText: string = StrCast(this.rootDoc.buttonText);
+        const tooltip: string = StrCast(this.rootDoc.toolTip);
+
+        const script = ScriptCast(this.rootDoc.onClick);
+        const toggleStatus = script ? script.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
+        // Colors
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+
+        console.log(tooltip, toggleStatus);
+        return (
+            <Toggle tooltip={`Toggle ${tooltip}`} toggleType={ToggleType.BUTTON} type={Type.PRIM} toggleStatus={toggleStatus} text={buttonText} color={color} icon={this.Icon(color)!} label={this.label}/>
+        )
+    }
+
+    /**
+     * Default
+     */
+    @computed get defaultButton() {
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        const tooltip: string = StrCast(this.rootDoc.toolTip);
+
+        return (
+            <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label}/>
+        )
+    }
+
+    @computed get editableText() {
+        // Script for running the toggle
+        const script = ScriptCast(this.rootDoc.script);
+        // Function to run the script
+        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} setEditing={(editing: boolean) => {}}            
+        />
+        
+        return (
+            <div className="menuButton editableText">
+                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} />
+                <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}>
+                    <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
+                </div>
+            </div>
+        );
+    }
+
+    render() {
+        // determine dash button metadata
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        const tooltip: string = StrCast(this.rootDoc.toolTip);
+
+        // TODO:glr Add label of button type
+        let button: JSX.Element = this.defaultButton;
+        // prettier-ignore
+        switch (this.type) {
+            case ButtonType.EditableText: 
+                button = this.editableText;
+                break;
+            case ButtonType.DropdownList:
+                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.ToggleButton: button = this.toggleButton; break;
+            case ButtonType.TextButton: 
+                // 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 = (
+                    <IconButton tooltip={tooltip} color={color} icon={this.Icon(color)!} label={this.label}/>
+                );
+                break;
+            case ButtonType.MenuButton: button = (
+                    <IconButton tooltip={tooltip} tooltipPlacement='right' size={Size.LARGE} color={color} icon={this.Icon(color)!} label={this.label}/>
+                );
+                break;
+        }
+
+        return button;
+    }
+}
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
deleted file mode 100644
index 9d9fa26b0..000000000
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ /dev/null
@@ -1,438 +0,0 @@
-@import '../../global/globalCssVariables';
-
-.menuButton {
-    height: 100%;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    font-size: 80%;
-    border-radius: $standard-border-radius;
-    transition: 0.15s;
-
-    .menuButton-wrap {
-        grid-column: 1;
-        justify-content: center;
-        align-items: center;
-        text-align: center;
-    }
-
-    .fontIconBox-label {
-        color: $white;
-        bottom: -1;
-        position: absolute;
-        text-align: center;
-        font-size: 7px;
-        letter-spacing: normal;
-        background-color: inherit;
-        border-radius: 8px;
-        padding: 0;
-        width: 100%;
-        font-family: 'system-ui';
-        text-transform: uppercase;
-        font-weight: bold;
-        transition: 0.15s;
-    }
-
-    .fontIconBox-icon {
-        width: 80%;
-        height: 80%;
-    }
-
-    &.clickBtn,
-    &.clickBtnLabel {
-        cursor: pointer;
-        flex-direction: column;
-
-        svg {
-            width: 50% !important;
-            height: 50%;
-        }
-    }
-
-    &.clickBtnLabel {
-        svg {
-            margin-top: -4px;
-        }
-    }
-
-    &.textBtn {
-        display: grid;
-        /* grid-row: auto; */
-        grid-auto-flow: column;
-        cursor: pointer;
-        width: 100%;
-        justify-content: center;
-        align-items: center;
-        justify-items: center;
-    }
-
-    &.tglBtn,
-    &.tglBtnLabel {
-        cursor: pointer;
-        flex-direction: column;
-
-        &.switch {
-            //TOGGLE
-
-            .switch {
-                position: relative;
-                display: inline-block;
-                width: 100%;
-                height: 25px;
-                margin: 0;
-            }
-
-            .switch input {
-                opacity: 0;
-                width: 0;
-                height: 0;
-            }
-
-            .slider {
-                position: absolute;
-                cursor: pointer;
-                top: 0;
-                left: 0;
-                right: 0;
-                bottom: 0;
-                background-color: lightgrey;
-                -webkit-transition: 0.4s;
-                transition: 0.4s;
-            }
-
-            .slider:before {
-                position: absolute;
-                content: '';
-                height: 21px;
-                width: 21px;
-                left: 2px;
-                bottom: 2px;
-                background-color: $white;
-                -webkit-transition: 0.4s;
-                transition: 0.4s;
-            }
-
-            input:checked + .slider {
-                background-color: $medium-blue;
-            }
-
-            input:focus + .slider {
-                box-shadow: 0 0 1px $medium-blue;
-            }
-
-            input:checked + .slider:before {
-                -webkit-transform: translateX(26px);
-                -ms-transform: translateX(26px);
-                transform: translateX(26px);
-            }
-
-            /* Rounded sliders */
-            .slider.round {
-                border-radius: $standard-border-radius;
-            }
-
-            .slider.round:before {
-                border-radius: $standard-border-radius;
-            }
-        }
-
-        svg {
-            width: 50% !important;
-            height: 50%;
-        }
-
-        &:hover {
-            background-color: rgba(0, 0, 0, 0.3);
-        }
-    }
-
-    &.tglBtnLabel {
-        svg {
-            margin-top: -4px;
-        }
-    }
-
-    &.toolBtn,
-    &.toolBtnLabel {
-        cursor: pointer;
-        width: 100%;
-        border-radius: 100%;
-        flex-direction: column;
-        // margin-top: -4px;
-
-        svg {
-            width: 60% !important;
-            height: 60%;
-        }
-    }
-
-    &.toolBtnLabel {
-        svg {
-            margin-top: -4px;
-        }
-    }
-
-    &.menuBtn {
-        cursor: pointer !important;
-        border-radius: 0px;
-        flex-direction: column;
-
-        svg {
-            width: 45% !important;
-            height: 45%;
-        }
-
-        &:hover {
-            filter: brightness(0.85);
-        }
-    }
-
-    &.colorBtn,
-    &.colorBtnLabel {
-        color: black;
-        cursor: pointer;
-        flex-direction: column;
-        background: transparent;
-
-        .colorButton-color {
-            margin-top: 3px;
-            width: 80%;
-            height: 3px;
-        }
-
-        .menuButton-dropdownBox {
-            position: absolute;
-            width: fit-content;
-            height: fit-content;
-            color: black;
-            top: 100%;
-            left: 0;
-            z-index: 21;
-            background-color: #e3e3e3;
-            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
-            border-radius: 3px;
-        }
-    }
-
-    &.colorBtnLabel {
-        svg {
-            margin-top: -4px;
-        }
-    }
-
-    &.drpdownList {
-        width: 100%;
-        display: grid;
-        grid-auto-columns: 80px 20px;
-        justify-items: center;
-        font-family: 'Roboto';
-        white-space: nowrap;
-        text-overflow: ellipsis;
-        font-size: 13;
-        font-weight: 600;
-        overflow: hidden;
-        cursor: pointer;
-        background: transparent;
-        align-content: center;
-        align-items: center;
-
-        .menuButton-dropdownList {
-            position: absolute;
-            width: 150px;
-            height: fit-content;
-            top: 100%;
-            z-index: 21;
-            background-color: $white;
-            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
-            padding: 1px;
-
-            .list-item {
-                color: $black;
-                width: 100%;
-                height: 25px;
-                font-weight: 400;
-                display: flex;
-                justify-content: left;
-                align-items: center;
-                padding-left: 5px;
-            }
-
-            .list-item:hover {
-                background-color: lightgrey;
-            }
-        }
-    }
-
-    &.numBtn {
-        cursor: pointer;
-        background: transparent;
-
-        &.slider {
-            color: $white;
-            cursor: pointer;
-            flex-direction: column;
-            background: transparent;
-
-            .menu-slider {
-                width: 100px;
-            }
-
-            .menuButton-dropdownBox {
-                position: absolute;
-                width: fit-content;
-                height: fit-content;
-                top: 100%;
-                z-index: 21;
-                background-color: #e3e3e3;
-                box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
-                border-radius: $standard-border-radius;
-
-                .menu-slider {
-                    height: 10px;
-                }
-                input[type='range']::-webkit-slider-runnable-track {
-                    background: gray;
-                    height: 3px;
-                }
-
-                input[type='range']::-webkit-slider-thumb {
-                    box-shadow: 1px 1px 1px #000000;
-                    border: 1px solid #000000;
-                    height: 10px;
-                    width: 10px;
-                    border-radius: 5px;
-                    background: #ffffff;
-                    cursor: pointer;
-                    -webkit-appearance: none;
-                    margin-top: -4px;
-                }
-            }
-        }
-
-        .button {
-            width: 25%;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-
-            &.number {
-                width: 50%;
-
-                .button-input {
-                    background: none;
-                    border: none;
-                    text-align: right;
-                    width: 100%;
-                    color: $white;
-                    height: 100%;
-                    text-align: center;
-                }
-
-                .button-input:focus {
-                    outline: none;
-                }
-            }
-        }
-
-        &.list {
-            width: 100%;
-            justify-content: space-around;
-            border: $standard-border;
-
-            .menuButton-dropdownList {
-                position: absolute;
-                width: fit-content;
-                height: fit-content;
-                min-width: 50%;
-                max-height: 50vh;
-                overflow-y: scroll;
-                top: 100%;
-                z-index: 21;
-                background-color: $white;
-                box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
-                padding: 1px;
-
-                .list-item {
-                    color: $black;
-                    width: 100%;
-                    height: 25px;
-                    font-weight: 400;
-                    display: flex;
-                    justify-content: center;
-                    align-items: center;
-                }
-
-                .list-item:hover {
-                    background-color: lightgrey;
-                }
-            }
-        }
-    }
-
-    &.editableText {
-        cursor: text;
-        display: flex;
-        flex-direction: row;
-        gap: 5px;
-        padding-left: 10px;
-        justify-content: flex-start;
-        color: black;
-        background-color: $light-gray;
-        padding: 5px;
-        padding-left: 10px;
-        width: 100%;
-        height: 100%;
-    }
-
-    &.drpDownBtn {
-        cursor: pointer;
-        background: transparent;
-        border: solid 0.5px grey;
-
-        &.true {
-            background: rgba(0, 0, 0, 0.3);
-        }
-
-        .menuButton-dropdownBox {
-            position: absolute;
-            width: 150px;
-            height: 250px;
-            top: 100%;
-            background-color: #e3e3e3;
-            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
-            border-radius: $standard-border-radius;
-        }
-    }
-
-    .menuButton-dropdown {
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        font-size: 15px;
-        grid-column: 2;
-        border-radius: 0px 7px 7px 0px;
-        width: 13px;
-        height: 100%;
-        right: 0;
-    }
-
-    .menuButton-dropdown-header {
-        width: 100%;
-        font-weight: 300;
-        padding: 5px;
-        overflow: hidden;
-        font-size: 12px;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-    }
-
-    .dropbox-background {
-        width: 200vw;
-        height: 200vh;
-        top: -100vh;
-        z-index: 20;
-        left: -100vw;
-        background: transparent;
-        position: fixed;
-    }
-}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
deleted file mode 100644
index e31407f4b..000000000
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ /dev/null
@@ -1,385 +0,0 @@
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-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 { Doc, StrListCast } from '../../../../fields/Doc';
-import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
-import { SelectionManager } from '../../../util/SelectionManager';
-import { undoable, UndoManager } from '../../../util/UndoManager';
-import { ContextMenu } from '../../ContextMenu';
-import { DocComponent } from '../../DocComponent';
-import { EditableView } from '../../EditableView';
-import { Colors } from '../../global/globalEnums';
-import { StyleProp } from '../../StyleProvider';
-import { FieldView, FieldViewProps } from '.././FieldView';
-import { OpenWhere } from '../DocumentView';
-import { RichTextMenu } from '../formattedText/RichTextMenu';
-import './FontIconBox.scss';
-
-export enum ButtonType {
-    TextButton = 'textBtn',
-    MenuButton = 'menuBtn',
-    DropdownList = 'drpdownList',
-    DropdownButton = 'drpdownBtn',
-    ClickButton = 'clickBtn',
-    DoubleButton = 'dblBtn',
-    ToggleButton = 'tglBtn',
-    ColorButton = 'colorBtn',
-    ToolButton = 'toolBtn',
-    NumberSliderButton = 'numSliderBtn',
-    NumberDropdownButton = 'numDropdownBtn',
-    NumberInlineButton = 'numInlineBtn',
-    EditableText = 'editableText',
-}
-
-export interface ButtonProps extends FieldViewProps {
-    type?: ButtonType;
-}
-@observer
-export class FontIconBox extends DocComponent<ButtonProps>() {
-    public static LayoutString(fieldKey: string) {
-        return FieldView.LayoutString(FontIconBox, fieldKey);
-    }
-    @observable noTooltip = false;
-    showTemplate = (): void => {
-        const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
-        dragFactory && this.props.addDocTab(dragFactory, OpenWhere.addRight);
-    };
-    dragAsTemplate = (): void => {
-        this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
-    };
-    useAsPrototype = (): void => {
-        this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)');
-    };
-
-    specificContextMenu = (): void => {
-        if (!Doc.noviceMode) {
-            const cm = ContextMenu.Instance;
-            cm.addItem({ description: 'Show Template', event: this.showTemplate, icon: 'tag' });
-            cm.addItem({ description: 'Use as Render Template', event: this.dragAsTemplate, icon: 'tag' });
-            cm.addItem({ description: 'Use as Prototype', event: this.useAsPrototype, icon: 'tag' });
-        }
-    };
-
-    static GetShowLabels() {
-        return BoolCast(Doc.UserDoc()._showLabel);
-    }
-    static SetShowLabels(show: boolean) {
-        Doc.UserDoc()._showLabel = show;
-    }
-    static GetRecognizeGestures() {
-        return BoolCast(Doc.UserDoc()._recognizeGestures);
-    }
-    static SetRecognizeGestures(show: boolean) {
-        Doc.UserDoc()._recognizeGestures = show;
-    }
-
-    // Determining UI Specs
-    @computed get label() {
-        return StrCast(this.rootDoc.icon_label, StrCast(this.rootDoc.title));
-    }
-    Icon = (color: string, iconFalse?: boolean) => {
-        let icon;
-        if (iconFalse ) {
-            icon = StrCast(this.dataDoc[this.fieldKey ?? 'iconFalse'] ?? this.dataDoc.icon, 'user') as any;
-            if (icon) return <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />
-            else return null
-        }
-        icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any;
-        const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />;
-        return !icon ? null : icon === 'pres-trail' ? trailsIcon() : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
-    };
-    @computed get dropdown() {
-        return BoolCast(this.rootDoc.dropDownOpen);
-    }
-    @computed get buttonList() {
-        return StrListCast(this.rootDoc.btnList);
-    }
-    @computed get type() {
-        return StrCast(this.rootDoc.btnType);
-    }
-
-    /**
-     * Types of buttons in dash:
-     * - Main menu button (LHS)
-     * - Tool button
-     * - Expandable button (CollectionLinearView)
-     * - Button inside of CollectionLinearView vs. outside of CollectionLinearView
-     * - Action button
-     * - Dropdown button
-     * - Color button
-     * - Dropdown list
-     * - Number button
-     **/
-
-    _batch: UndoManager.Batch | undefined = undefined;
-    /**
-     * Number button
-     */
-    @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(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));
-        const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</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`)}
-        />
-    }
-
-    /**
-     * Dropdown button
-     */
-    @computed get dropdownButton() {
-        const active: string = StrCast(this.rootDoc.dropDownOpen);
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        return (
-            <div
-                className={`menuButton ${this.type} ${active}`}
-                style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
-                onClick={action(() => {
-                    this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
-                    this.noTooltip = this.rootDoc.dropDownOpen;
-                    Doc.UnBrushAllDocs();
-                })}>
-                {this.Icon(color)}
-                {!this.label || !FontIconBox.GetShowLabels() ? null : (
-                    <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
-                        {' '}
-                        {this.label}{' '}
-                    </div>
-                )}
-                <div className="menuButton-dropdown" style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
-                    <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
-                </div>
-                {this.rootDoc.dropDownOpen ? <div className="menuButton-dropdownBox">{/* DROPDOWN BOX CONTENTS */}</div> : null}
-            </div>
-        );
-    }
-
-    /**
-     * Dropdown list
-     */
-    @computed get dropdownListButton() {
-        const active: string = StrCast(this.rootDoc.dropDownOpen);
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-
-        const 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();
-                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);
-        } catch (e) {
-            console.log(e);
-        }
-
-        console.log("current item: ", text);
-
-        // 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,
-                    val: value,
-                    style: { fontFamily: value },
-                    onClick: undoable(() => script.script.run({ self: this.rootDoc, value }), value)
-                    // shortcut: '#',
-                }
-            ));
-
-
-        return (
-            <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}
-            />
-        )
-    }
-
-
-    @computed get colorScript() {
-        return ScriptCast(this.rootDoc.script);
-    }
-
-    /**
-     * Color button
-     */
-    @computed get colorButton() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const curColor = this.colorScript?.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent';
-        const tooltip: string = StrCast(this.rootDoc.toolTip);
-
-        return (
-            <ColorPicker 
-                setSelectedColor={(value) => {
-                    const s = this.colorScript;
-                    s && undoable(() => s.script.run({ self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)();
-                }}
-                selectedColor={curColor}
-                type={Type.PRIM}
-                color={color}
-                icon={this.Icon(color)!}
-                tooltip={tooltip}
-                label={this.label}
-            />
-        )
-    }
-
-    @computed get toggleButton() {
-        // Determine the type of toggle button
-        const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle);
-        const buttonText: string = StrCast(this.rootDoc.buttonText);
-        const tooltip: string = StrCast(this.rootDoc.toolTip);
-        
-        // Colors
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-
-        console.log(buttonText, tooltip);
-        return (
-            <Toggle tooltip={`Toggle + ${tooltip}`} toggleType={ToggleType.BUTTON} type={Type.PRIM} toggleStatus={switchToggle} text={buttonText} color={color} icon={this.Icon(color)!} label={this.label}/>
-        )
-    }
-
-    /**
-     * Default
-     */
-    @computed get defaultButton() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const tooltip: string = StrCast(this.rootDoc.toolTip);
-
-        return (
-            <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label}/>
-        )
-    }
-
-    @computed get editableText() {
-        // Script for running the toggle
-        const script = ScriptCast(this.rootDoc.script);
-        // Function to run the script
-        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'} />
-                <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}>
-                    <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
-                </div>
-            </div>
-        );
-    }
-
-    render() {
-        // determine dash button metadata
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const tooltip: string = StrCast(this.rootDoc.toolTip);
-
-        // TODO:glr Add label of button type
-        let button: JSX.Element = this.defaultButton;
-        // prettier-ignore
-        switch (this.type) {
-            case ButtonType.EditableText: 
-                button = this.editableText;
-                break;
-            case ButtonType.DropdownList:
-                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.ToggleButton: button = this.toggleButton; break;
-            case ButtonType.TextButton: 
-                // 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 = (
-                    <IconButton tooltip={tooltip} color={color} icon={this.Icon(color)!} label={this.label}/>
-                );
-                break;
-            case ButtonType.MenuButton: button = (
-                    <IconButton tooltip={tooltip} tooltipPlacement='right' size={Size.LARGE} color={color} icon={this.Icon(color)!} label={this.label}/>
-                );
-                break;
-        }
-
-        return button;
-    }
-}
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 5480600b0..64ea4f52c 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -15,6 +15,8 @@ import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup';
 import { LightboxView } from '../LightboxView';
 import { EditorView } from 'prosemirror-view';
 import './AnchorMenu.scss';
+import { ColorPicker, Group, IconButton, Popup, Size, Type } from 'browndash-components';
+import { StrCast } from '../../../fields/Types';
 
 @observer
 export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -42,7 +44,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     ];
 
     @observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)';
-    @observable private _showLinkPopup: boolean = false;
 
     @observable public Status: 'marquee' | 'annotation' | '' = '';
 
@@ -131,7 +132,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             () => this._opacity,
             opacity => {
                 if (!opacity) {
-                    this._showLinkPopup = false;
                     this.setGPTPopupVis(false);
                     this.setGPTPopupText('');
                 }
@@ -141,7 +141,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         this._disposer = reaction(
             () => SelectionManager.Views().slice(),
             selected => {
-                this._showLinkPopup = false;
                 this.setGPTPopupVis(false);
                 this.setGPTPopupText('');
                 AnchorMenu.Instance.fadeOut(true);
@@ -253,49 +252,25 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         AnchorMenu.Instance.fadeOut(true);
     };
 
-    @action
-    toggleLinkPopup = (e: React.MouseEvent) => {
-        //ignore the potential null type error because this method cannot be called unless the user selects text and clicks the link button
-        //change popup visibility field to visible
-        this._showLinkPopup = !this._showLinkPopup;
-    };
-
     @computed get highlighter() {
-        const button = (
-            <button className="antimodeMenu-button anchor-color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}>
-                <div className="anchor-color-preview">
-                    <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />
-                    <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div>
-                </div>
-            </button>
-        );
-
-        const dropdownContent = (
-            <div className="dropdown">
-                <p>Change highlighter color:</p>
-                <div className="color-wrapper">
-                    {this._palette.map(color => {
-                        if (color) {
-                            return this.highlightColor === color ? (
-                                <button className="color-button active" key={`active ${color}`} style={{ backgroundColor: color }} onPointerDown={e => this.changeHighlightColor(color, e)}></button>
-                            ) : (
-                                <button className="color-button" key={`inactive ${color}`} style={{ backgroundColor: color }} onPointerDown={e => this.changeHighlightColor(color, e)}></button>
-                            );
-                        }
-                    })}
-                </div>
-            </div>
-        );
-        return (
-            <Tooltip key="highlighter" title={<div className="dash-tooltip">{'Click to Highlight'}</div>}>
-                <div className="anchorMenu-highlighter">
-                    <ButtonDropdown key={'highlighter'} button={button} dropdownContent={dropdownContent} pdf={true} />
-                </div>
-            </Tooltip>
-        );
+        return <Group>
+            <IconButton 
+                icon={<FontAwesomeIcon icon="highlighter" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />}
+                tooltip={'Click to Highlight'}
+                onClick={this.highlightClicked}
+                colorPicker={this.highlightColor}
+                color={StrCast(Doc.UserDoc().userColor)}
+            />
+            <ColorPicker
+                colorPickerType={'github'}
+                selectedColor={this.highlightColor}
+                setSelectedColor={color => this.changeHighlightColor(color)}
+                size={Size.XSMALL}
+            />
+        </Group>
     }
 
-    @action changeHighlightColor = (color: string, e: React.PointerEvent) => {
+    @action changeHighlightColor = (color: string) => {
         const col: ColorState = {
             hex: color,
             hsl: { a: 0, h: 0, s: 0, l: 0, source: '' },
@@ -304,8 +279,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             oldHue: 0,
             source: '',
         };
-        e.preventDefault();
-        e.stopPropagation();
         this.highlightColor = Utils.colorString(col);
     };
 
@@ -339,11 +312,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.Status === 'marquee' ? (
                 <>
                     {this.highlighter}
-                    <Tooltip key="annotate" title={<div className="dash-tooltip">Drag to Place Annotation</div>}>
-                        <button className="antimodeMenu-button annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: 'grab' }}>
-                            <FontAwesomeIcon icon="comment-alt" size="lg" />
-                        </button>
-                    </Tooltip>
+                    <IconButton 
+                        tooltip={'Drag to Place Annotation'} 
+                        onPointerDown={this.pointerDown} 
+                        icon={<FontAwesomeIcon icon="comment-alt"/>}
+                        color={StrCast(Doc.UserDoc().userColor)}
+                    />
                     {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection*/}
                     {AnchorMenu.Instance.StartCropDrag === unimplementedFunction && this.canSummarize() && (
                         <Tooltip key="gpt" title={<div className="dash-tooltip">Summarize with AI</div>}>
@@ -364,32 +338,35 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                         mode={this.GPTMode}
                     />
                     {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
-                        <Tooltip key="annoaudiotate" title={<div className="dash-tooltip">Click to Record Annotation</div>}>
-                            <button className="antimodeMenu-button annotate" onPointerDown={this.audioDown} style={{ cursor: 'grab' }}>
-                                <FontAwesomeIcon icon="microphone" size="lg" />
-                            </button>
-                        </Tooltip>
+                        <IconButton 
+                            tooltip={'Click to Record Annotation'}
+                            onPointerDown={this.audioDown}
+                            icon={<FontAwesomeIcon icon="microphone" />}
+                            color={StrCast(Doc.UserDoc().userColor)}
+                        />
                     )}
                     {this.canEdit() && (
-                        <Tooltip key="gpttextedit" title={<div className="dash-tooltip">AI edit suggestions</div>}>
-                            <button className="antimodeMenu-button annotate" onPointerDown={this.gptEdit} style={{ cursor: 'grab' }}>
-                                <FontAwesomeIcon icon="pencil-alt" size="lg" />
-                            </button>
-                        </Tooltip>
+                        <IconButton 
+                            tooltip={'AI edit suggestions'}
+                            onPointerDown={this.gptEdit}
+                            icon={<FontAwesomeIcon icon="pencil-alt" />}
+                            color={StrCast(Doc.UserDoc().userColor)}
+                        />
                     )}
-                    <Tooltip key="link" title={<div className="dash-tooltip">Find document to link to selected text</div>}>
-                        <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup}>
-                            <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
-                            <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'top left', top: 12, left: 12 }} icon={'link'} size="lg" />
-                        </button>
-                    </Tooltip>
-                    <LinkPopup key="popup" showPopup={this._showLinkPopup} linkCreateAnchor={this.onMakeAnchor} />,
+                    <Popup 
+                        tooltip='Find document to link to selected text'
+                        type={Type.PRIM}
+                        icon={<FontAwesomeIcon icon={'search'} />}
+                        popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />}
+                        color={StrCast(Doc.UserDoc().userColor)}
+                    />
                     {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
-                        <Tooltip key="crop" title={<div className="dash-tooltip">Click/Drag to create cropped image</div>}>
-                            <button className="antimodeMenu-button annotate" onPointerDown={this.cropDown} style={{ cursor: 'grab' }}>
-                                <FontAwesomeIcon icon="image" size="lg" />
-                            </button>
-                        </Tooltip>
+                        <IconButton 
+                            tooltip={'Click/Drag to create cropped image'}
+                            onPointerDown={this.cropDown}
+                            icon={<FontAwesomeIcon icon="image"/>}
+                            color={StrCast(Doc.UserDoc().userColor)}
+                        />
                     )}
                 </>
             ) : (
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 5516cf205..a439aea3e 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -9,7 +9,6 @@
     background: none;
     z-index: 1000;
     padding: 0px;
-    overflow: scroll;
     cursor: default;
 
     .searchBox-bar {
@@ -22,7 +21,7 @@
         padding: 5px;
         top: 0px;
         position: sticky;
-        background: $light-gray;
+        overflow-y: scroll;
         border-bottom: $standard-border;
 
         .searchBox-type {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 12c25ca09..11f22ad5e 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -17,6 +17,7 @@ import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import './SearchBox.scss';
 import { fetchRecommendations } from '../newlightbox/utils';
 import { IRecommendation, Recommendation } from '../newlightbox/components';
+import { Colors } from '../global/globalEnums';
 
 const DAMPENING_FACTOR = 0.9;
 const MAX_ITERATIONS = 25;
@@ -528,7 +529,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         ))
 
         return (
-            <div style={{ pointerEvents: 'all' }} className="searchBox-container">
+            <div className="searchBox-container" style={{pointerEvents: 'all', background: StrCast(Doc.UserDoc().userBackgroundColor)}}>
                 <div className="searchBox-bar">
                     {isLinkSearch ? null : (
                         <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
@@ -552,14 +553,14 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                     />
                 </div>
                 {resultsJSX.length > 0 && <div className="searchBox-results-container">
-                    <div className="section-header">
+                    <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>}
                 {recommendationsJSX.length > 0 && <div className="searchBox-recommendations-container">
-                    <div className="section-header">
+                    <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>
diff --git a/src/client/views/selectedDoc/SelectedDocView.scss b/src/client/views/selectedDoc/SelectedDocView.scss
new file mode 100644
index 000000000..156dfc37b
--- /dev/null
+++ b/src/client/views/selectedDoc/SelectedDocView.scss
@@ -0,0 +1,3 @@
+.selectedDocView-container {
+    
+}
\ No newline at end of file
diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx
new file mode 100644
index 000000000..1c9e124a2
--- /dev/null
+++ b/src/client/views/selectedDoc/SelectedDocView.tsx
@@ -0,0 +1,47 @@
+import React = require('react');
+import { Doc } from "../../../fields/Doc";
+import { observer } from "mobx-react";
+import { computed } from "mobx";
+import { StrCast } from "../../../fields/Types";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Colors, ListBox } from 'browndash-components';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DocFocusOptions } from '../nodes/DocumentView';
+
+export interface SelectedDocViewProps {
+    selectedDocs: Doc[];
+}
+
+@observer
+export class SelectedDocView extends React.Component<SelectedDocViewProps> {
+
+    @computed get selectedDocs() {
+        return this.props.selectedDocs;
+    }
+    
+
+    render() {
+        return <div className={`selectedDocView-container`}>
+            <ListBox
+                items={this.selectedDocs.map((doc) => {
+                    const icon = Doc.toIcon(doc);
+                    const iconEle = <FontAwesomeIcon size={'1x'} icon={icon} />;
+                    const text = StrCast(doc.title)
+                    const finished = () => {
+                        
+                    };
+                    const options: DocFocusOptions = {
+                        playAudio: false,
+                    };
+                    return {
+                        text: text,
+                        val: StrCast(doc._id),
+                        icon: iconEle,
+                        onClick: () => {DocumentManager.Instance.showDocument(doc, options, finished);}
+                    }
+                })}
+                color={Colors.WHITE}
+            />
+        </div>
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/selectedDoc/index.ts b/src/client/views/selectedDoc/index.ts
new file mode 100644
index 000000000..1f1db91f6
--- /dev/null
+++ b/src/client/views/selectedDoc/index.ts
@@ -0,0 +1 @@
+export * from './SelectedDocView'
\ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index b613a0e3e..1b0c6803c 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, IconButton, FontSize, Size, Type } from 'browndash-components';
+import { Button, IconButton, FontSize, Size, Type, isDark } from 'browndash-components';
 import { action, computed, observable, reaction } from 'mobx';
 import { Tooltip } from '@mui/material';
 import { observer } from 'mobx-react';
@@ -36,9 +36,10 @@ export class TopBar extends React.Component {
         });
     };
 
-    @observable textColor: string = Colors.LIGHT_GRAY;
+    @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 ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY;
+        return PingManager.Instance.IsBeating ? StrCast(Doc.UserDoc().userBackgroundColor, Colors.DARK_GRAY) : Colors.MEDIUM_GRAY;
     }
 
     @observable happyHeart: boolean = PingManager.Instance.IsBeating;
@@ -59,16 +60,16 @@ export class TopBar extends React.Component {
         return (
             <div className="topbar-left">
                 {Doc.ActiveDashboard ? (
-                    <IconButton onClick={this.navigateToHome} icon={<FontAwesomeIcon icon="home" />} color={this.textColor} />
+                    <IconButton onClick={this.navigateToHome} icon={<FontAwesomeIcon icon="home" />} color={this.color} />
                 ) : (
                     <div className="logo-container">
                         <img className="logo" src="/assets/medium-blue-light-blue-circle.png" alt="dash logo"></img>
-                        <span style={{ color: Colors.LIGHT_GRAY, fontWeight: 200 }}>brown</span>
-                        <span style={{ color: Colors.LIGHT_BLUE, fontWeight: 500 }}>dash</span>
+                        <span style={{ color: isDark(this.backgroundColor) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY, fontWeight: 200 }}>brown</span>
+                        <span style={{ color: isDark(this.backgroundColor) ? Colors.LIGHT_BLUE : Colors.MEDIUM_BLUE, fontWeight: 500 }}>dash</span>
                     </div>
                 )}
                 {Doc.ActiveDashboard && (
-                    <Button text="Explore" tooltip="Browsing mode for directly navigating to documents" size={Size.SMALL} color={this.textColor} onClick={action(() => (MainView.Instance._exploreMode = !MainView.Instance._exploreMode))} />
+                    <Button text="Explore" tooltip="Browsing mode for directly navigating to documents" size={Size.SMALL} color={this.color} onClick={action(() => (MainView.Instance._exploreMode = !MainView.Instance._exploreMode))} />
                 )}
             </div>
         );
@@ -97,7 +98,7 @@ export class TopBar extends React.Component {
                     text={StrCast(Doc.ActiveDashboard.title)}
                     tooltip="Open Dashboards"
                     size={Size.SMALL}
-                    color={'white'}
+                    color={this.color}
                     style={{fontWeight: 700, fontSize: '1rem'}}
                     onClick={(e: React.MouseEvent) => {
                         const dashView = Doc.ActiveDashboard && DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard);
@@ -118,7 +119,7 @@ export class TopBar extends React.Component {
                     <IconButton
                         tooltip="Work on a copy of the dashboard layout"
                         size={Size.SMALL}
-                        color={this.textColor}
+                        color={this.color}
                         onClick={async () => {
                             const batch = UndoManager.StartBatch('snapshot');
                             await DashboardView.snapshotDashboard();
@@ -142,23 +143,25 @@ export class TopBar extends React.Component {
                 {Doc.ActiveDashboard && <Button
                     text={GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'View Original'}
                     type={Type.TERT}
+                    color={this.variantColor}
                     onClick={() => {
                         SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
                     }}
                     size={Size.SMALL}
                 />}
-                <IconButton tooltip={"Server ⌘⇧S"} size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={ServerStats.Instance.open} icon={<FaStamp />} />
-                <IconButton tooltip={"Issue Reporter ⌘I"} size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={ReportManager.Instance.open} icon={<FaBug />} />
-                <IconButton tooltip={"Documentation ⌘D"} size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
-                <IconButton tooltip={"Settings ⌘⇧S"} size={Size.SMALL} color={Colors.LIGHT_GRAY} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} />
+                <IconButton tooltip={"Server ⌘⇧S"} size={Size.SMALL} color={this.color} onClick={ServerStats.Instance.open} icon={<FaStamp />} />
+                <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={PingManager.Instance.showAlert}
+                    type={Type.TERT}
                     tooltip={'Server is ' + (PingManager.Instance.IsBeating ? '' : 'NOT ') + 'running'}
                     color={this.happyHeart ? Colors.LIGHT_BLUE : Colors.ERROR_RED}
                     icon={<FontAwesomeIcon icon={this.happyHeart ? 'heart' : 'heart-broken'} />}
                 />
-                {/* <Button text={'Logout'} borderRadius={5} hoverStyle={'gray'} backgroundColor={Colors.DARK_GRAY} color={this.textColor} fontSize={FontSize.SECONDARY} onClick={() => window.location.assign(Utils.prepend('/logout'))} /> */}
+                {/* <Button text={'Logout'} borderRadius={5} hoverStyle={'gray'} backgroundColor={Colors.DARK_GRAY} color={this.color} fontSize={FontSize.SECONDARY} onClick={() => window.location.assign(Utils.prepend('/logout'))} /> */}
             </div>
         );
     }
@@ -168,8 +171,9 @@ export class TopBar extends React.Component {
             //TODO:glr Add support for light / dark mode
             <div style={{ 
                 pointerEvents: 'all',
-                color: this.textColor,
-                background: this.backgroundColor, 
+                color: this.color,
+                background: this.backgroundColor,
+                // borderColor: this.color
             }} className="topbar-container">
                 <div className="topbar-inner-container">
                     {this.topbarLeft}
-- 
cgit v1.2.3-70-g09d2


From 75a7c44f9615c4ac024403011a968f8b3038c500 Mon Sep 17 00:00:00 2001
From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com>
Date: Tue, 18 Jul 2023 01:00:46 -0400
Subject: added number inputs to right hand side

---
 package-lock.json                                  | 639 +++++++++++----------
 package.json                                       |   2 +-
 src/Utils.ts                                       |  16 +
 src/client/util/SharingManager.scss                |   1 +
 src/client/util/SharingManager.tsx                 |  16 +-
 src/client/views/DashboardView.tsx                 |   4 +-
 src/client/views/PropertiesButtons.tsx             |  46 --
 src/client/views/PropertiesView.scss               |   2 +-
 src/client/views/PropertiesView.tsx                |  90 ++-
 src/client/views/UndoStack.tsx                     |   6 +-
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   5 +-
 src/client/views/pdf/AnchorMenu.tsx                |  52 +-
 src/client/views/selectedDoc/SelectedDocView.tsx   |   2 +-
 src/client/views/topbar/TopBar.tsx                 |  15 +-
 14 files changed, 476 insertions(+), 420 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index d4facf59e..6d1071aeb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3270,7 +3270,7 @@
         "@types/bcrypt-nodejs": {
             "version": "0.0.30",
             "resolved": "https://registry.npmjs.org/@types/bcrypt-nodejs/-/bcrypt-nodejs-0.0.30.tgz",
-            "integrity": "sha512-gSWCu7EOXhcM0FYM8tanV0deaX1VM07sgBS8dSUmgnAQ5/3Xn6k+mWF+ZfE+c/OCW14IVnNdTOKcHgvL78oDFQ==",
+            "integrity": "sha1-TN2WtJKTs5MhIuS34pVD415rrlg=",
             "dev": true
         },
         "@types/bezier-js": {
@@ -4522,7 +4522,7 @@
         "@types/typescript": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/@types/typescript/-/typescript-2.0.0.tgz",
-            "integrity": "sha512-WMEWfMISiJ2QKyk5/dSdgL0ZwP//PZj0jmDU0hMh51FmLq4WIYzjlngsUQZXejQL+QtkXJUOGjb3G3UCvgZuSQ==",
+            "integrity": "sha1-xDNTnJi64oaCswfqp6D9IRW4PCg=",
             "dev": true,
             "requires": {
                 "typescript": "*"
@@ -4837,7 +4837,7 @@
                 "textarea-caret": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.0.2.tgz",
-                    "integrity": "sha512-gRzeti2YS4did7UJnPQ47wrjD+vp+CJIe9zbsu0bJ987d8QVLvLNG9757rqiQTIy4hGIeFauTTJt5Xkn51UkXg=="
+                    "integrity": "sha1-82DEhpmqGr9xhoCkOjGoUGZcLK8="
                 }
             }
         },
@@ -4856,7 +4856,7 @@
         "Base64": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
-            "integrity": "sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw=="
+            "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg="
         },
         "D": {
             "version": "1.0.0",
@@ -4936,7 +4936,7 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
+            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
         },
         "agent-base": {
             "version": "6.0.2",
@@ -5105,7 +5105,7 @@
         "any-promise": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
-            "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
+            "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
         },
         "anymatch": {
             "version": "2.0.0",
@@ -5258,7 +5258,7 @@
         "arr-diff": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-            "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA=="
+            "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
         },
         "arr-flatten": {
             "version": "1.1.0",
@@ -5268,7 +5268,7 @@
         "arr-union": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-            "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q=="
+            "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
         },
         "array-back": {
             "version": "2.0.0",
@@ -5370,7 +5370,7 @@
         "array-flatten": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-            "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+            "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
         },
         "array-includes": {
             "version": "3.1.6",
@@ -5403,7 +5403,7 @@
         "array-unique": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-            "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ=="
+            "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
         },
         "array.prototype.flat": {
             "version": "1.3.1",
@@ -5480,7 +5480,7 @@
         "assert-plus": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-            "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
+            "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
         },
         "assertion-error": {
             "version": "1.1.0",
@@ -5490,7 +5490,7 @@
         "assign-symbols": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-            "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw=="
+            "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
         },
         "ast-types-flow": {
             "version": "0.0.7",
@@ -5520,7 +5520,7 @@
         "async-foreach": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
-            "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA=="
+            "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI="
         },
         "async-limiter": {
             "version": "1.0.1",
@@ -5531,7 +5531,7 @@
         "asynckit": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-            "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+            "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
         },
         "atob": {
             "version": "2.1.2",
@@ -5581,7 +5581,7 @@
         "aws-sign2": {
             "version": "0.7.0",
             "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-            "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="
+            "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
         },
         "aws4": {
             "version": "1.12.0",
@@ -5659,7 +5659,7 @@
         "babel": {
             "version": "6.23.0",
             "resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
-            "integrity": "sha512-ZDcCaI8Vlct8PJ3DvmyqUz+5X2Ylz3ZuuItBe/74yXosk2dwyVo/aN7MCJ8HJzhnnJ+6yP4o+lDgG9MBe91DLA=="
+            "integrity": "sha1-0NHn2APpdHZb7qMjLU4VPA77kPQ="
         },
         "babel-code-frame": {
             "version": "6.26.0",
@@ -5818,12 +5818,12 @@
         "babel-plugin-syntax-jsx": {
             "version": "6.18.0",
             "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
-            "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw=="
+            "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
         },
         "babel-runtime": {
             "version": "6.26.0",
             "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
-            "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+            "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
             "requires": {
                 "core-js": "^2.4.0",
                 "regenerator-runtime": "^0.11.0"
@@ -5867,7 +5867,7 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
+            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
         },
         "bail": {
             "version": "2.0.2",
@@ -5896,7 +5896,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -5932,12 +5932,12 @@
         "base16": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
-            "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ=="
+            "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA="
         },
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
+            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
         },
         "base64-js": {
             "version": "1.5.1",
@@ -5957,18 +5957,18 @@
         "batch": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
-            "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+            "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
             "dev": true
         },
         "bcrypt-nodejs": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz",
-            "integrity": "sha512-NmTbLm867btBHCBZ222FQXkQKzecB0KG6pTXFa6NeTVZaSnLfCsx7EK2PL3J+kX8xJThUquEBbhimRCKKZX9zA=="
+            "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs="
         },
         "bcrypt-pbkdf": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-            "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+            "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
             "requires": {
                 "tweetnacl": "^0.14.3"
             }
@@ -5981,7 +5981,7 @@
         "bezier-curve": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/bezier-curve/-/bezier-curve-1.0.0.tgz",
-            "integrity": "sha512-h6uZJ6qdFfswS1rIRericgouhTeiVi/MnH10OKtCu2IZzXa+ZcjaxRLHY4u/evRsJcxYbbiNkgWQj2Z4UIcpEQ=="
+            "integrity": "sha1-o9+v6rEqlMRicw1QeYxSqEBdc3k="
         },
         "bezier-js": {
             "version": "4.1.1",
@@ -6120,7 +6120,7 @@
         "boolbase": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
-            "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+            "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
         },
         "bootstrap": {
             "version": "4.6.2",
@@ -6182,7 +6182,7 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
@@ -6198,9 +6198,9 @@
             }
         },
         "browndash-components": {
-            "version": "0.0.76",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.76.tgz",
-            "integrity": "sha512-2ZrDNGxbXnmqRSBifCn6+czz/LqeuyZwSsnRj73Wmo9OOKvYIneGZE17/aFkAl76rXdVmZCcfBdKKBcwEYwNHQ==",
+            "version": "0.0.78",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.0.78.tgz",
+            "integrity": "sha512-kW9RuemZEA77KPc3zAE1/PnwDOP0gjqfP2xBDkJyDLvWIXSUUDTDJBCBxvFNOjU83OyZWaDxhrFtSgFnSKX4LQ==",
             "requires": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
@@ -6730,7 +6730,7 @@
         "browser-assert": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz",
-            "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ=="
+            "integrity": "sha1-mqpaKox0aFwq4Fv+Ru/WBvBowgA="
         },
         "browser-process-hrtime": {
             "version": "1.0.0",
@@ -6774,7 +6774,7 @@
         "buffer-crc32": {
             "version": "0.2.13",
             "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
-            "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="
+            "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
         },
         "buffer-equal-constant-time": {
             "version": "1.0.1",
@@ -6800,7 +6800,7 @@
         "built-in-math-eval": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/built-in-math-eval/-/built-in-math-eval-0.3.0.tgz",
-            "integrity": "sha512-5XD5cujru60ooKJ4sGZqoH5v2Xvgw7ezV54gJX/OnPkgDKoH3BnlMEi8xW6hl8xaEjxKHebgrsawroeZnGwIMA==",
+            "integrity": "sha1-JA3CHLOJQ5WIxhxGDrAHZJfvxBw=",
             "requires": {
                 "math-codegen": "^0.3.5"
             }
@@ -6923,7 +6923,7 @@
         "caller-callsite": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
-            "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==",
+            "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
             "requires": {
                 "callsites": "^2.0.0"
             },
@@ -6931,14 +6931,14 @@
                 "callsites": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
-                    "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="
+                    "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
                 }
             }
         },
         "caller-path": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
-            "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==",
+            "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
             "requires": {
                 "caller-callsite": "^2.0.0"
             }
@@ -7006,7 +7006,7 @@
         "caseless": {
             "version": "0.12.0",
             "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-            "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
+            "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
         },
         "ccount": {
             "version": "2.0.1",
@@ -7083,12 +7083,12 @@
         "check-error": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
-            "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA=="
+            "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
         },
         "child_process": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
-            "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g=="
+            "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o="
         },
         "chokidar": {
             "version": "2.1.8",
@@ -7137,7 +7137,7 @@
         "clamp": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
-            "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA=="
+            "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
         },
         "class-transformer": {
             "version": "0.2.3",
@@ -7158,7 +7158,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -7208,7 +7208,7 @@
         "clipboard": {
             "version": "1.7.1",
             "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
-            "integrity": "sha512-smkaRaIQsrnKN1F3wd1/vY9Q+DeR4L8ZCXKeHCFC2j8RZuSBbuImcLdnIO4GTxmzJxQuDGNKkyfpGoPW7Ua5bQ==",
+            "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=",
             "requires": {
                 "good-listener": "^1.2.2",
                 "select": "^1.1.2",
@@ -7283,7 +7283,7 @@
         "clj-fuzzy": {
             "version": "0.3.3",
             "resolved": "https://registry.npmjs.org/clj-fuzzy/-/clj-fuzzy-0.3.3.tgz",
-            "integrity": "sha512-9cyh9A8+OphDZeKIG21MgyDHWDkWxTvagwvFLVjtdi6eToFENF7iDLlKwhHrnBQRSQwprKNhazG053nE/UgwfQ=="
+            "integrity": "sha1-seU0MJHFIC28UlMoY+HEp4RX8D0="
         },
         "clone-deep": {
             "version": "4.0.1",
@@ -7318,7 +7318,7 @@
         "collection-visit": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-            "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
+            "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
             "requires": {
                 "map-visit": "^1.0.0",
                 "object-visit": "^1.0.0"
@@ -7344,7 +7344,7 @@
         "color-name": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-            "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+            "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
         },
         "color-string": {
             "version": "1.9.1",
@@ -7413,7 +7413,7 @@
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
+            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -7423,7 +7423,7 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
+            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
         },
         "compress-commons": {
             "version": "2.1.1",
@@ -7479,7 +7479,7 @@
                 "bytes": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
-                    "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+                    "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
                     "dev": true
                 },
                 "debug": {
@@ -7494,7 +7494,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 }
             }
@@ -7502,7 +7502,7 @@
         "concat-map": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-            "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
         },
         "concat-stream": {
             "version": "1.6.2",
@@ -7568,7 +7568,7 @@
         "connect-flash": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz",
-            "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA=="
+            "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA="
         },
         "connect-history-api-fallback": {
             "version": "1.6.0",
@@ -7626,7 +7626,7 @@
         "console-control-strings": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-            "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
+            "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
         },
         "constantinople": {
             "version": "3.1.2",
@@ -7707,7 +7707,7 @@
         "cookie-signature": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-            "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+            "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
         },
         "cookies": {
             "version": "0.8.0",
@@ -7752,7 +7752,7 @@
         "copy-descriptor": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-            "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw=="
+            "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
         },
         "copy-webpack-plugin": {
             "version": "4.6.0",
@@ -7969,7 +7969,7 @@
         "crypto-random-string": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
-            "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg=="
+            "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
         },
         "css-box-model": {
             "version": "1.2.1",
@@ -7982,7 +7982,7 @@
         "css-color-keywords": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
-            "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="
+            "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
         },
         "css-in-js-utils": {
             "version": "2.0.1",
@@ -8151,7 +8151,7 @@
         "custom-event": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-            "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
+            "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
         },
         "cyclist": {
             "version": "1.0.1",
@@ -8159,6 +8159,16 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3": {
             "version": "7.8.4",
             "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
@@ -8438,7 +8448,7 @@
         "dashdash": {
             "version": "1.14.1",
             "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-            "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+            "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
             "requires": {
                 "assert-plus": "^1.0.0"
             }
@@ -8490,7 +8500,7 @@
         "de-indent": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
-            "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
+            "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0="
         },
         "debounce": {
             "version": "1.2.1",
@@ -8508,7 +8518,7 @@
         "decamelize": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-            "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
+            "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
         },
         "decimal.js-light": {
             "version": "2.5.1",
@@ -8747,7 +8757,7 @@
         "delayed-stream": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-            "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+            "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
         },
         "delegate": {
             "version": "3.2.0",
@@ -8757,7 +8767,7 @@
         "delegates": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-            "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
+            "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
         },
         "denque": {
             "version": "1.5.1",
@@ -8860,7 +8870,7 @@
                 "import-fresh": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
-                    "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+                    "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
                     "requires": {
                         "caller-path": "^2.0.0",
                         "resolve-from": "^3.0.0"
@@ -8895,7 +8905,7 @@
                 "parse-json": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-                    "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+                    "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
                     "requires": {
                         "error-ex": "^1.3.1",
                         "json-parse-better-errors": "^1.0.1"
@@ -8909,7 +8919,7 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="
+                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
                 },
                 "string-width": {
                     "version": "4.2.3",
@@ -8981,7 +8991,7 @@
         "deps-regex": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.1.4.tgz",
-            "integrity": "sha512-3tzwGYogSJi8HoG93R5x9NrdefZQOXgHgGih/7eivloOq6yC6O+yoFxZnkgP661twvfILONfoKRdF9GQOGx2RA=="
+            "integrity": "sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ="
         },
         "dequal": {
             "version": "2.0.3",
@@ -9186,7 +9196,7 @@
         "double-bits": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz",
-            "integrity": "sha512-BCLEIBq0O/DWoA7BsCu/R+RP0ZXiowP8BhtJT3qeuuQEBpnS8LK/Wo6UTJQv6v8mK1fj8n90YziHLwGdM5whSg=="
+            "integrity": "sha1-WKu6RUlNpND6Nrc60RoobJGEscY="
         },
         "duplexer3": {
             "version": "0.1.5",
@@ -9234,7 +9244,7 @@
         "ecc-jsbn": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-            "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+            "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
             "requires": {
                 "jsbn": "~0.1.0",
                 "safer-buffer": "^2.1.0"
@@ -9251,7 +9261,7 @@
         "ee-first": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-            "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+            "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
         },
         "electron-to-chromium": {
             "version": "1.4.454",
@@ -9280,7 +9290,7 @@
         "encodeurl": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-            "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
+            "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
         },
         "encoding": {
             "version": "0.1.13",
@@ -9355,7 +9365,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -9539,6 +9549,28 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -9550,6 +9582,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -9561,12 +9594,12 @@
         "escape-html": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-            "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+            "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
         },
         "escape-string-regexp": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-            "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
+            "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
         },
         "escodegen": {
             "version": "1.14.3",
@@ -10879,7 +10912,7 @@
         "etag": {
             "version": "1.8.1",
             "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-            "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
+            "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
         },
         "event-target-shim": {
             "version": "5.0.1",
@@ -10951,7 +10984,7 @@
         "exeq": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/exeq/-/exeq-2.4.0.tgz",
-            "integrity": "sha512-B648qbDS00nQZv9UQGLT5RbZm/5dNBX10F8oWeXcgpFHSLm1249u95t/3sn2wXdQjLhlF+edAECdshFtSr1K0Q==",
+            "integrity": "sha1-Td8qaEZIxCeteZNJzzO9dTWPiEo=",
             "requires": {
                 "bluebird": "^3.0.3",
                 "native-or-bluebird": "^1.2.0"
@@ -10960,7 +10993,7 @@
         "exif": {
             "version": "0.6.0",
             "resolved": "https://registry.npmjs.org/exif/-/exif-0.6.0.tgz",
-            "integrity": "sha512-gEwM4uanNMfLnDNKclZ7jPEA99E3rpy4ntoS6QW8u6murZjl1o8qRaPdMoC46Syg3d9/QaET0bYKhWlTwJCPgg==",
+            "integrity": "sha1-YKYmaAdlQst+T1cZnUrG830sX0o=",
             "requires": {
                 "debug": "^2.2"
             },
@@ -10976,7 +11009,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
@@ -10988,7 +11021,7 @@
         "expand-brackets": {
             "version": "2.1.4",
             "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-            "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
+            "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
             "requires": {
                 "debug": "^2.3.3",
                 "define-property": "^0.2.5",
@@ -11010,7 +11043,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -11018,7 +11051,7 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
@@ -11026,7 +11059,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
@@ -11109,7 +11142,7 @@
         "express-flash": {
             "version": "0.0.2",
             "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz",
-            "integrity": "sha512-QVUR0ZZRCaa8+iPHoUQaQJrQWcQuK/Q+19M7IUIdIEtvwhrA/ifHT7y1CVJI41YfGiOQnbGtn3uvd2vOdgu58A==",
+            "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=",
             "requires": {
                 "connect-flash": "0.1.x"
             }
@@ -11166,7 +11199,7 @@
         "expressjs": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/expressjs/-/expressjs-1.0.1.tgz",
-            "integrity": "sha512-eFnQ5bMJxTZ29XwRJPV8ee/OURBBMS6Fm+b5rvMMEyz6u2IxPEh2SRzMZt9WvgnV+SMLmnzkALE1DnGG1HxJCw=="
+            "integrity": "sha1-IgMoRpoY31rWFeK3oM6ZXxf7ru8="
         },
         "ext": {
             "version": "1.7.0",
@@ -11193,7 +11226,7 @@
         "extend-shallow": {
             "version": "3.0.2",
             "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+            "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
             "requires": {
                 "assign-symbols": "^1.0.0",
                 "is-extendable": "^1.0.1"
@@ -11257,7 +11290,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -11265,7 +11298,7 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
@@ -11335,7 +11368,7 @@
         "extsprintf": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-            "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
+            "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
         },
         "fast-deep-equal": {
             "version": "3.1.3",
@@ -11361,7 +11394,7 @@
         "fast-levenshtein": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-            "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+            "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
             "dev": true
         },
         "fast-text-encoding": {
@@ -11416,7 +11449,7 @@
         "fd-slicer": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
-            "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+            "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
             "requires": {
                 "pend": "~1.2.0"
             }
@@ -11424,7 +11457,7 @@
         "ffmpeg": {
             "version": "0.0.4",
             "resolved": "https://registry.npmjs.org/ffmpeg/-/ffmpeg-0.0.4.tgz",
-            "integrity": "sha512-3TgWUJJlZGQn+crJFyhsO/oNeRRnGTy6GhgS98oUCIfZrOW5haPPV7DUfOm3xJcHr5q3TJpjk2GudPutrNisRA==",
+            "integrity": "sha1-HEYN+OfaUSf2LO70v6BsWciWMMs=",
             "requires": {
                 "when": ">= 0.0.1"
             }
@@ -11471,7 +11504,7 @@
         "fill-range": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-            "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+            "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
             "requires": {
                 "extend-shallow": "^2.0.1",
                 "is-number": "^3.0.0",
@@ -11482,7 +11515,7 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
@@ -11492,7 +11525,7 @@
         "filter-obj": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
-            "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ=="
+            "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs="
         },
         "finalhandler": {
             "version": "1.2.0",
@@ -11526,7 +11559,7 @@
         "find": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/find/-/find-0.1.7.tgz",
-            "integrity": "sha512-jPrupTOe/pO//3a9Ty2o4NqQCp0L46UG+swUnfFtdmtQVN8pEltKpAqR7Nuf6vWn0GBXx5w+R1MyZzqwjEIqdA==",
+            "integrity": "sha1-yGyHrxqxjyIrvjjeyGy8dg0Wpvs=",
             "requires": {
                 "traverse-chain": "~0.1.0"
             }
@@ -11675,7 +11708,7 @@
         "fluent-ffmpeg": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz",
-            "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==",
+            "integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=",
             "requires": {
                 "async": ">=0.2.9",
                 "which": "^1.1.1"
@@ -11758,12 +11791,12 @@
         "for-in": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-            "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ=="
+            "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
         },
         "forever-agent": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-            "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="
+            "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
         },
         "fork-ts-checker-webpack-plugin": {
             "version": "1.6.0",
@@ -11817,7 +11850,7 @@
         "fragment-cache": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-            "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
+            "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
             "requires": {
                 "map-cache": "^0.2.2"
             }
@@ -11825,7 +11858,7 @@
         "fresh": {
             "version": "0.5.2",
             "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-            "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
+            "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
         },
         "from2": {
             "version": "2.3.0",
@@ -11941,7 +11974,7 @@
         "fs.realpath": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-            "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
         },
         "fsevents": {
             "version": "1.2.13",
@@ -12119,7 +12152,7 @@
         "get-func-name": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
-            "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig=="
+            "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
         },
         "get-intrinsic": {
             "version": "1.2.0",
@@ -12163,12 +12196,12 @@
         "get-value": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-            "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA=="
+            "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
         },
         "getpass": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-            "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+            "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
             "requires": {
                 "assert-plus": "^1.0.0"
             }
@@ -12295,7 +12328,7 @@
         "golden-layout": {
             "version": "1.5.9",
             "resolved": "https://registry.npmjs.org/golden-layout/-/golden-layout-1.5.9.tgz",
-            "integrity": "sha512-iBXDQCXOTgUEQJo96zPbjDoy5bRIk9XW5l+q+pDgLnIyReqaa1aiQctNud4epsskyLt952BG521dew5Z1liSxA==",
+            "integrity": "sha1-o5vB9qZ+b4hreXwBbdkk6UJrp38=",
             "requires": {
                 "jquery": "*"
             }
@@ -12303,7 +12336,7 @@
         "good-listener": {
             "version": "1.2.2",
             "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-            "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+            "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
             "requires": {
                 "delegate": "^3.1.2"
             }
@@ -12482,7 +12515,7 @@
         "har-schema": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-            "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="
+            "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
         },
         "har-validator": {
             "version": "5.1.5",
@@ -12532,19 +12565,19 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
+            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
         },
         "has-flag": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-            "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
+            "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
         },
         "has-property-descriptors": {
             "version": "1.0.0",
@@ -12575,12 +12608,12 @@
         "has-unicode": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-            "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+            "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
         },
         "has-value": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-            "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
+            "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
             "requires": {
                 "get-value": "^2.0.6",
                 "has-values": "^1.0.0",
@@ -12590,7 +12623,7 @@
         "has-values": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-            "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
+            "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
             "requires": {
                 "is-number": "^3.0.0",
                 "kind-of": "^4.0.0"
@@ -12604,7 +12637,7 @@
                 "kind-of": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-                    "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
+                    "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -12707,7 +12740,7 @@
         "hpack.js": {
             "version": "2.1.6",
             "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
-            "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
+            "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
             "dev": true,
             "requires": {
                 "inherits": "^2.0.1",
@@ -12736,7 +12769,7 @@
         "html": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/html/-/html-1.0.0.tgz",
-            "integrity": "sha512-lw/7YsdKiP3kk5PnR1INY17iJuzdAtJewxr14ozKJWbbR97znovZ0mh+WEMZ8rjc3lgTK+ID/htTjuyGKB52Kw==",
+            "integrity": "sha1-pUT6nqVJK/s6LMqCEKEL57WvH2E=",
             "requires": {
                 "concat-stream": "^1.4.7"
             }
@@ -12819,7 +12852,7 @@
         "http-browserify": {
             "version": "1.7.0",
             "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
-            "integrity": "sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==",
+            "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=",
             "requires": {
                 "Base64": "~0.2.0",
                 "inherits": "~2.0.1"
@@ -12833,7 +12866,7 @@
         "http-deceiver": {
             "version": "1.2.7",
             "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
-            "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
+            "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
             "dev": true
         },
         "http-errors": {
@@ -12880,7 +12913,7 @@
         "http-signature": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-            "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
+            "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
             "requires": {
                 "assert-plus": "^1.0.0",
                 "jsprim": "^1.2.2",
@@ -12899,12 +12932,12 @@
         "https": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
-            "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg=="
+            "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q="
         },
         "https-browserify": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
-            "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg=="
+            "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
         },
         "https-proxy-agent": {
             "version": "5.0.1",
@@ -12956,7 +12989,7 @@
         "icss-replace-symbols": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
-            "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==",
+            "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
             "dev": true
         },
         "icss-utils": {
@@ -12988,7 +13021,7 @@
         "ignore-by-default": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
-            "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
+            "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
         },
         "iink-js": {
             "version": "1.5.4",
@@ -13032,7 +13065,7 @@
         "image-size-stream": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
-            "integrity": "sha512-N505B5FSy2Xf5l/Haef+99TwfJqTu40hnU560+rC0Cm6cxtwVz2yRFh9WpOk1YEjfv3dI0PgVYAH0hmXQmjDcw==",
+            "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=",
             "requires": {
                 "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
                 "readable-stream": "^1.0.33",
@@ -13046,12 +13079,12 @@
                 "isarray": {
                     "version": "0.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-                    "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
+                    "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
                 },
                 "readable-stream": {
                     "version": "1.1.14",
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-                    "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
+                    "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
                     "requires": {
                         "core-util-is": "~1.0.0",
                         "inherits": "~2.0.1",
@@ -13062,14 +13095,14 @@
                 "string_decoder": {
                     "version": "0.10.31",
                     "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
-                    "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
+                    "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
                 }
             }
         },
         "immediate": {
             "version": "3.0.6",
             "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
-            "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+            "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
         },
         "import-fresh": {
             "version": "3.3.0",
@@ -13097,7 +13130,7 @@
         "imurmurhash": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-            "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
+            "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
         },
         "in-publish": {
             "version": "2.0.1",
@@ -13115,12 +13148,12 @@
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
+            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
         },
         "inflight": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-            "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+            "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
             "requires": {
                 "once": "^1.3.0",
                 "wrappy": "1"
@@ -13137,7 +13170,7 @@
                 "camelcase": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw=="
+                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
                 }
             }
         },
@@ -13491,7 +13524,7 @@
         "is-accessor-descriptor": {
             "version": "0.1.6",
             "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-            "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+            "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -13504,7 +13537,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -13533,7 +13566,7 @@
         "is-arrayish": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-            "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+            "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
         },
         "is-bigint": {
             "version": "1.0.4",
@@ -13589,7 +13622,7 @@
         "is-data-descriptor": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-            "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+            "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -13602,7 +13635,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -13637,7 +13670,7 @@
         "is-directory": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
-            "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw=="
+            "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
         },
         "is-expression": {
             "version": "3.0.0",
@@ -13663,7 +13696,7 @@
         "is-extglob": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-            "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
+            "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
         },
         "is-finite": {
             "version": "1.1.0",
@@ -13673,7 +13706,7 @@
         "is-fullwidth-code-point": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-            "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="
+            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
         },
         "is-generator-function": {
             "version": "1.0.10",
@@ -13694,7 +13727,7 @@
         "is-in-browser": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
-            "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g=="
+            "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
         },
         "is-installed-globally": {
             "version": "0.1.0",
@@ -13724,7 +13757,7 @@
         "is-number": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-            "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+            "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -13737,7 +13770,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -13875,7 +13908,7 @@
         "is-typedarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-            "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
+            "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
         },
         "is-url": {
             "version": "1.2.4",
@@ -13930,17 +13963,17 @@
         "isarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-            "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
         },
         "isexe": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-            "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
         },
         "isobject": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-            "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
+            "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
         },
         "isomorphic-fetch": {
             "version": "2.2.1",
@@ -13965,7 +13998,7 @@
         "isstream": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-            "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
+            "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
         },
         "its-set": {
             "version": "1.2.3",
@@ -14047,7 +14080,7 @@
         "jsbn": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-            "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
+            "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
         },
         "jsdom": {
             "version": "15.2.1",
@@ -14173,7 +14206,7 @@
         "json-css": {
             "version": "1.5.6",
             "resolved": "https://registry.npmjs.org/json-css/-/json-css-1.5.6.tgz",
-            "integrity": "sha512-B/0T0OxZH9tSb93tXV6VOYtXqrPz/Vgz2QrCT/4NXen8HGElYkYr9V+8IrSVTMj/ftxa8cG1kcu7f3iAMlaFlQ=="
+            "integrity": "sha1-65ZPg0ouTqobwvaY/12wB6JsfAA="
         },
         "json-parse-better-errors": {
             "version": "1.0.2",
@@ -14204,7 +14237,7 @@
         "json-stringify-safe": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-            "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
+            "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
         },
         "json5": {
             "version": "1.0.2",
@@ -14456,7 +14489,7 @@
         "klaw": {
             "version": "1.3.1",
             "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
-            "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==",
+            "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
             "requires": {
                 "graceful-fs": "^4.1.9"
             }
@@ -14601,22 +14634,22 @@
         "lodash._getnative": {
             "version": "3.9.1",
             "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
-            "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA=="
+            "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
         },
         "lodash.chunk": {
             "version": "4.2.0",
             "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
-            "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w=="
+            "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw="
         },
         "lodash.curry": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
-            "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA=="
+            "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA="
         },
         "lodash.debounce": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-3.1.1.tgz",
-            "integrity": "sha512-lcmJwMpdPAtChA4hfiwxTtgFeNAaow701wWUgVUqeD0XJF7vMXIN+bu/2FJSGxT0NUbZy9g9VFrlOFfPjl+0Ew==",
+            "integrity": "sha1-gSIRw3ipTMKdWqTjNGzwv846ffU=",
             "requires": {
                 "lodash._getnative": "^3.0.0"
             }
@@ -14624,42 +14657,42 @@
         "lodash.defaults": {
             "version": "4.2.0",
             "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
-            "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
+            "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
         },
         "lodash.difference": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
-            "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="
+            "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
         },
         "lodash.flatten": {
             "version": "4.4.0",
             "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
-            "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
+            "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
         },
         "lodash.flow": {
             "version": "3.5.0",
             "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz",
-            "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw=="
+            "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o="
         },
         "lodash.get": {
             "version": "4.4.2",
             "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
-            "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
+            "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
         },
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
             "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
-            "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+            "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
         },
         "lodash.memoize": {
             "version": "4.1.2",
             "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-            "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
+            "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
         },
         "lodash.merge": {
             "version": "4.6.2",
@@ -14680,12 +14713,12 @@
         "lodash.throttle": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
-            "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
+            "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
         },
         "lodash.union": {
             "version": "4.6.0",
             "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
-            "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="
+            "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
         },
         "log-symbols": {
             "version": "2.2.0",
@@ -14841,7 +14874,7 @@
         "map-cache": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-            "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg=="
+            "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
         },
         "map-obj": {
             "version": "1.0.1",
@@ -14851,7 +14884,7 @@
         "map-visit": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-            "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
+            "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
             "requires": {
                 "object-visit": "^1.0.0"
             }
@@ -14901,7 +14934,7 @@
         "math-codegen": {
             "version": "0.3.5",
             "resolved": "https://registry.npmjs.org/math-codegen/-/math-codegen-0.3.5.tgz",
-            "integrity": "sha512-SsFYMv33FxMKYxI1PBiaZT+8AeDITK+k/PKhbHNlOPHIz5FIPF4wy78yWqanN6luXdsXENUZgCIC6xH6bfUq1g==",
+            "integrity": "sha1-R5nuRnfe0Ud2bQA8ykt4ee3UDMo=",
             "requires": {
                 "extend": "^3.0.0",
                 "mr-parser": "^0.2.1"
@@ -14910,7 +14943,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
+            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -14918,7 +14951,7 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
+                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
                 }
             }
         },
@@ -15088,7 +15121,7 @@
         "media-typer": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-            "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
+            "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
         },
         "memfs": {
             "version": "3.4.13",
@@ -15139,7 +15172,7 @@
         "memorystream": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
-            "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="
+            "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI="
         },
         "meow": {
             "version": "3.7.0",
@@ -15169,7 +15202,7 @@
         "merge-descriptors": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-            "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+            "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
         },
         "merge-stream": {
             "version": "2.0.0",
@@ -15180,7 +15213,7 @@
         "methods": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-            "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
+            "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
         },
         "microevent.ts": {
             "version": "0.1.1",
@@ -15744,7 +15777,7 @@
                 "he": {
                     "version": "1.1.1",
                     "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
-                    "integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA==",
+                    "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
                     "dev": true
                 },
                 "minimatch": {
@@ -15774,7 +15807,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 },
                 "supports-color": {
@@ -15959,14 +15992,14 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
         "mr-parser": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/mr-parser/-/mr-parser-0.2.1.tgz",
-            "integrity": "sha512-hug+mpbSSKnH13rFqy3zm+XiG+QTStiDAgMTHK355TIstQE0qBkBtSJsa5YHP94AuarVX9b/4dcebdTRZ9YiEw=="
+            "integrity": "sha1-hhi5ukF+KOn0OaQcaVtVTq/u2Sc="
         },
         "mri": {
             "version": "1.2.0",
@@ -16036,7 +16069,7 @@
         "native-or-bluebird": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz",
-            "integrity": "sha512-0SH8UubxDfe382eYiwmd12qxAbiWGzlGZv6CkMA+DPojWa/Y0oH4hE0lRtFfFgJmPQFyKXeB8XxPbZz6TvvKaQ=="
+            "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck="
         },
         "natural-compare": {
             "version": "1.4.0",
@@ -16063,7 +16096,7 @@
         "nextafter": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz",
-            "integrity": "sha512-7PO+A89Tll2rSEfyrjtqO0MaI37+nnxBdnQcPypfbEYYuGaJxWGCqaOwQX4a3GHNTS08l1kazuiLEWZniZjMUQ==",
+            "integrity": "sha1-t9d7U1MQ4+CX5gJauwqQNHfsGjo=",
             "requires": {
                 "double-bits": "^1.1.0"
             }
@@ -16094,7 +16127,7 @@
         "node-ensure": {
             "version": "0.0.0",
             "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
-            "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw=="
+            "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc="
         },
         "node-environment-flags": {
             "version": "1.0.5",
@@ -20198,7 +20231,7 @@
         "oauth": {
             "version": "0.9.15",
             "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
-            "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
+            "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
         },
         "oauth-sign": {
             "version": "0.9.0",
@@ -20208,12 +20241,12 @@
         "object-assign": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-            "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
+            "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
         },
         "object-copy": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-            "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
+            "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
             "requires": {
                 "copy-descriptor": "^0.1.0",
                 "define-property": "^0.2.5",
@@ -20223,7 +20256,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -20236,7 +20269,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -20306,7 +20339,7 @@
         "object-visit": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-            "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
+            "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
             "requires": {
                 "isobject": "^3.0.0"
             }
@@ -20368,7 +20401,7 @@
         "object.pick": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-            "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+            "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
             "requires": {
                 "isobject": "^3.0.1"
             }
@@ -20406,7 +20439,7 @@
         "once": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-            "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
             "requires": {
                 "wrappy": "1"
             }
@@ -20507,7 +20540,7 @@
         "os-tmpdir": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-            "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="
+            "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
         },
         "osenv": {
             "version": "0.1.5",
@@ -20526,7 +20559,7 @@
         "p-debounce": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-1.0.0.tgz",
-            "integrity": "sha512-ttOxn4Yt0hzIsLLqKi/Ry9QRxW+UQKdoWHz7g99Ci57zPkqUU3kbWKAeHuv+HfRLe109acYLUY6kuVCOOqnt4g=="
+            "integrity": "sha1-y38svu/YegnrqGHhErZ1J+Yh4v0="
         },
         "p-finally": {
             "version": "1.0.0",
@@ -20703,7 +20736,7 @@
         "pascalcase": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-            "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="
+            "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
         },
         "passport": {
             "version": "0.4.1",
@@ -20725,7 +20758,7 @@
         "passport-local": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
-            "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
+            "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
             "requires": {
                 "passport-strategy": "1.x.x"
             }
@@ -20745,7 +20778,7 @@
         "passport-strategy": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
-            "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA=="
+            "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
         },
         "path-browserify": {
             "version": "1.0.1",
@@ -20760,12 +20793,12 @@
         "path-exists": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-            "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="
+            "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
         },
         "path-is-absolute": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
+            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
         },
         "path-is-inside": {
             "version": "1.0.2",
@@ -20775,7 +20808,7 @@
         "path-key": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-            "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="
+            "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
         },
         "path-parse": {
             "version": "1.0.7",
@@ -20785,7 +20818,7 @@
         "path-to-regexp": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-            "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+            "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
         },
         "path-type": {
             "version": "4.0.0",
@@ -20800,7 +20833,7 @@
         "pause": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
-            "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
+            "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
         },
         "pdf-parse": {
             "version": "1.1.1",
@@ -20848,7 +20881,7 @@
         "pend": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
-            "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
+            "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
         },
         "perfect-scrollbar": {
             "version": "1.5.5",
@@ -20858,7 +20891,7 @@
         "performance-now": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-            "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
+            "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
         },
         "picocolors": {
             "version": "1.0.0",
@@ -21004,7 +21037,7 @@
         "posix-character-classes": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-            "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg=="
+            "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
         },
         "postcss": {
             "version": "7.0.39",
@@ -21257,7 +21290,7 @@
         "process": {
             "version": "0.11.10",
             "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
-            "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
+            "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
         },
         "process-nextick-args": {
             "version": "2.0.1",
@@ -21331,7 +21364,7 @@
         "prosemirror-find-replace": {
             "version": "0.9.0",
             "resolved": "https://registry.npmjs.org/prosemirror-find-replace/-/prosemirror-find-replace-0.9.0.tgz",
-            "integrity": "sha512-LfhQ/Zr0PkkJpCsr9vTJ5ZPYh49mSVVG+hHJ6djT+chlCW+t2ilSxBpBG+2IeE/I5nlbcvuLLAbxeI1g3pTCpA=="
+            "integrity": "sha1-QgsENNF5xdBJD44hSNhVGpVJY4I="
         },
         "prosemirror-history": {
             "version": "1.3.0",
@@ -21436,7 +21469,7 @@
         "prr": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-            "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+            "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
             "dev": true
         },
         "pseudomap": {
@@ -21674,12 +21707,12 @@
         "pure-color": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz",
-            "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA=="
+            "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4="
         },
         "q": {
             "version": "1.5.1",
             "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
-            "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="
+            "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
         },
         "qs": {
             "version": "6.5.3",
@@ -21727,7 +21760,7 @@
         "random-bytes": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
-            "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="
+            "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
         },
         "randombytes": {
             "version": "2.1.0",
@@ -21830,7 +21863,7 @@
         "react-base16-styling": {
             "version": "0.5.3",
             "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz",
-            "integrity": "sha512-EPuchwVvYPSFFIjGpH0k6wM0HQsmJ0vCk7BSl5ryxMVFIWW4hX4Kksu4PNtxfgOxDebTLkJQ8iC7zwAql0eusg==",
+            "integrity": "sha1-OFjyTpxN2MvT9wLz901YHKKRcmk=",
             "requires": {
                 "base16": "^1.0.0",
                 "lodash.curry": "^4.0.1",
@@ -21904,7 +21937,7 @@
         "react-dock": {
             "version": "0.2.4",
             "resolved": "https://registry.npmjs.org/react-dock/-/react-dock-0.2.4.tgz",
-            "integrity": "sha512-ywUJPC/TIM9PO700skka0fH4aqbrH8RojUXejZFvjtqlc5KZ+xjHqFdo4A3j+dp+0NLFZ3Nai4xzcf3FUJ9BsQ==",
+            "integrity": "sha1-5yfcdVCztzEWY13LnA4E0Lev4Xw=",
             "requires": {
                 "lodash.debounce": "^3.1.1",
                 "prop-types": "^15.5.8"
@@ -22267,7 +22300,7 @@
         "react-themeable": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
-            "integrity": "sha512-kl5tQ8K+r9IdQXZd8WLa+xxYN04lLnJXRVhHfdgwsUJr/SlKJxIejoc9z9obEkx1mdqbTw1ry43fxEUwyD9u7w==",
+            "integrity": "sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4=",
             "requires": {
                 "object-assign": "^3.0.0"
             },
@@ -22275,7 +22308,7 @@
                 "object-assign": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
-                    "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ=="
+                    "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
                 }
             }
         },
@@ -22424,7 +22457,7 @@
         "readline": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
-            "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg=="
+            "integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw="
         },
         "recharts": {
             "version": "2.5.0",
@@ -22453,7 +22486,7 @@
         "rechoir": {
             "version": "0.6.2",
             "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
-            "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
+            "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
             "requires": {
                 "resolve": "^1.1.6"
             }
@@ -22621,7 +22654,7 @@
         "relateurl": {
             "version": "0.2.7",
             "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
-            "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="
+            "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
         },
         "remark-gfm": {
             "version": "3.0.1",
@@ -22744,7 +22777,7 @@
         "repeat-string": {
             "version": "1.6.1",
             "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-            "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="
+            "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
         },
         "repeating": {
             "version": "2.0.1",
@@ -22853,7 +22886,7 @@
         "require-directory": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
+            "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
         },
         "require-from-string": {
             "version": "2.0.2",
@@ -22868,7 +22901,7 @@
         "require-package-name": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz",
-            "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q=="
+            "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk="
         },
         "require_optional": {
             "version": "1.0.1",
@@ -22889,7 +22922,7 @@
         "requires-port": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
-            "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
+            "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
         },
         "resize-observer-polyfill": {
             "version": "1.5.1",
@@ -22934,7 +22967,7 @@
         "resolve-url": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-            "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="
+            "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
         },
         "responselike": {
             "version": "3.0.0",
@@ -23080,7 +23113,7 @@
         "safe-regex": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-            "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+            "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
             "requires": {
                 "ret": "~0.1.10"
             }
@@ -23178,7 +23211,7 @@
         "scss-loader": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/scss-loader/-/scss-loader-0.0.1.tgz",
-            "integrity": "sha512-SbT/smRJjkvvdHSEdAYAplosVkrtaSwwgUlnQCOuDS5sOKNjrS/eYCMvKeV6+YxK5cCOCsOJZd3vltrXatFp+g==",
+            "integrity": "sha1-6uAXueDzjBKlMtslwiC5Avs05nE=",
             "dev": true
         },
         "scss-tokenizer": {
@@ -23208,17 +23241,17 @@
         "section-iterator": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz",
-            "integrity": "sha512-xvTNwcbeDayXotnV32zLb3duQsP+4XosHpb/F+tu6VzEZFmIjzPdNk6/O+QOOx5XTh08KL2ufdXeCO33p380pQ=="
+            "integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio="
         },
         "select": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-            "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+            "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
         },
         "select-hose": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
-            "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
+            "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
             "dev": true
         },
         "selfsigned": {
@@ -23238,7 +23271,7 @@
         "semver-compare": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
-            "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="
+            "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
         },
         "semver-diff": {
             "version": "2.1.0",
@@ -23307,7 +23340,7 @@
         "serve-index": {
             "version": "1.9.1",
             "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
-            "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+            "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
             "dev": true,
             "requires": {
                 "accepts": "~1.3.4",
@@ -23337,7 +23370,7 @@
                 "http-errors": {
                     "version": "1.6.3",
                     "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
-                    "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+                    "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
                     "dev": true,
                     "requires": {
                         "depd": "~1.1.2",
@@ -23349,13 +23382,13 @@
                 "inherits": {
                     "version": "2.0.3",
                     "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-                    "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+                    "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
                     "dev": true
                 },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 },
                 "setprototypeof": {
@@ -23367,7 +23400,7 @@
                 "statuses": {
                     "version": "1.5.0",
                     "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-                    "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+                    "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
                     "dev": true
                 }
             }
@@ -23386,7 +23419,7 @@
         "set-blocking": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-            "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+            "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
         },
         "set-value": {
             "version": "2.0.1",
@@ -23402,7 +23435,7 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
@@ -23420,7 +23453,7 @@
         "setimmediate": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
-            "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+            "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
         },
         "setprototypeof": {
             "version": "1.2.0",
@@ -23595,7 +23628,7 @@
         "shebang-command": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-            "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
+            "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
             "requires": {
                 "shebang-regex": "^1.0.0"
             }
@@ -23603,7 +23636,7 @@
         "shebang-regex": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-            "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="
+            "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
         },
         "shelljs": {
             "version": "0.8.5",
@@ -23638,7 +23671,7 @@
         "simple-assign": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz",
-            "integrity": "sha512-otdSSQzuVsmDoe5MnSm4ZgHd5sl0ak6A1CTjW1R/DUHQ8xoZuU1NUzf9x6n9Dvp3nxpvW51WNMQ/7rQ9432xDg=="
+            "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o="
         },
         "simple-concat": {
             "version": "1.0.1",
@@ -23658,7 +23691,7 @@
         "simple-swizzle": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
-            "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+            "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
             "requires": {
                 "is-arrayish": "^0.3.1"
             },
@@ -23690,7 +23723,7 @@
         "sliced": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
-            "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA=="
+            "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
         },
         "snapdragon": {
             "version": "0.8.2",
@@ -23718,7 +23751,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -23726,7 +23759,7 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
@@ -23734,7 +23767,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
@@ -23751,7 +23784,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -23800,7 +23833,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -23969,7 +24002,7 @@
         "source-map": {
             "version": "0.5.7",
             "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-            "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+            "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
         },
         "source-map-js": {
             "version": "1.0.2",
@@ -24017,7 +24050,7 @@
         "sparse-bitfield": {
             "version": "3.0.3",
             "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
-            "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+            "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
             "optional": true,
             "requires": {
                 "memory-pager": "^1.0.2"
@@ -24133,7 +24166,7 @@
         "sprintf-js": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-            "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
+            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
         },
         "sshpk": {
             "version": "1.17.0",
@@ -24163,12 +24196,12 @@
         "standard-error": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/standard-error/-/standard-error-1.1.0.tgz",
-            "integrity": "sha512-4v7qzU7oLJfMI5EltUSHCaaOd65J6S4BqKRWgzMi4EYaE5fvNabPxmAPGdxpGXqrcWjhDGI/H09CIdEuUOUeXg=="
+            "integrity": "sha1-I+UWj6HAggGJ5YEnAaeQWFENDTQ="
         },
         "standard-http-error": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/standard-http-error/-/standard-http-error-2.0.1.tgz",
-            "integrity": "sha512-DX/xPIoyXQTuY6BMZK4Utyi4l3A4vFoafsfqrU6/dO4Oe/59c7PyqPd2IQj9m+ZieDg2K3RL9xOYJsabcD9IUA==",
+            "integrity": "sha1-+K6RcuPO+cs40ucIShkl9Xp8NL0=",
             "requires": {
                 "standard-error": ">= 1.1.0 < 2"
             }
@@ -24176,7 +24209,7 @@
         "static-extend": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-            "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
+            "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
             "requires": {
                 "define-property": "^0.2.5",
                 "object-copy": "^0.1.0"
@@ -24185,7 +24218,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -24224,7 +24257,7 @@
         "stealthy-require": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
-            "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g=="
+            "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
         },
         "stop-iteration-iterator": {
             "version": "1.0.0",
@@ -24257,7 +24290,7 @@
         "stream-parser": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz",
-            "integrity": "sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==",
+            "integrity": "sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M=",
             "requires": {
                 "debug": "2"
             },
@@ -24273,7 +24306,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
@@ -24286,7 +24319,7 @@
         "strict-uri-encode": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
-            "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
+            "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
         },
         "string-width": {
             "version": "2.1.1",
@@ -24376,7 +24409,7 @@
         "strip-ansi": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-            "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+            "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
             "requires": {
                 "ansi-regex": "^3.0.0"
             }
@@ -24412,7 +24445,7 @@
         "strip-json-comments": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-            "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
+            "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
         },
         "style-loader": {
             "version": "0.23.1",
@@ -24614,7 +24647,7 @@
         "temp-dir": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz",
-            "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ=="
+            "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0="
         },
         "tempy": {
             "version": "0.2.1",
@@ -24696,7 +24729,7 @@
         "through": {
             "version": "2.3.8",
             "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-            "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
+            "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
         },
         "through2": {
             "version": "2.0.5",
@@ -24773,7 +24806,7 @@
         "to-array": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
-            "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
+            "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
         },
         "to-fast-properties": {
             "version": "2.0.0",
@@ -24783,7 +24816,7 @@
         "to-object-path": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-            "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
+            "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -24796,7 +24829,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -24817,7 +24850,7 @@
         "to-regex-range": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-            "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+            "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
             "requires": {
                 "is-number": "^3.0.0",
                 "repeat-string": "^1.6.1"
@@ -24844,7 +24877,7 @@
                 "nopt": {
                     "version": "1.0.10",
                     "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
-                    "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
+                    "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
                     "requires": {
                         "abbrev": "1"
                     }
@@ -24865,7 +24898,7 @@
         "tr46": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-            "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+            "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
         },
         "translate-google-api": {
             "version": "1.0.4",
@@ -24893,7 +24926,7 @@
         "traverse-chain": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
-            "integrity": "sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg=="
+            "integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
         },
         "tree-kill": {
             "version": "1.2.2",
@@ -24927,7 +24960,7 @@
         "tryit": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
-            "integrity": "sha512-6C5h3CE+0qjGp+YKYTs74xR0k/Nw/ePtl/Lp6CCf44hqBQ66qnH1sDFR5mV/Gc48EsrHLB53lCFSffQCkka3kg=="
+            "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics="
         },
         "ts-loader": {
             "version": "5.4.5",
@@ -25271,7 +25304,7 @@
         "tunnel-agent": {
             "version": "0.6.0",
             "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-            "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+            "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
             "requires": {
                 "safe-buffer": "^5.0.1"
             }
@@ -25279,7 +25312,13 @@
         "tweetnacl": {
             "version": "0.14.5",
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-            "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
+            "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+        },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
         },
         "type-check": {
             "version": "0.4.0",
@@ -25328,7 +25367,7 @@
         "typedarray": {
             "version": "0.0.6",
             "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-            "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
+            "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
         },
         "typescript": {
             "version": "4.9.5",
@@ -25373,7 +25412,7 @@
                 "jsonfile": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
-                    "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+                    "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
                     "requires": {
                         "graceful-fs": "^4.1.6"
                     }
@@ -25509,7 +25548,7 @@
         "unicode-trie": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-0.3.1.tgz",
-            "integrity": "sha512-WgVuO0M2jDl7hVfbPgXv2LUrD81HM0bQj/bvLGiw6fJ4Zo8nNFnDrA0/hU2Te/wz6pjxCm5cxJwtLjo2eyV51Q==",
+            "integrity": "sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU=",
             "requires": {
                 "pako": "^0.2.5",
                 "tiny-inflate": "^1.0.0"
@@ -25518,7 +25557,7 @@
                 "pako": {
                     "version": "0.2.9",
                     "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
-                    "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
+                    "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
                 }
             }
         },
@@ -25568,7 +25607,7 @@
         "unique-string": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
-            "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==",
+            "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
             "requires": {
                 "crypto-random-string": "^1.0.0"
             }
@@ -25641,12 +25680,12 @@
         "unpipe": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-            "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
+            "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
         },
         "unset-value": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-            "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
+            "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
             "requires": {
                 "has-value": "^0.3.1",
                 "isobject": "^3.0.0"
@@ -25655,7 +25694,7 @@
                 "has-value": {
                     "version": "0.3.1",
                     "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-                    "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+                    "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
                     "requires": {
                         "get-value": "^2.0.3",
                         "has-values": "^0.1.4",
@@ -25665,7 +25704,7 @@
                         "isobject": {
                             "version": "2.1.0",
                             "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-                            "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+                            "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
                             "requires": {
                                 "isarray": "1.0.0"
                             }
@@ -25675,7 +25714,7 @@
                 "has-values": {
                     "version": "0.1.4",
                     "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-                    "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="
+                    "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
                 }
             }
         },
@@ -25741,7 +25780,7 @@
         "urix": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-            "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg=="
+            "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
         },
         "url": {
             "version": "0.11.0",
@@ -25833,12 +25872,12 @@
         "util-deprecate": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+            "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
         },
         "utila": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
-            "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA=="
+            "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw="
         },
         "utility-types": {
             "version": "3.10.0",
@@ -25848,7 +25887,7 @@
         "utils-merge": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
-            "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
+            "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
         },
         "uuid": {
             "version": "3.4.0",
@@ -25858,7 +25897,7 @@
         "uuid-js": {
             "version": "0.7.5",
             "resolved": "https://registry.npmjs.org/uuid-js/-/uuid-js-0.7.5.tgz",
-            "integrity": "sha512-lJFducSMfVDO3E1wBe/zflgU25JbpX9KfF+g0k6OxIt9xeybdZd27n75vPg+4cLN55UKGjJ46w3K3q3l+8KgkQ=="
+            "integrity": "sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA="
         },
         "uvu": {
             "version": "0.5.6",
@@ -25893,7 +25932,7 @@
         "valid-url": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
-            "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA=="
+            "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA="
         },
         "validate-npm-package-license": {
             "version": "3.0.4",
@@ -25912,12 +25951,12 @@
         "vary": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
-            "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
+            "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
         },
         "verror": {
             "version": "1.10.0",
             "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-            "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+            "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
             "requires": {
                 "assert-plus": "^1.0.0",
                 "core-util-is": "1.0.2",
@@ -26089,7 +26128,7 @@
         "warning": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
-            "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==",
+            "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
             "requires": {
                 "loose-envify": "^1.0.0"
             }
@@ -26121,7 +26160,7 @@
         "web-request": {
             "version": "1.0.7",
             "resolved": "https://registry.npmjs.org/web-request/-/web-request-1.0.7.tgz",
-            "integrity": "sha512-mVySwo0f8FFw77ZCEEjZ93g7SqAvWREj15FefhJgPkkylu7b185N/u3Oa92sNpMdhjcErGB7oGpOkAAIvflSjw==",
+            "integrity": "sha1-twxCs81FV3noLbaIYlOySR8r1Wk=",
             "requires": {
                 "request": "^2.69.0"
             }
@@ -26134,7 +26173,7 @@
         "webidl-conversions": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-            "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+            "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
         },
         "webpack": {
             "version": "5.75.0",
@@ -26420,7 +26459,7 @@
                 "resolve-cwd": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-                    "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+                    "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
                     "dev": true,
                     "requires": {
                         "resolve-from": "^3.0.0"
@@ -26429,7 +26468,7 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
                     "dev": true
                 },
                 "semver": {
@@ -26611,7 +26650,7 @@
         "whatwg-url": {
             "version": "5.0.0",
             "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-            "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+            "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
             "requires": {
                 "tr46": "~0.0.3",
                 "webidl-conversions": "^3.0.0"
@@ -26620,7 +26659,7 @@
         "when": {
             "version": "3.7.8",
             "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz",
-            "integrity": "sha512-5cZ7mecD3eYcMiCH4wtRPA5iFJZ50BJYDfckI5RRpQiktMiYTcn0ccLTZOvcbBume+1304fQztxeNzNS9Gvrnw=="
+            "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I="
         },
         "which": {
             "version": "1.3.1",
@@ -26657,7 +26696,7 @@
         "which-module": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-            "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q=="
+            "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
         },
         "which-pm-runs": {
             "version": "1.1.0",
@@ -26806,7 +26845,7 @@
         "wrappy": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-            "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+            "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
         },
         "write": {
             "version": "1.0.3",
@@ -26877,7 +26916,7 @@
         "xoauth2": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/xoauth2/-/xoauth2-1.2.0.tgz",
-            "integrity": "sha512-hKuNbkj3q/ifCcfWnW6KURP+6ExSuLdLG007gasNhMEMKlLaejNkIA6eu5Ol1xPP0/kzTuA87XHDaAcUw5k73Q=="
+            "integrity": "sha1-8u76wRRyyXHqO8RuVU60sSMhRuU="
         },
         "xregexp": {
             "version": "4.4.1",
@@ -26972,7 +27011,7 @@
         "yauzl": {
             "version": "2.10.0",
             "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
-            "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+            "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
             "requires": {
                 "buffer-crc32": "~0.2.3",
                 "fd-slicer": "~1.1.0"
@@ -26981,7 +27020,7 @@
         "yeast": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
-            "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
+            "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
         },
         "yn": {
             "version": "3.1.1",
diff --git a/package.json b/package.json
index e301268dd..c7c6bb4f0 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.76",
+        "browndash-components": "0.0.78",
         "browser-assert": "^1.2.1",
         "bson": "^4.6.1",
         "canvas": "^2.9.3",
diff --git a/src/Utils.ts b/src/Utils.ts
index 2e06b5631..e03632c8b 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -6,6 +6,7 @@ import { Socket } from 'socket.io';
 import { Colors } from './client/views/global/globalEnums';
 import { Message } from './server/Message';
 import Color = require('color');
+import { DocumentType } from './client/documents/DocumentTypes';
 
 export namespace Utils {
     export let CLICK_TIME = 300;
@@ -15,6 +16,21 @@ export namespace Utils {
         return Date.now() - downTime < Utils.CLICK_TIME && Math.abs(x - downX) < Utils.DRAG_THRESHOLD && Math.abs(y - downY) < Utils.DRAG_THRESHOLD;
     }
 
+    export function cleanDocumentType(type: DocumentType)  {
+        switch(type) {
+            case DocumentType.IMG:
+                return "Image"
+            case DocumentType.AUDIO:
+                return "Audio"
+            case DocumentType.COL:
+                return "Collection"
+            case DocumentType.RTF:
+                return "Text"
+            default: 
+                return type.charAt(0).toUpperCase() + type.slice(1)
+        }
+    }
+
     export function readUploadedFileAsText(inputFile: File) {
         const temporaryFileReader = new FileReader();
 
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 05401e2cd..b11e694ff 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -12,6 +12,7 @@
     }
 
     .sharing-contents {
+        padding: 10px;
         display: flex;
         flex-direction: column;
 
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 8e5ae7bc0..33c9992d0 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -551,7 +551,12 @@ export class SharingManager extends React.Component<{}> {
         return (
             <div className="sharing-interface">
                 {GroupManager.Instance?.currentGroup ? <GroupMemberView group={GroupManager.Instance.currentGroup} onCloseButtonClick={action(() => (GroupManager.Instance.currentGroup = undefined))} /> : null}
-                <div className="sharing-contents">
+                <div className="sharing-contents"
+                    style={{
+                        background: StrCast(Doc.UserDoc().userBackgroundColor),
+                        color: StrCast(Doc.UserDoc().userColor)
+                    }}
+                >
                     <p className="share-title">
                         <div className="share-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties/sharing-and-permissions/', '_blank')}>
                             <FontAwesomeIcon icon={'question-circle'} size={'sm'} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties/sharing-and-permissions/', '_blank')} />
@@ -652,6 +657,13 @@ export class SharingManager extends React.Component<{}> {
     }
 
     render() {
-        return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
+        return <MainViewModal 
+            contents={this.sharingInterface} 
+            isDisplayed={this.isOpen} 
+            interactive={true} 
+            dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} 
+            overlayDisplayedOpacity={this.overlayOpacity} 
+            closeOnExternalClick={this.close} 
+        />;
     }
 }
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index e574d68de..cf746bbb3 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -180,7 +180,7 @@ export class DashboardView extends React.Component {
                             fillWidth
                         />
                         <Button 
-                            text={'Shared Dashboards' + '(' + this.getDashboards(DashboardGroup.SharedDashboards).length + ')'}
+                            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)) ? 'lightgreen' : color}
                             align={'flex-start'}
@@ -226,7 +226,7 @@ export class DashboardView extends React.Component {
                                                 e.stopPropagation();
                                                 this.onContextMenu(dashboard, e);
                                             }}>
-                                            <Button size={Size.SMALL} color={color} icon={<FontAwesomeIcon color={color} size="lg" icon="bars" />} />
+                                            <Button size={Size.SMALL} color={color} icon={<FontAwesomeIcon color={color} icon="bars" />} />
                                         </div>
                                     </div>
                                     <div className={`background`} style={{
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 3148d9ddd..b0f9d5a7e 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -301,32 +301,6 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             // </Tooltip>
         );
     }
-    @computed
-    get perspectiveButton() {
-        return !this.selectedDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
-                <div>
-                    <div className="propertiesButtons-linkFlyout">
-                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
-                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
-                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                            </div>
-                        </Flyout>
-                    </div>
-                    <div className="propertiesButtons-title"> Perspective </div>
-                </div>
-            </Tooltip>
-        );
-    }
-
-    @undoBatch
-    handlePerspectiveChange = (e: any) => {
-        this.selectedDoc && (this.selectedDoc._type_collection = e.target.value);
-        SelectionManager.Views()
-            .filter(dv => dv.docView)
-            .map(dv => dv.docView!)
-            .forEach(docView => (docView.layoutDoc._type_collection = e.target.value));
-    };
 
     @undoBatch
     @action
@@ -406,26 +380,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             </div>
         );
     }
-    @computed
-    get onPerspectiveFlyout() {
-        const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear];
 
-        const makeLabel = (value: string, label: string) => (
-            <div className="radio" key={label}>
-                <label>
-                    <input type="radio" value={value} checked={(this.selectedDoc?._type_collection ?? 'invalid') === value} onChange={this.handlePerspectiveChange} />
-                    {label}
-                </label>
-            </div>
-        );
-        return (
-            <form>
-                {Object.values(CollectionViewType)
-                    .filter(type => !excludedViewTypes.includes(type))
-                    .map(type => makeLabel(type, type))}
-            </form>
-        );
-    }
 
     render() {
         const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)];
@@ -466,7 +421,6 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.snapButton, { display: !isCollection ? 'none' : '' })}
                 {toggle(this.clustersButton, { display: !isFreeForm ? 'none' : '' })}
                 {toggle(this.panButton, { display: !isFreeForm ? 'none' : '' })}
-                {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? 'none' : '' })}
             </div>
         );
     }
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index b195afbd8..6dbc11d29 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -23,7 +23,7 @@
 
     .propertiesView-name,
     .propertiesView-type {
-        padding: 0px 10px;
+        padding: 5px 10px;
     }
 
 
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index a61a14536..ae6f6afe2 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -7,7 +7,7 @@ import { concat, intersection } from 'lodash';
 import { Lambda, action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
 import { Doc, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
 import { AclAdmin, DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
@@ -32,7 +32,7 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
-import { Button, Colors, EditableText, NumberDropdown, Size, Slider, Type } from 'browndash-components';
+import { Button, Colors, EditableText, NumberDropdown, Size, Slider, Type, NumberInput } from 'browndash-components';
 import { PropertiesSection } from './PropertiesSection';
 import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
 import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
@@ -548,11 +548,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get type() {
-        const type = StrCast(this.selectedDoc?.type);
+        const type = Utils.cleanDocumentType(StrCast(this.selectedDoc?.type) as DocumentType);
         return (
             <Button
                 formLabel={"Type"}
                 text={type}
+                type={Type.SEC}
                 color={this.color}
                 align='flex-start'
                 fillWidth
@@ -992,35 +993,68 @@ 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}
+            />
+            <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}
-                <Slider 
-                    formLabel={'Height'} 
-                    multithumb={false}
-                    color={this.color}
-                    size={Size.XSMALL} 
-                    min={0} 
-                    max={1000} 
-                    number={Number(this.shapeHgt)}
-                    setNumber={undoable((val: string) => !isNaN(Number(val)) && (this.shapeHgt = val), 'set height')}
-                    fillWidth
-                />
-                <Slider 
-                    formLabel={'Width'} 
-                    multithumb={false}
-                    color={this.color}
-                    size={Size.XSMALL} 
-                    min={0} 
-                    max={1000} 
-                    number={Number(this.shapeWid)}
-                    setNumber={undoable((val: string) => !isNaN(Number(val)) && (this.shapeWid = val), 'set width')}
-                    fillWidth
-                />
-
-                {this.hgtInput}
-                {this.XpsInput}
+                {this.getNumber(
+                    "Height",
+                    " px",
+                    0,
+                    1000,
+                    Number(this.shapeHgt),
+                    undoable((val: string) => !isNaN(Number(val)) && (this.shapeHgt = val), 'set height')
+                )}
+                {this.getNumber(
+                    "Width",
+                    " px",
+                    0,
+                    1000,
+                    Number(this.shapeWid),
+                    undoable((val: string) => !isNaN(Number(val)) && (this.shapeWid = val), 'set width')
+                )}
+                {this.getNumber(
+                    "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",
+                    -2000,
+                    2000,
+                    Number(this.shapeYps),
+                    undoable((val: string) => !isNaN(Number(val)) && (this.shapeYps = val), 'set y coord')
+                )}
             </div>
         );
     }
diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx
index 28f1b8bd9..aaca7110e 100644
--- a/src/client/views/UndoStack.tsx
+++ b/src/client/views/UndoStack.tsx
@@ -25,7 +25,11 @@ export class UndoStack extends React.Component<UndoStackProps> {
                     placement={`top-start`}
                     type={Type.TERT}
                     popup={
-                    <div className="undoStack-commandsContainer" ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })} style={{ background: UndoManager.batchCounter.get() ? 'yellow' : undefined }}>
+                    <div className="undoStack-commandsContainer" ref={r => r?.scroll({ behavior: 'auto', top: r?.scrollHeight + 20 })} 
+                        style={{ 
+                            background: UndoManager.batchCounter.get() ? 'yellow' : StrCast(Doc.UserDoc().userBackgroundColor),
+                            color: StrCast(Doc.UserDoc().userColor)
+                        }}>
                         {UndoManager.undoStackNames.map((name, i) => (
                             <div className="undoStack-resultContainer" key={i}>
                                 <div className="undoStack-commandString">{name.replace(/[^\.]*\./, '')}</div>
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 2958fa737..e98faf2a0 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -20,6 +20,7 @@ import { OpenWhere } from '../DocumentView';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
 import './FontIconBox.scss';
 import { SelectedDocView } from '../../selectedDoc';
+import { Utils } from '../../../../Utils';
 
 export enum ButtonType {
     TextButton = 'textBtn',
@@ -214,9 +215,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                         console.log("doc selected", selected.title);
                         dropdown = false;
                         if (selectedDocs.length > 1) {
-                            text = selectedDocs.length + " documents selected"
+                            text = selectedDocs.length + " selected"
                         } else {
-                            text = selected.type === DocumentType.RTF ? 'Text' : StrCast(selected.type);
+                            text = Utils.cleanDocumentType(StrCast(selected.type) as DocumentType);
                             icon = Doc.toIcon(selected);
                         }
                         return <Popup
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 64ea4f52c..07b2afd91 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -15,7 +15,7 @@ import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup';
 import { LightboxView } from '../LightboxView';
 import { EditorView } from 'prosemirror-view';
 import './AnchorMenu.scss';
-import { ColorPicker, Group, IconButton, Popup, Size, Type } from 'browndash-components';
+import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
 import { StrCast } from '../../../fields/Types';
 
 @observer
@@ -371,29 +371,33 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                 </>
             ) : (
                 <>
-                    <Tooltip key="trash" title={<div className="dash-tooltip">Remove Link Anchor</div>}>
-                        <button className="antimodeMenu-button" style={{ display: this.Delete === returnFalse ? 'none' : undefined }} onPointerDown={this.Delete}>
-                            <FontAwesomeIcon icon="trash-alt" size="lg" />
-                        </button>
-                    </Tooltip>
-                    <Tooltip key="Pin" title={<div className="dash-tooltip">Pin to Presentation</div>}>
-                        <button className="antimodeMenu-button" style={{ display: this.PinToPres === returnFalse ? 'none' : undefined }} onPointerDown={this.PinToPres}>
-                            <FontAwesomeIcon icon="map-pin" size="lg" />
-                        </button>
-                    </Tooltip>
-                    <Tooltip key="trail" title={<div className="dash-tooltip">Show Linked Trail</div>}>
-                        <button className="antimodeMenu-button" style={{ display: this.ShowTargetTrail === returnFalse ? 'none' : undefined }} onPointerDown={this.ShowTargetTrail}>
-                            <FontAwesomeIcon icon="taxi" size="lg" />
-                        </button>
-                    </Tooltip>
-                    <Tooltip key="toggle" title={<div className="dash-tooltip">make target visibility toggle on click</div>}>
-                        <button
-                            className="antimodeMenu-button"
-                            style={{ display: this.IsTargetToggler === returnFalse ? 'none' : undefined, color: this.IsTargetToggler() ? 'black' : 'white', backgroundColor: this.IsTargetToggler() ? 'white' : 'black' }}
-                            onPointerDown={this.MakeTargetToggle}>
-                            <FontAwesomeIcon icon="thumbtack" size="lg" />
-                        </button>
-                    </Tooltip>
+                    {this.Delete !== returnFalse && <IconButton 
+                        tooltip={'Remove Link Anchor'}
+                        onPointerDown={this.Delete}
+                        icon={<FontAwesomeIcon icon="trash-alt" />}
+                        color={StrCast(Doc.UserDoc().userColor)}
+                    />}
+                    {this.PinToPres !== returnFalse && <IconButton 
+                        tooltip={'Pin to Presentation'}
+                        onPointerDown={this.PinToPres}
+                        icon={<FontAwesomeIcon icon="map-pin" />}
+                        color={StrCast(Doc.UserDoc().userColor)}
+                    />}
+                    {this.ShowTargetTrail !== returnFalse && <IconButton 
+                        tooltip={'Show Linked Trail'}
+                        onPointerDown={this.ShowTargetTrail}
+                        icon={<FontAwesomeIcon icon="taxi" />}
+                        color={StrCast(Doc.UserDoc().userColor)}
+                    />}
+                    {this.IsTargetToggler !== returnFalse && <Toggle
+                        tooltip={'Make target visibility toggle on click'}
+                        type={Type.PRIM}
+                        toggleType={ToggleType.BUTTON}
+                        toggleStatus={this.IsTargetToggler()}
+                        onClick={this.MakeTargetToggle}
+                        icon={<FontAwesomeIcon icon="thumbtack" />}
+                        color={StrCast(Doc.UserDoc().userColor)}
+                    />}
                 </>
             );
 
diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx
index 1c9e124a2..955a4a174 100644
--- a/src/client/views/selectedDoc/SelectedDocView.tsx
+++ b/src/client/views/selectedDoc/SelectedDocView.tsx
@@ -40,7 +40,7 @@ export class SelectedDocView extends React.Component<SelectedDocViewProps> {
                         onClick: () => {DocumentManager.Instance.showDocument(doc, options, finished);}
                     }
                 })}
-                color={Colors.WHITE}
+                color={StrCast(Doc.UserDoc().userColor)}
             />
         </div>
     }
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index b9d5ed924..5c5547180 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -119,14 +119,6 @@ export class TopBar extends React.Component {
                         dashView?.showContextMenu(e.clientX + 20, e.clientY + 30);
                     }}
                 />
-                <Button
-                    text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'}
-                    onClick={() => {
-                        SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
-                    }}
-                    color={this.color}
-                    size={Size.SMALL}
-                />
                 {!Doc.noviceMode && (
                     <IconButton
                         tooltip="Work on a copy of the dashboard layout"
@@ -152,15 +144,14 @@ export class TopBar extends React.Component {
     @computed get topbarRight() {
         return (
             <div className="topbar-right">
-                {Doc.ActiveDashboard && <Button
-                    text={GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'View Original'}
+                <Button
+                    text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'}
                     type={Type.TERT}
                     color={this.variantColor}
                     onClick={() => {
                         SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
                     }}
-                    size={Size.SMALL}
-                />}
+                />
                 <IconButton tooltip={"Server ⌘⇧S"} size={Size.SMALL} color={this.color} onClick={ServerStats.Instance.open} icon={<FaStamp />} />
                 <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" />} />
-- 
cgit v1.2.3-70-g09d2


From c13c54f4ab45ab098efe991a403f8fc0a2467704 Mon Sep 17 00:00:00 2001
From: eperelm2 <emily_perelman@brown.edu>
Date: Tue, 18 Jul 2023 12:35:52 -0400
Subject: merged with master for component library

---
 package-lock.json                       | 243 ++++++++++++++++++--------------
 src/client/views/FilterPanel.scss       |  10 +-
 src/client/views/FilterPanel.tsx        |   5 +-
 src/client/views/PropertiesSection.scss |   2 +-
 src/client/views/PropertiesSection.tsx  |  27 +++-
 src/client/views/PropertiesView.tsx     |  72 ++++------
 6 files changed, 201 insertions(+), 158 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index d89dc2f65..ec4130567 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4481,7 +4481,7 @@
         "@types/strip-bom": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
-            "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==",
+            "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
             "dev": true
         },
         "@types/strip-json-comments": {
@@ -4837,7 +4837,7 @@
                 "textarea-caret": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.0.2.tgz",
-                    "integrity": "sha1-82DEhpmqGr9xhoCkOjGoUGZcLK8="
+                    "integrity": "sha512-gRzeti2YS4did7UJnPQ47wrjD+vp+CJIe9zbsu0bJ987d8QVLvLNG9757rqiQTIy4hGIeFauTTJt5Xkn51UkXg=="
                 }
             }
         },
@@ -4936,7 +4936,7 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
         },
         "agent-base": {
             "version": "6.0.2",
@@ -5867,7 +5867,7 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
         },
         "bail": {
             "version": "2.0.2",
@@ -5937,7 +5937,7 @@
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
+            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
         },
         "base64-js": {
             "version": "1.5.1",
@@ -7413,7 +7413,7 @@
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -7423,7 +7423,7 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
         },
         "compress-commons": {
             "version": "2.1.1",
@@ -8151,7 +8151,7 @@
         "custom-event": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-            "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
+            "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
         },
         "cyclist": {
             "version": "1.0.1",
@@ -8159,6 +8159,16 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3": {
             "version": "7.8.4",
             "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.4.tgz",
@@ -9225,7 +9235,7 @@
         "dynamic-dedupe": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
-            "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==",
+            "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
             "dev": true,
             "requires": {
                 "xtend": "^4.0.0"
@@ -9355,7 +9365,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -9539,6 +9549,28 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.62",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
+            "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -9550,6 +9582,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -11862,7 +11895,7 @@
         "fs-extra": {
             "version": "0.26.7",
             "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
-            "integrity": "sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==",
+            "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
             "requires": {
                 "graceful-fs": "^4.1.2",
                 "jsonfile": "^2.1.0",
@@ -12532,14 +12565,14 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
         },
         "has-flag": {
             "version": "3.0.0",
@@ -13115,7 +13148,7 @@
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
         },
         "inflight": {
             "version": "1.0.6",
@@ -14226,7 +14259,7 @@
         "jsonfile": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
-            "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
+            "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
             "requires": {
                 "graceful-fs": "^4.1.6"
             }
@@ -14649,7 +14682,7 @@
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
@@ -14910,7 +14943,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
+            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -14918,7 +14951,7 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
+                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
                 }
             }
         },
@@ -16604,7 +16637,7 @@
             "dependencies": {
                 "@iarna/cli": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/@iarna/cli/-/cli-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-rvVVqDa2g860niRbqs3D5RhL4la3dc1vwk+NlpKPZxKaMSHtE2se6C2x8NeveN+rcjp3/686X+u+09CZ+7lmAQ==",
                     "requires": {
                         "glob": "^7.1.2",
@@ -16707,7 +16740,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -16722,7 +16755,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -16731,12 +16764,12 @@
                 },
                 "asap": {
                     "version": "2.0.6",
-                    "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+                    "resolved": false,
                     "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
                 },
                 "asn1": {
                     "version": "0.2.6",
-                    "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
                     "requires": {
                         "safer-buffer": "~2.1.0"
@@ -16744,7 +16777,7 @@
                 },
                 "assert-plus": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
                 },
                 "asynckit": {
@@ -16754,22 +16787,22 @@
                 },
                 "aws-sign2": {
                     "version": "0.7.0",
-                    "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="
                 },
                 "aws4": {
                     "version": "1.11.0",
-                    "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
                 },
                 "balanced-match": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
                 },
                 "bcrypt-pbkdf": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
                     "requires": {
                         "tweetnacl": "^0.14.3"
@@ -16790,7 +16823,7 @@
                 },
                 "bluebird": {
                     "version": "3.7.2",
-                    "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
                 },
                 "boxen": {
@@ -16838,7 +16871,7 @@
                 },
                 "cacache": {
                     "version": "12.0.4",
-                    "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
                     "requires": {
                         "bluebird": "^3.5.5",
@@ -16875,7 +16908,7 @@
                 },
                 "caseless": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
                 },
                 "chalk": {
@@ -17019,7 +17052,7 @@
                 },
                 "combined-stream": {
                     "version": "1.0.8",
-                    "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+                    "resolved": false,
                     "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
                     "requires": {
                         "delayed-stream": "~1.0.0"
@@ -17027,7 +17060,7 @@
                 },
                 "concat-map": {
                     "version": "0.0.1",
-                    "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
                 },
                 "concat-stream": {
@@ -17057,7 +17090,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17072,7 +17105,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17081,7 +17114,7 @@
                 },
                 "config-chain": {
                     "version": "1.1.13",
-                    "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
                     "requires": {
                         "ini": "^1.3.4",
@@ -17182,7 +17215,7 @@
                 },
                 "dashdash": {
                     "version": "1.14.1",
-                    "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
                     "requires": {
                         "assert-plus": "^1.0.0"
@@ -17215,7 +17248,7 @@
                 },
                 "decode-uri-component": {
                     "version": "0.2.2",
-                    "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
                 },
                 "deep-extend": {
@@ -17261,7 +17294,7 @@
                 },
                 "dezalgo": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
                     "requires": {
                         "asap": "^2.0.0",
@@ -17313,7 +17346,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17328,7 +17361,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17337,7 +17370,7 @@
                 },
                 "ecc-jsbn": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
                     "requires": {
                         "jsbn": "~0.1.0",
@@ -17372,7 +17405,7 @@
                 },
                 "env-paths": {
                     "version": "2.2.1",
-                    "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="
                 },
                 "err-code": {
@@ -17456,7 +17489,7 @@
                 },
                 "extsprintf": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
                 },
                 "fast-json-stable-stringify": {
@@ -17466,12 +17499,12 @@
                 },
                 "figgy-pudding": {
                     "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw=="
                 },
                 "filter-obj": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ=="
                 },
                 "find-npm-prefix": {
@@ -17504,7 +17537,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17519,7 +17552,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17528,12 +17561,12 @@
                 },
                 "forever-agent": {
                     "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="
                 },
                 "form-data": {
                     "version": "2.3.3",
-                    "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
                     "requires": {
                         "asynckit": "^0.4.0",
@@ -17566,7 +17599,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17581,7 +17614,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17649,7 +17682,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17664,7 +17697,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -17673,7 +17706,7 @@
                 },
                 "fs.realpath": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
                 },
                 "function-bind": {
@@ -17763,7 +17796,7 @@
                 },
                 "getpass": {
                     "version": "0.1.7",
-                    "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+                    "resolved": false,
                     "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
                     "requires": {
                         "assert-plus": "^1.0.0"
@@ -17771,7 +17804,7 @@
                 },
                 "glob": {
                     "version": "7.2.3",
-                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
                     "requires": {
                         "fs.realpath": "^1.0.0",
@@ -17784,7 +17817,7 @@
                     "dependencies": {
                         "minimatch": {
                             "version": "3.1.2",
-                            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+                            "resolved": false,
                             "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
                             "requires": {
                                 "brace-expansion": "^1.1.7"
@@ -17827,12 +17860,12 @@
                 },
                 "graceful-fs": {
                     "version": "4.2.10",
-                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+                    "resolved": false,
                     "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
                 },
                 "har-schema": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="
                 },
                 "har-validator": {
@@ -17911,7 +17944,7 @@
                 },
                 "http-signature": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
                     "requires": {
                         "assert-plus": "^1.0.0",
@@ -18038,7 +18071,7 @@
                 },
                 "is-cidr": {
                     "version": "3.1.1",
-                    "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Gx+oErgq1j2jAKCR2Kbq0b3wbH0vQKqZ0wOlHxm0o56nq51Cs/DZA8oz9dMDhbHyHEGgJ86eTeVudtgMMOx3Mw==",
                     "requires": {
                         "cidr-regex": "^2.0.10"
@@ -18117,7 +18150,7 @@
                 },
                 "is-typedarray": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
                 },
                 "isarray": {
@@ -18132,12 +18165,12 @@
                 },
                 "isstream": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
                 },
                 "jsbn": {
                     "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
                 },
                 "json-parse-better-errors": {
@@ -18147,7 +18180,7 @@
                 },
                 "json-parse-even-better-errors": {
                     "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
                 },
                 "json-schema": {
@@ -18157,7 +18190,7 @@
                 },
                 "json-stringify-safe": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
                 },
                 "jsonparse": {
@@ -18375,7 +18408,7 @@
                 },
                 "lock-verify": {
                     "version": "2.2.2",
-                    "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.2.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-2CUNtr1ZSVKJHcYP8uEzafmmuyauCB5zZimj8TvQd/Lflt9kXVZs+8S+EbAzZLaVUDn8CYGmeC3DFGdYfnCzeQ==",
                     "requires": {
                         "@iarna/cli": "^2.1.0",
@@ -18504,7 +18537,7 @@
                 },
                 "meant": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/meant/-/meant-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-88ZRGcNxAq4EH38cQ4D85PM57pikCwS8Z99EWHODxN7KBY+UuPiqzRTtZzS8KTXO/ywSWbdjjJST2Hly/EQxLw=="
                 },
                 "mime-db": {
@@ -18522,7 +18555,7 @@
                 },
                 "minimatch": {
                     "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
                     "requires": {
                         "brace-expansion": "^1.1.7"
@@ -18571,7 +18604,7 @@
                 },
                 "mkdirp": {
                     "version": "0.5.6",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+                    "resolved": false,
                     "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
                     "requires": {
                         "minimist": "^1.2.6"
@@ -18619,7 +18652,7 @@
                 },
                 "node-gyp": {
                     "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw==",
                     "requires": {
                         "env-paths": "^2.2.0",
@@ -18957,7 +18990,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -18972,7 +19005,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -18986,7 +19019,7 @@
                 },
                 "path-is-absolute": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
                 },
                 "path-is-inside": {
@@ -19006,7 +19039,7 @@
                 },
                 "performance-now": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
                 },
                 "pify": {
@@ -19055,7 +19088,7 @@
                 },
                 "proto-list": {
                     "version": "1.2.4",
-                    "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="
                 },
                 "protoduck": {
@@ -19078,7 +19111,7 @@
                 },
                 "psl": {
                     "version": "1.9.0",
-                    "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
                 },
                 "pump": {
@@ -19118,12 +19151,12 @@
                 },
                 "qs": {
                     "version": "6.5.3",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA=="
                 },
                 "query-string": {
                     "version": "6.14.1",
-                    "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
                     "requires": {
                         "decode-uri-component": "^0.2.0",
@@ -19134,7 +19167,7 @@
                 },
                 "qw": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-1PhZ/iLKwlVNq45dnerTMKFjMof49uqli7/0QsvPNbX5OJ3IZ8msa9lUpvPheVdP+IYYPrf6cOaVil7S35joVA=="
                 },
                 "rc": {
@@ -19180,7 +19213,7 @@
                 },
                 "read-package-json": {
                     "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==",
                     "requires": {
                         "glob": "^7.1.1",
@@ -19239,7 +19272,7 @@
                 },
                 "request": {
                     "version": "2.88.2",
-                    "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
                     "requires": {
                         "aws-sign2": "~0.7.0",
@@ -19309,7 +19342,7 @@
                 },
                 "safe-buffer": {
                     "version": "5.2.1",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
                 },
                 "safer-buffer": {
@@ -19480,7 +19513,7 @@
                 },
                 "sshpk": {
                     "version": "1.17.0",
-                    "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
                     "requires": {
                         "asn1": "~0.2.3",
@@ -19536,7 +19569,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -19551,7 +19584,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -19565,7 +19598,7 @@
                 },
                 "strict-uri-encode": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
                 },
                 "string-width": {
@@ -19721,7 +19754,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -19736,7 +19769,7 @@
                             "dependencies": {
                                 "safe-buffer": {
                                     "version": "5.1.2",
-                                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                                    "resolved": false,
                                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                                 }
                             }
@@ -19755,7 +19788,7 @@
                 },
                 "tough-cookie": {
                     "version": "2.5.0",
-                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
                     "requires": {
                         "psl": "^1.1.28",
@@ -19764,7 +19797,7 @@
                     "dependencies": {
                         "punycode": {
                             "version": "2.1.1",
-                            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
                         }
                     }
@@ -19779,7 +19812,7 @@
                 },
                 "tweetnacl": {
                     "version": "0.14.5",
-                    "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+                    "resolved": false,
                     "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
                 },
                 "typedarray": {
@@ -19850,7 +19883,7 @@
                 },
                 "uri-js": {
                     "version": "4.4.1",
-                    "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
                     "requires": {
                         "punycode": "^2.1.0"
@@ -19891,7 +19924,7 @@
                 },
                 "uuid": {
                     "version": "3.4.0",
-                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
                 },
                 "validate-npm-package-license": {
@@ -19913,7 +19946,7 @@
                 },
                 "verror": {
                     "version": "1.10.0",
-                    "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
                     "requires": {
                         "assert-plus": "^1.0.0",
@@ -24773,12 +24806,12 @@
         "to-array": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
-            "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+            "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
         },
         "to-fast-properties": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-            "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
+            "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
         },
         "to-object-path": {
             "version": "0.3.0",
@@ -25145,7 +25178,7 @@
                 "strip-bom": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-                    "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+                    "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
                     "dev": true
                 }
             }
@@ -25281,6 +25314,12 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -26420,7 +26459,7 @@
                 "resolve-cwd": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-                    "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
+                    "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
                     "dev": true,
                     "requires": {
                         "resolve-from": "^3.0.0"
@@ -26429,7 +26468,7 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
                     "dev": true
                 },
                 "semver": {
@@ -26981,7 +27020,7 @@
         "yeast": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
-            "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+            "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
         },
         "yn": {
             "version": "3.1.1",
diff --git a/src/client/views/FilterPanel.scss b/src/client/views/FilterPanel.scss
index 34d06ea23..4f0460659 100644
--- a/src/client/views/FilterPanel.scss
+++ b/src/client/views/FilterPanel.scss
@@ -199,11 +199,11 @@
    // float:right;
 
    .filterBox-facetHeader-collapse{
-    float: right;
-                justify-items: right;
-                align-items: flex-end;
-                margin-left: auto;
-                margin-right: 9px;
+        float: right;
+        justify-items: right;
+        align-items: flex-end;
+        margin-left: auto;
+        margin-right: 9px;
     }
 
 }
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index b10fafb57..68d29942b 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -177,7 +177,8 @@ export class FilterPanel extends React.Component<filterProps> {
                     <div style={{ width: '100%' }}>
                         <Select placeholder="Add a filter..." options={options} isMulti={false} onChange={val => this.facetClick((val as UserOptions).value)} onKeyDown={e => e.stopPropagation()} value={null} closeMenuOnSelect={true} />
                     </div>
-                    {/* <div className="filterBox-select-bool">
+                    {/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */}
+                    {/* <div className="filterBox-select-bool"> 
                         <select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._childFilters_boolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.childFilters_boolean)}>
                             {['AND', 'OR'].map(bool => (
                                 <option value={bool} key={bool}>
@@ -197,7 +198,7 @@ export class FilterPanel extends React.Component<filterProps> {
                                 
                                 <div className = "filterBox-facetHeader-collapse"> 
                                  <AiOutlineMinusSquare/>
-                                 <CiCircleRemove/>
+                                 {/* <CiCircleRemove/> */}
                                   </div>
                                
                             </div>
diff --git a/src/client/views/PropertiesSection.scss b/src/client/views/PropertiesSection.scss
index 79479a4ce..321b6300c 100644
--- a/src/client/views/PropertiesSection.scss
+++ b/src/client/views/PropertiesSection.scss
@@ -9,7 +9,7 @@
     .propertiesView-sectionTitle {
         text-align: center;
         display: flex;
-        padding: 10px;
+        padding: 3px 10px;
         font-size: 14px;
         font-weight: bold;
         justify-content: space-between;
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
index b900d17ca..ec8043ffe 100644
--- a/src/client/views/PropertiesSection.tsx
+++ b/src/client/views/PropertiesSection.tsx
@@ -1,6 +1,6 @@
 import React = require('react');
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
-import { action, computed } from "mobx"
+import { action, computed, observable } from "mobx"
 import { observer } from "mobx-react"
 import './PropertiesSection.scss'
 import { Doc } from '../../fields/Doc';
@@ -12,7 +12,8 @@ export interface PropertiesSectionProps {
     isOpen: boolean,
     setIsOpen: (bool: boolean) => any 
     inSection?: boolean,
-    setInSection?: (bool: boolean) => any 
+    setInSection?: (bool: boolean) => any
+    onDoubleClick?: () => void
 }
 
 @observer
@@ -28,14 +29,28 @@ export class PropertiesSection extends React.Component<PropertiesSectionProps> {
     @computed get variantColor() {
         return StrCast(Doc.UserDoc().userVariantColor);
     }
+
+    @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" onPointerDown={action(() => (this.props.setIsOpen(!this.props.isOpen)))} style={{ 
-            background: this.props.isOpen ?  this.variantColor : this.backgroundColor,
-            color: this.color
-        }}>
+        <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"  />
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 31c03de4c..cb0663554 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -575,19 +575,29 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
-    @computed get type() {
-        const type = Utils.cleanDocumentType(StrCast(this.selectedDoc?.type) as DocumentType);
-        return (
-            <Button
-                formLabel={"Type"}
-                text={type}
-                type={Type.SEC}
-                color={this.color}
-                align='flex-start'
-                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>
+           
+         )
+     }
 
     @computed get currentComponent() {
 
@@ -607,30 +617,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         }
     }
 
-    @computed get currentType() {
-       // console.log("current type " + this.selectedDoc?.type)
-
-        const documentType = StrCast(this.selectedDoc?.type)
-        var currentType: string = documentType;
-        var capitalizedDocType = currentType.charAt(0).toUpperCase() + currentType.slice(1)
-
-        return (
-            <div>
-                <div className = "propertiesView-wordType">Type</div>  
-                <div className= "currentType"> 
-                <div className='currentType-icon'>
-                     {this.currentComponent}
-                </div>
-               
-                  {capitalizedDocType} 
-               
-                </div>
-               
-            </div>
-          
-        )
-    }
-
     @undoBatch
     @action
     setTitle = (value: string | number) => {
@@ -1183,6 +1169,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             </>}
             isOpen={this.openSharing}
             setIsOpen={(bool) => this.openSharing = bool}
+            onDoubleClick={() => this.onDoubleClick()}
         />
     }
 
@@ -1218,7 +1205,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             </div>}
             isOpen={this.openFilters}
             setIsOpen={(bool) => this.openFilters = bool}
-            onDoubleClick={() => this.onDoubleClick()}
+             onDoubleClick={() => this.onDoubleClick()}
         />
     }
 
@@ -1232,7 +1219,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     content={this.isInk ? this.appearanceEditor : null}
                     isOpen={this.openAppearance}
                     setIsOpen={(bool) => this.openAppearance = bool}
-                    onDoubleClick={() => this.onDoubleClick()}
+                     onDoubleClick={() => this.onDoubleClick()}
                 />
                 <PropertiesSection
                     title="Transform"
@@ -1260,7 +1247,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get contextsSubMenu() {
         return <PropertiesSection
             title="Other Contexts"
-            content={this.contexts}
+            content={this.contextCount > 0 ? this.contexts : "There are no other contexts."}
             isOpen={this.openContexts}
             setIsOpen={(bool) => this.openContexts = bool}
             onDoubleClick={() => this.onDoubleClick()}
@@ -1269,10 +1256,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
 
 
+
+
     @computed get linksSubMenu() {
         return <PropertiesSection
             title="Linked To"
-            content={this.links}
+            content={this.linkCount > 0 ? this.links : "There are no current links." }
             isOpen={this.openLinks}
             setIsOpen={(bool) => this.openLinks = bool}
             onDoubleClick={() => this.onDoubleClick()}
@@ -1753,7 +1742,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         
                         <div className="propertiesView-name">{this.editableTitle}</div>
                         <div className = "propertiesView-type"> {this.currentType} </div>
-                        <div className="propertiesView-type">{this.type}</div>
                         {this.contextsSubMenu}
                         {this.linksSubMenu}
                         {!this.selectedDoc || !LinkManager.currentLink || (!hasSelectedAnchor && this.selectedDoc !== LinkManager.currentLink) ? null : (
-- 
cgit v1.2.3-70-g09d2

