From 48dbde117728e4b36bae11f0f16a796059ecf7ee Mon Sep 17 00:00:00 2001
From: dg314 <david_grossman@brown.edu>
Date: Sat, 17 Jul 2021 15:13:39 -0400
Subject: initial search panel

---
 src/client/views/MainView.tsx | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4eeb1fc95..bc9c6f4d2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -180,8 +180,8 @@ export class MainView extends React.Component {
             const targClass = targets[0].className.toString();
             if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) {
                 const check = targets.some((thing) =>
-                    (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
-                        thing.className === "collectionSchema-header-menuOptions"));
+                (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
+                    thing.className === "collectionSchema-header-menuOptions"));
                 !check && SearchBox.Instance.resetSearch(true);
             }
             !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
@@ -388,7 +388,6 @@ export class MainView extends React.Component {
                     SettingsManager.Instance.open();
                     break;
                 case "Catalog":
-                    SearchBox.Instance._searchFullDB = "My Stuff";
                     SearchBox.Instance.enter(undefined);
                     break;
                 case "Help":
@@ -523,7 +522,7 @@ export class MainView extends React.Component {
         </svg>;
     }
 
-    @computed get search() {
+    /*@computed get search() {
         TraceMobx();
         return <div className="mainView-searchPanel">
             <SearchBox Document={CurrentUserUtils.MySearchPanelDoc}
@@ -555,7 +554,7 @@ export class MainView extends React.Component {
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined} />
         </div>;
-    }
+    }*/
 
     @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
         return !DocumentLinksButton.invisibleWebDoc ? null :
@@ -604,7 +603,7 @@ export class MainView extends React.Component {
             <GroupManager />
             <GoogleAuthenticationManager />
             <DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
-            {this.search}
+            {/*this.search*/}
             <CollectionMenu />
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
-- 
cgit v1.2.3-70-g09d2


From fc1559fd74bde212f418b27806b68a76748153ad Mon Sep 17 00:00:00 2001
From: dg314 <david_grossman@brown.edu>
Date: Thu, 29 Jul 2021 15:12:03 -0400
Subject: search panel updates

---
 src/client/util/CurrentUserUtils.ts    |  11 +-
 src/client/views/MainView.tsx          |   6 +-
 src/client/views/search/SearchBox.scss | 173 ++++--------
 src/client/views/search/SearchBox.tsx  | 491 +++++++++++++++------------------
 4 files changed, 291 insertions(+), 390 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 596ccd9a6..31384da3b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -947,17 +947,12 @@ export class CurrentUserUtils {
 
     // Search sidebar is where searches within the document are performed
     static setupSearchSidebar(doc: Doc) {
-        if (doc.mySearchPanelDoc === undefined) {
-            doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({
-                _width: 500, _height: 300, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
+        if (doc.mySearchPanel === undefined) {
+            doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
+                backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
                 childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", system: true
             })) as any as Doc;
         }
-        if (doc.mySearchPanel === undefined) {
-            const searchPanelDoc = Cast(doc.mySearchPanelDoc, Doc, null);
-            //const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true });
-            doc.mySearchPanel = new PrefetchProxy(Docs.Create.StackingDocument([searchPanelDoc], { title: "Search", _yMargin: 20, ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true }));
-        }
     }
 
     static setupClickEditorTemplates(doc: Doc) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index bc9c6f4d2..0d957518b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -178,12 +178,12 @@ export class MainView extends React.Component {
         const targets = document.elementsFromPoint(e.x, e.y);
         if (targets.length) {
             const targClass = targets[0].className.toString();
-            if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) {
+            /*if (SearchBox.Instance._resultsOpen) {
                 const check = targets.some((thing) =>
                 (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
                     thing.className === "collectionSchema-header-menuOptions"));
                 !check && SearchBox.Instance.resetSearch(true);
-            }
+            }*/
             !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
             !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
         }
@@ -192,7 +192,7 @@ export class MainView extends React.Component {
     initEventListeners = () => {
         window.addEventListener("drop", e => e.preventDefault(), false);  // prevent default behavior of navigating to a new web page
         window.addEventListener("dragover", e => e.preventDefault(), false);
-        document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
+        //document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
         document.addEventListener("pointerdown", this.globalPointerDown);
         document.addEventListener("click", (e: MouseEvent) => {
             if (!e.cancelBubble) {
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 4f5b7e41a..f8e994da7 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -2,141 +2,84 @@
 @import "./NaviconButton.scss";
 
 .searchBox-container {
-    display: flex;
-    flex-direction: column;
     width: 100%;
     height: 100%;
-    position: relative;
     font-size: 10px;
     line-height: 1;
-    overflow-y: auto;
-    overflow-x: visible;
-    background: lightgrey;
-    overflow: visible;
+    background: none;
     z-index: 1000;
+    padding: 0px;
+    cursor: default;
 
     .searchBox-bar {
-        height: $searchpanel-height;
+        width: 100%;
+        height: 35px;
         display: flex;
         justify-content: center;
         align-items: center;
-        background-color: black;
+        background-color: none;
+        padding: 5px;
 
-        .searchBox-lozenges {
-            position: absolute;
-            left: 15;
-            display: flex;
-
-            .searchBox-lozenge-user,
-            .searchBox-lozenge-dashboard,
-            .searchBox-lozenge {
-                height: 18px;
-                padding: 4px;
-                margin-right: 5px;
-                display: flex;
-                align-items: center;
-                border: grey 1px solid;
-                .searchBox-logoff,
-                .searchBox-dashboards {
-                    border-radius: 3px;
-                    background: olivedrab;
-                    color: white;
-                    display: none;
-                    margin-left: 5px;
-                    padding: 1px 2px 1px 2px;
-                    cursor: pointer;
-                }
-                .searchBox-logoff {
-                    background: red;
-                }
-
-                .searchBox-dashSelect{
-                    border: none;
-                    background-color: transparent;
+        .searchBox-type {
+            display: block;
+            width: 50px;
+            outline: none;
+            padding: 1px 5px 1px 5px;
+            color: black;
+            height: 25px;
+            border: 1px solid black;
+        }
 
-                    &:hover {
-                        cursor: pointer;
-                    }
-                }
-            }
-            .searchBox-lozenge-user:hover {
-                .searchBox-logoff {
-                    display:inline-block;
-                }
-            }
-            .searchBox-lozenge-dashboard:hover {
-                .searchBox-dashboards {
-                    display:inline-block;
-                }
-            }
+        .searchBox-input {
+            display: block;
+            width: calc(100% - 50px);
+            outline: none;
+            padding: 1px 5px 1px 5px;
+            color: black;
+            height: 25px;
+            border: 1px solid black;
         }
-        .searchBox-query {
-            position: relative;
+    }
+
+    .searchBox-results-container {
+        display: flex;
+        flex-direction: column;
+        width: 100%;
+        height: 100%;
+        justify-content: "center";
+    
+        .searchBox-results-count {
             display: flex;
-            width: 450;
+            color: gray;
+            margin-left: 5px;
         }
-        .searchBox-barChild {
+    
+        .searchBox-results-scroll-view {
+            margin-top: 10px;
+            display: inline-block;
+            width: 100%;
+            height: calc(100% - 55px);
+            overflow-y: scroll;
 
-            &.searchBox-collection {
-                flex: 0 1 auto;
-                margin-left: 2px;
-                margin-right: 2px
-            }
+            .searchBox-results-scroll-view-result {
+                display: inline-block;
+                vertical-align: middle;
+                width: 100%;
+                height: 40px;
+                cursor: pointer;
+                font-size: 15px;
+                padding: 10px;
 
-            &.searchBox-input {
-                margin:5px;
-                border-radius:20px;
-                border:black;    
-                display: block;
-                width: 130px;
-                -webkit-transition: width 0.4s;
-                transition: width 0.4s;
-                align-self: stretch;
-                outline:none;
-                &:focus {
-                    width: 500px;
-                    outline:none;
+                &.searchBox-results-scroll-view-result-selected {
+                    background: gray;
                 }
-            }
-            &.searchBox-filter {
-                align-self: stretch;
-                button{
-                    transform:none;
-                    &:hover {
-                        transform: none;
-                    }
-                }
-            }
 
-            &.searchBox-submit {
-                margin-left: 2px;
-                margin-right: 2px
-            }
-
-            &.searchBox-close {
-                color: $light-color;
-                max-height: $searchpanel-height;
+                .titletitle {
+                    display: relative;
+                    float: left;
+                    left: 50px;
+                }
             }
         }
-    }   
-}
-
-.searchBox-results {
-    display: flex;
-    flex-direction: column;
-    top: 300px;
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    overflow: visible;
-
-    .no-result {
-        width: 500px;
-        background: $light-color-secondary;
-        padding: 10px;
-        height: 50px;
-        text-transform: uppercase;
-        text-align: left;
-        font-weight: bold;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index cc4590969..374a754bf 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym, FieldsSym } from '../../../fields/Doc';
 import { documentSchema } from "../../../fields/documentSchemas";
 import { Copy, Id, ToString } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
@@ -26,6 +26,9 @@ import "./SearchBox.scss";
 import { undoBatch } from "../../util/UndoManager";
 import { DocServer } from "../../DocServer";
 import { MainView } from "../MainView";
+import { DocumentManager } from "../../util/DocumentManager";
+import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells";
+import { transpileModule } from "typescript";
 
 export const searchSchema = createSchema({ Document: Doc });
 
@@ -38,33 +41,18 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     public static Instance: SearchBox;
 
     private _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB];
-    private _numResultsPerPage = 500;
-    private _numTotalResults = -1;
-    private _endIndex = -1;
-    private _lockPromise?: Promise<void>;
     private _resultsSet = new Map<Doc, number>();
     private _inputRef = React.createRef<HTMLInputElement>();
-    private _maxSearchIndex: number = 0;
-    private _curRequest?: Promise<any> = undefined;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
 
-    private docsforfilter: Doc[] | undefined = [];
-    private realTotalResults: number = 0;
-    private newsearchstring = "";
-
-    @observable _undoBackground: string | undefined = "";
+    @observable _searchString = "";
+    @observable _docTypeString = "all";
     @observable _icons: string[] = this._allIcons;
     @observable _results: [Doc, string[], string[]][] = [];
-    @observable _visibleElements: JSX.Element[] = [];
-    @observable _visibleDocuments: Doc[] = [];
+    @observable _selectedResult: Doc | undefined = undefined;
     @observable _deletedDocsStatus: boolean = false;
     @observable _onlyAliases: boolean = true;
-    @observable _searchbarOpen = false;
-    @observable _noResults = "";
-    @observable _pageStart = 0;
-    @observable open = false;
-    @observable children = 0;
     @computed get filter() { return this._results?.length && (this.currentSelectedCollection?.props.Document._searchFilterDocs || this.currentSelectedCollection?.props.Document._docFilters); }
 
     constructor(props: any) {
@@ -76,8 +64,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         if (this._inputRef.current) {
             this._inputRef.current.focus();
         }
-        this._disposers.filters = reaction(() => this.props.Document._docFilters,
-            (filters: any) => this.setSearchFilter(this.currentSelectedCollection, !this.filter ? undefined : this.docsforfilter));
     });
 
     componentWillUnmount() {
@@ -86,32 +72,25 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
     @computed get currentSelectedCollection() { return CollectionDockingView.Instance; }
 
-    onChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
-        this.newsearchstring = e.target.value;
-        if (e.target.value === "") {
-            console.log("Reset start");
-            this.docsforfilter = undefined;
-            this.setSearchFilter(this.currentSelectedCollection, undefined);
-            this.resetSearch(false);
-
-            this.open = false;
-            this._results = [];
-            this._resultsSet.clear();
-            this._visibleElements = [];
-            this._numTotalResults = -1;
-            this._endIndex = -1;
-            this._curRequest = undefined;
-            this._maxSearchIndex = 0;
-        }
+    onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
+        this._searchString = e.target.value;
+        this.submitSearch();
     });
 
-    enter = action((e: React.KeyboardEvent | undefined) => {
+    onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => {
+        this._docTypeString = e.target.value;
+        this.submitSearch();
+    });
+
+    /*enter = action((e: React.KeyboardEvent | undefined) => {
         if (!e || e.key === "Enter") {
-            this.layoutDoc._searchString = this.newsearchstring;
-            this._pageStart = 0;
-            this.open = true;
             this.submitSearch();
         }
+    });*/
+
+    onResultClick = action((doc: Doc) => {
+        this.selectElement(doc);
+        this._selectedResult = doc;
     });
 
     @action
@@ -126,21 +105,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         return finalDocs;
     }
 
-    static async foreachRecursiveDocAsync(docs: Doc[], func: (doc: Doc) => void) {
-        let newarray: Doc[] = [];
-        while (docs.length > 0) {
-            newarray = [];
-            await Promise.all(docs.filter(d => d).map(async d => {
-                const fieldKey = Doc.LayoutFieldKey(d);
-                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                const data = d[annos ? fieldKey + "-annotations" : fieldKey];
-                const docs = await DocListCastAsync(data);
-                docs && newarray.push(...docs);
-                func(d);
-            }));
-            docs = newarray;
-        }
-    }
     static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
         let newarray: Doc[] = [];
         while (docs.length > 0) {
@@ -172,15 +136,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             });
 
             this._results = found;
-            this.docsforfilter = this._results.map(r => r[0]);
-            this.setSearchFilter(selectedCollection, this.filter && found.length ? this.docsforfilter : undefined);
-            this._numTotalResults = found.length;
-            this.realTotalResults = found.length;
         }
-        else {
-            this._noResults = "No collection selected :(";
-        }
-
     }
 
     static documentKeys(doc: Doc) {
@@ -197,246 +153,253 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
     @action
     submitSearch = async () => {
-        this.resetSearch(false);
-
-        //this.props.Document._docFilters = new List();
-        this._noResults = "";
+        this.resetSearch();
 
         this.dataDoc[this.fieldKey] = new List<Doc>([]);
-        this.children = 0;
-        let query = StrCast(this.layoutDoc._searchString);
+        let query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
         this._results = [];
         this._resultsSet.clear();
-        this._visibleElements = [];
-        this._visibleDocuments = [];
 
         if (query) {
-            this._endIndex = 12;
-            this._maxSearchIndex = 0;
-            this._numTotalResults = -1;
             this.searchCollection(query);
-            runInAction(() => {
-                this.open = this._searchbarOpen = true;
-                this.resultsScrolled();
-            });
         }
     }
 
-    getAllResults = async (query: string) => {
-        return SearchUtil.Search(query, true, { fq: this.filterQuery, start: 0, rows: 10000000 });
-    }
+    resetSearch = action(() => {
+        this._results.forEach(result => {
+            Doc.UnBrushDoc(result[0]);
+            Doc.ClearSearchMatches();
+        });
+    });
 
-    private get filterQuery() {
-        const baseExpr = "NOT system_b:true";
-        const authorExpr = undefined;
-        const includeDeleted = this._deletedDocsStatus ? "" : " NOT deleted_b:true";
-        const typeExpr = this._onlyAliases ? "NOT {!join from=id to=proto_i}type_t:*" : `(type_t:* OR {!join from=id to=proto_i}type_t:*) ${this._blockedTypes.map(type => `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`;
-        // fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection   q:text_t:hello
-        return [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, "");
+    selectElement = async (doc: Doc) => {
+        await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right    
     }
 
-    @computed get primarySort() {
-        const suffixMap = (type: ColumnType) => {
-            switch (type) {
-                case ColumnType.Date: return "_d";
-                case ColumnType.String: return "_t";
-                case ColumnType.Boolean: return "_b";
-                case ColumnType.Number: return "_n";
+    render() {
+        const results = this._results.map(result => {
+            var className = "searchBox-results-scroll-view-result";
+
+            if (this._selectedResult == result[0]) {
+                className += " searchBox-results-scroll-view-result-selected"
             }
-        };
-        const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
-        return headers.reduce((p: Opt<string>, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined);
-    }
 
-    searchDatabase = async (query: string) => {
-        this._lockPromise && (await this._lockPromise);
-        this._lockPromise = new Promise(async res => {
-            while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) {
-                this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, /*sort: this.primarySort,*/ fq: this.filterQuery, start: 0, rows: this._numResultsPerPage, hl: "on", "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => {
-                    // happens at the beginning
-                    this.realTotalResults = res.numFound <= 0 ? 0 : res.numFound;
-                    if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) {
-                        this._numTotalResults = res.numFound;
-                    }
-                    const highlighting = res.highlighting || {};
-                    const highlightList = res.docs.map(doc => highlighting[doc[Id]]);
-                    const lines = new Map<string, string[]>();
-                    res.docs.map((doc, i) => lines.set(doc[Id], res.lines[i]));
-                    const docs = res.docs;
-                    const highlights: typeof res.highlighting = {};
-                    docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]);
-                    const filteredDocs = this.filterDocsByType(docs);
-
-                    runInAction(() => filteredDocs.forEach((doc, i) => {
-                        const index = this._resultsSet.get(doc);
-                        const highlight = highlights[doc[Id]];
-                        const line = lines.get(doc[Id]) || [];
-                        const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : [];
-                        // if (this.findCommonElements(hlights)) {
-                        // }
-                        if (index === undefined) {
-                            this._resultsSet.set(doc, this._results.length);
-                            this._results.push([doc, hlights, line]);
-                        } else {
-                            this._results[index][1].push(...hlights);
-                            this._results[index][2].push(...line);
-                        }
-                    }));
-
-                    this._curRequest = undefined;
-                }));
-                this._maxSearchIndex += this._numResultsPerPage;
-
-                await this._curRequest;
+            if (this._docTypeString == "all" || this._docTypeString == result[0].type) {
+                return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="titletitle">{result[0].title}</div></div>)
             }
 
-            this.resultsScrolled();
+            return null;
+        })
 
-            const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
-            this.docsforfilter = this._results.map(r => r[0]);
-            this.setSearchFilter(selectedCollection, this.filter ? this.docsforfilter : undefined);
-            res();
-        });
-        return this._lockPromise;
+        results.filter(result => result)
+
+        return (
+            <div style={{ pointerEvents: "all" }} className="searchBox-container">
+                <div className="searchBox-bar">
+                    <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
+                        <option value="all">All</option>
+                        <option value="rtf">Text</option>
+                        <option value="img">Image</option>
+                        <option value="pdf">PDF</option>
+                    </select>
+                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" ref={this._inputRef} className="searchBox-input" />
+                </div >
+                <div className="searchBox-results-container">
+                    <div className="searchBox-results-count">
+                        {`${results.length}` + " result" + (results.length == 1 ? "" : "s")}
+                    </div>
+                    <div className="searchBox-results-scroll-view">
+                        {results}
+                    </div>
+                </div>
+            </div >
+        );
     }
+}
+
+
+
+/*
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from '@material-ui/core';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym, FieldsSym } from '../../../fields/Doc';
+import { documentSchema } from "../../../fields/documentSchemas";
+import { Copy, Id, ToString } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { DocumentType } from "../../documents/DocumentTypes";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { SetupDrag } from '../../util/DragManager';
+import { SearchUtil } from '../../util/SearchUtil';
+import { Transform } from '../../util/Transform';
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView";
+import { CollectionViewType } from '../collections/CollectionView';
+import { ViewBoxBaseComponent } from "../DocComponent";
+import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import "./SearchBox.scss";
+import { undoBatch } from "../../util/UndoManager";
+import { DocServer } from "../../DocServer";
+import { MainView } from "../MainView";
+import { SelectionManager } from "../../util/SelectionManager";
+import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells";
+import { transpileModule } from "typescript";
+import { DocumentManager } from "../../util/DocumentManager";
+
+export const searchSchema = createSchema({ Document: Doc });
+
+type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
+const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
+
+@observer
+export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
+    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
+    public static Instance: SearchBox;
+
+    @observable _searchString = "";
+    @observable _docTypeString = "all";
+    @observable _results: [Doc, string[], string[]][] = [];
+    @observable _selectedResult: Doc | undefined = undefined;
+    @observable _deletedDocsStatus: boolean = false;
+    @observable _onlyAliases: boolean = true;
 
-    @action.bound
-    openSearch(e: React.SyntheticEvent) {
-        e.stopPropagation();
-        this._results.forEach(result => Doc.BrushDoc(result[0]));
+    constructor(props: any) {
+        super(props);
+        SearchBox.Instance = this;
     }
 
-    resetSearch = action((close: boolean) => {
-        this._results.forEach(result => {
-            Doc.UnBrushDoc(result[0]);
-            Doc.ClearSearchMatches();
-        });
-        close && (this.open = this._searchbarOpen = false);
+    onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
+        this._searchString = e.target.value;
+        this.submitSearch();
     });
 
-    @action.bound
-    closeResults() {
-        this._results = [];
-        this._resultsSet.clear();
-        this._visibleElements = [];
-        this._visibleDocuments = [];
-        this._numTotalResults = -1;
-        this._endIndex = -1;
-        this._curRequest = undefined;
-    }
+    onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => {
+        this._docTypeString = e.target.value;
+        this.submitSearch();
+    });
 
-    @action
-    resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
-        this._endIndex = 30;
-        const headers = new Set<string>(["title", "author", "text", "type", "data", "*lastModified", "context"]);
+    onResultClick = action((doc: Doc) => {
+        this.selectElement(doc);
+        this._selectedResult = doc;
+    });
 
-        if (this._numTotalResults <= this._maxSearchIndex) {
-            this._numTotalResults = this._results.length;
+    static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
+        const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
+        let newarray: Doc[] = [];
+        while (docs.length > 0) {
+            newarray = [];
+            docs.filter(d => d).forEach(d => {
+                const dtype = StrCast(d.type, "string") as DocumentType;
+                if (dtype && !blockedTypes.includes(dtype)) {
+                    const fieldKey = Doc.LayoutFieldKey(d);
+                    const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+                    const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+                    data && newarray.push(...DocListCast(data));
+                    func(d);
+                }
+            });
+            docs = newarray;
         }
+    }
 
-        // only hit right at the beginning
-        // visibleElements is all of the elements (even the ones you can't see)
-        if (this._visibleElements.length !== this._numTotalResults) {
-            // undefined until a searchitem is put in there
-            this._visibleElements = Array<JSX.Element>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
-            this._visibleDocuments = Array<Doc>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
-        }
-        let max = this._numResultsPerPage;
-        max > this._results.length ? max = this._results.length : console.log("");
-        for (let i = this._pageStart; i < max; i++) {
-            //if the index is out of the window then put a placeholder in
-            //should ones that have already been found get set to placeholders?
-
-            let result: [Doc, string[], string[]] | undefined = undefined;
-
-            result = this._results[i];
-            if (result) {
-                const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
-                const lines = new List<string>(result[2]);
-                highlights.forEach((item) => headers.add(item));
-                Doc.SetSearchMatch(result[0], { searchMatch: 1 });
-                if (i < this._visibleDocuments.length) {
-                    this._visibleDocuments[i] = result[0];
-                    Doc.BrushDoc(result[0]);
-                    Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
-                    this.children++;
+    @action
+    searchCollection(query: string) {
+        const selectedCollection = CollectionDockingView.Instance;
+        query = query.toLowerCase();
+
+        if (selectedCollection !== undefined) {
+            console.log("hello111")
+            // this._currentSelectedCollection = selectedCollection;
+            const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
+            const found: [Doc, string[], string[]][] = [];
+            SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => {
+                console.log("HELLO")
+                if (this._docTypeString == "all" || this._docTypeString == doc.type) {
+                    const hlights = new Set<string>();
+                    SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+                    Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]);
                 }
-            }
-        }
-        if (this.props.Document._schemaHeaders === undefined) {
-            this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
-        }
-        if (this._maxSearchIndex >= this._numTotalResults) {
-            this._visibleElements.length = this._results.length;
-            this._visibleDocuments.length = this._results.length;
+            });
+
+            this._results = found;
+            //this.setSearchFilter(selectedCollection, this.filter && found.length ? this._docsforfilter : undefined);
         }
     }
 
-    getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
-    panelHeight = () => this.props.PanelHeight();
-    selectElement = (doc: Doc) => { /* this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex)); */ };
-    returnHeight = () => NumCast(this.layoutDoc._height);
-    returnLength = () => Math.min(window.innerWidth, 51 + 205 * Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length);
-
-    setSearchFilter = action((collectionView: { props: { Document: Doc } }, docsForFilter: Doc[] | undefined) => {
-        if (collectionView) {
-            const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), null);
-            collectionView.props.Document._searchFilterDocs = docsForFilter?.length ? new List<Doc>(docsForFilter) : undefined;
-            collectionView.props.Document._docFilters = docsForFilter?.length && docFilters?.length ? new List<string>(docFilters) : undefined;
+    static documentKeys(doc: Doc) {
+        const keys: { [key: string]: boolean } = {};
+        // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
+        //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
+        //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
+        //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
+        //  is displayed (unlikely) it won't show up until something else changes.
+        //TODO Types
+        Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false));
+        return Array.from(Object.keys(keys));
+    }
+
+    @action
+    submitSearch = async () => {
+        Doc.ClearSearchMatches();
+        this._results = [];
+
+        this.dataDoc[this.fieldKey] = new List<Doc>([]);
+        let query = StrCast(this._searchString);
+        Doc.SetSearchQuery(query);
+        this._results = [];
+
+        if (query) {
+            this.searchCollection(query);
         }
-    });
+    }
+
+    selectElement = async (doc: Doc) => {
+        await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right
+    }
 
     render() {
-        const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+        const results = this._results.map(result => {
+            var className = "searchBox-results-scroll-view-result";
+
+            if (this._selectedResult == result[0]) {
+                className += " searchBox-results-scroll-view-result-selected"
+            }
+
+            return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="titletitle">{result[0].title}</div></div>)
+        })
 
         return (
             <div style={{ pointerEvents: "all" }} className="searchBox-container">
-                <div className="searchBox-bar" style={{ background: SearchBox.Instance._undoBackground }}>
-                    <div className="searchBox-query" >
-                        <input defaultValue={""} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this._inputRef}
-                            className="searchBox-barChild searchBox-input" onKeyPress={this.enter}
-                            style={{ padding: 1, paddingLeft: 20, paddingRight: 60, color: "black", height: 20, width: 250 }} />
-                        <div style={{ display: "flex", alignItems: "center" }}>
-                            <div style={{ position: "absolute", left: 10 }}>
-                                <FontAwesomeIcon icon={"search"} size="lg"
-                                    style={{ color: "black", padding: 1, position: "relative" }} />
-                            </div>
-                            <div style={{ position: "absolute", left: Doc.UserDoc().noviceMode ? 220 : 200, width: 30, zIndex: 9000, color: "grey", background: "white", }}>
-                                {`${this._results.length}` + " of " + `${this.realTotalResults}`}
-                            </div>
-                        </div>
-                    </div >
+                <div className="searchBox-bar">
+                    <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
+                        <option value="all">All</option>
+                        <option value="rtf">Text</option>
+                        <option value="img">Img</option>
+                        <option value="pdf">PDF</option>
+                        <option value="video">Vid</option>
+                        <option value="audio">Aud</option>
+                        <option value="inks">Ink</option>
+                        <option value="col">Col</option>
+                    </select>
+                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" />
                 </div >
-                {!this._searchbarOpen ? (null) :
-                    <div style={{ zIndex: 20000, color: "black" }} ref={(r) => r?.focus()}>
-                        <div style={{ display: "flex", justifyContent: "center", }}>
-                            <div style={{ display: this.open ? "flex" : "none", overflow: "auto", position: "absolute" }}>
-                                <CollectionSchemaView {...this.props}
-                                    CollectionView={undefined}
-                                    addDocument={returnFalse}
-                                    Document={this.props.Document}
-                                    moveDocument={returnFalse}
-                                    removeDocument={returnFalse}
-                                    PanelHeight={this.open ? this.returnHeight : returnZero}
-                                    PanelWidth={this.open ? this.returnLength : returnZero}
-                                    scrollOverflow={length > window.innerWidth || this.children > 6 ? true : false}
-                                    focus={this.selectElement}
-                                    ScreenToLocalTransform={Transform.Identity}
-                                />
-                                <div style={{ position: "absolute", right: 5, bottom: 7, width: 15, height: 15, }}
-                                    onPointerDown={e => setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => {
-                                        this.props.Document._height = NumCast(this.props.Document._height) + delta[1];
-                                        return false;
-                                    }, returnFalse, emptyFunction)}
-                                >
-                                    <FontAwesomeIcon icon="grip-lines" size="lg" />
-                                </div>
-                            </div>
-                        </div>
+                <div className="searchBox-results-container">
+                    <div className="searchBox-results-count">
+                        {`${this._results.length}` + " result" + (this._results.length == 1 ? "" : "s")}
                     </div>
-                }
+                    <div className="searchBox-results-scroll-view">
+                        {results}
+                    </div>
+                </div>
             </div >
         );
     }
-}
\ No newline at end of file
+}*/
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 2ec093982c06965086326df73d365e1b54f6a2a2 Mon Sep 17 00:00:00 2001
From: dg314 <david_grossman@brown.edu>
Date: Wed, 4 Aug 2021 03:19:16 -0400
Subject: search panel updates

---
 src/client/util/CurrentUserUtils.ts    | 27 ++++++-----
 src/client/views/GlobalKeyHandler.ts   |  7 ++-
 src/client/views/MainView.tsx          | 37 --------------
 src/client/views/search/SearchBox.scss | 19 ++++++--
 src/client/views/search/SearchBox.tsx  | 89 +++++++++++++++++-----------------
 5 files changed, 81 insertions(+), 98 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 31384da3b..734b96b1e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -46,6 +46,7 @@ export class CurrentUserUtils {
     //TODO tfs: these should be temporary...
     private static mainDocId: string | undefined;
 
+    public static searchBtn: Doc;
     public static get id() { return this.curr_id; }
     public static get MainDocId() { return this.mainDocId; }
     public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
@@ -533,15 +534,6 @@ export class CurrentUserUtils {
         ];
     }
 
-    /*static setupSearchPanel(doc: Doc) {
-        if (doc.mySearchPanelDoc === undefined) {
-            doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({
-                _width: 500, _height: 300, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
-                childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", system: true
-            })) as any as Doc;
-        }
-    }*/
-
     static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         if (doc.menuStack === undefined) {
             await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId);  // sets up the right sidebar collection for mobile upload documents and sharing
@@ -562,10 +554,23 @@ export class CurrentUserUtils {
                     _height: 60,
                     watchedDocuments,
                     onClick: ScriptField.MakeScript(click, { scriptContext: "any" })
-                }));
+                })
+            );
+
+            menuBtns.forEach(menuBtn => {
+                if (menuBtn.title == "Search") {
+                    this.searchBtn = menuBtn;
+                }
+            });
             // hack -- last button is assumed to be the userDoc
             menuBtns[menuBtns.length - 1].hidden = ComputedField.MakeFunction("IsNoviceMode()");
 
+            menuBtns.forEach(menuBtn => {
+                if (menuBtn.title == "Search") {
+                    doc.searchBtn = menuBtn
+                }
+            })
+
             doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, {
                 title: "menuItemPanel",
                 childDropAction: "alias",
@@ -950,7 +955,7 @@ export class CurrentUserUtils {
         if (doc.mySearchPanel === undefined) {
             doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
                 backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
-                childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", system: true
+                childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Sidebar", system: true
             })) as any as Doc;
         }
     }
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index d92cba3eb..2a3cb36c7 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -27,7 +27,6 @@ import { LightboxView } from "./LightboxView";
 import { MainView } from "./MainView";
 import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
 import { AnchorMenu } from "./pdf/AnchorMenu";
-import { SearchBox } from "./search/SearchBox";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { SettingsManager } from "../util/SettingsManager";
 
@@ -225,7 +224,11 @@ export class KeyManager {
                 PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 break;
             case "f":
-                SearchBox.Instance.enter(undefined);
+                const searchBtn = Doc.UserDoc().searchBtn as Doc;
+
+                if (searchBtn) {
+                    MainView.Instance.selectMenu(searchBtn);
+                }
                 break;
             case "o":
                 const target = SelectionManager.Views()[0];
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0d957518b..35222e91f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -387,9 +387,6 @@ export class MainView extends React.Component {
                 case "Settings":
                     SettingsManager.Instance.open();
                     break;
-                case "Catalog":
-                    SearchBox.Instance.enter(undefined);
-                    break;
                 case "Help":
                     break;
                 default:
@@ -522,40 +519,6 @@ export class MainView extends React.Component {
         </svg>;
     }
 
-    /*@computed get search() {
-        TraceMobx();
-        return <div className="mainView-searchPanel">
-            <SearchBox Document={CurrentUserUtils.MySearchPanelDoc}
-                DataDoc={CurrentUserUtils.MySearchPanelDoc}
-                fieldKey="data"
-                dropAction="move"
-                isSelected={returnTrue}
-                isContentActive={returnTrue}
-                select={returnTrue}
-                setHeight={returnFalse}
-                addDocument={undefined}
-                addDocTab={this.addDocTabFunc}
-                pinToPres={emptyFunction}
-                rootSelected={returnTrue}
-                styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
-                removeDocument={undefined}
-                ScreenToLocalTransform={Transform.Identity}
-                PanelWidth={this.getPWidth}
-                PanelHeight={this.getPHeight}
-                renderDepth={0}
-                focus={DocUtils.DefaultFocus}
-                docViewPath={returnEmptyDoclist}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined} />
-        </div>;
-    }*/
-
     @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
         return !DocumentLinksButton.invisibleWebDoc ? null :
             <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index f8e994da7..e4d1ac6a3 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -22,17 +22,18 @@
 
         .searchBox-type {
             display: block;
-            width: 50px;
+            width: 55px;
             outline: none;
             padding: 1px 5px 1px 5px;
             color: black;
             height: 25px;
             border: 1px solid black;
+            border-right: 0px;
         }
 
         .searchBox-input {
             display: block;
-            width: calc(100% - 50px);
+            width: calc(100% - 55px);
             outline: none;
             padding: 1px 5px 1px 5px;
             color: black;
@@ -74,10 +75,20 @@
                     background: gray;
                 }
 
-                .titletitle {
+                .searchBox-result-title {
                     display: relative;
                     float: left;
-                    left: 50px;
+                    width: calc(100% - 60px);
+                    text-align: left;
+                }
+
+                .searchBox-result-type {
+                    font-size: 12px;
+                    display: relative;
+                    float: right;
+                    width: 60px;
+                    text-align: right;
+                    color: #333;
                 }
             }
         }
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 374a754bf..3cbb4b3b1 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -35,29 +35,30 @@ export const searchSchema = createSchema({ Document: Doc });
 type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
 const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
 
+const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"]
+
 @observer
 export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
     public static Instance: SearchBox;
 
-    private _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB];
     private _resultsSet = new Map<Doc, number>();
     private _inputRef = React.createRef<HTMLInputElement>();
-    private _disposers: { [name: string]: IReactionDisposer } = {};
-    private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
 
     @observable _searchString = "";
     @observable _docTypeString = "all";
-    @observable _icons: string[] = this._allIcons;
     @observable _results: [Doc, string[], string[]][] = [];
     @observable _selectedResult: Doc | undefined = undefined;
     @observable _deletedDocsStatus: boolean = false;
     @observable _onlyAliases: boolean = true;
-    @computed get filter() { return this._results?.length && (this.currentSelectedCollection?.props.Document._searchFilterDocs || this.currentSelectedCollection?.props.Document._docFilters); }
 
     constructor(props: any) {
         super(props);
         SearchBox.Instance = this;
+        this._searchString = "reset_search";
+        this.submitSearch();
+        this._searchString = "";
+        this.submitSearch();
     }
 
     componentDidMount = action(() => {
@@ -67,11 +68,9 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     });
 
     componentWillUnmount() {
-        Object.values(this._disposers).forEach(disposer => disposer?.());
+        this.resetSearch;
     }
 
-    @computed get currentSelectedCollection() { return CollectionDockingView.Instance; }
-
     onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
         this._searchString = e.target.value;
         this.submitSearch();
@@ -79,32 +78,13 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
     onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => {
         this._docTypeString = e.target.value;
-        this.submitSearch();
     });
 
-    /*enter = action((e: React.KeyboardEvent | undefined) => {
-        if (!e || e.key === "Enter") {
-            this.submitSearch();
-        }
-    });*/
-
     onResultClick = action((doc: Doc) => {
         this.selectElement(doc);
         this._selectedResult = doc;
     });
 
-    @action
-    filterDocsByType(docs: Doc[]) {
-        const finalDocs: Doc[] = [];
-        docs.forEach(doc => {
-            const layoutresult = StrCast(doc.type, "string") as DocumentType;
-            if (layoutresult && !this._blockedTypes.includes(layoutresult) && this._icons.includes(layoutresult)) {
-                finalDocs.push(doc);
-            }
-        });
-        return finalDocs;
-    }
-
     static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
         let newarray: Doc[] = [];
         while (docs.length > 0) {
@@ -120,23 +100,39 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         }
     }
 
+    static formatType(type: String): String {
+        if (type == "pdf") {
+            return "PDF";
+        }
+        else if (type == "image") {
+            return "Img";
+        }
+
+        return type.charAt(0).toUpperCase() + type.substring(1, 3);
+    }
+
     @action
     searchCollection(query: string) {
-        const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
+        const selectedCollection = CollectionDockingView.Instance;
+        const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
         query = query.toLowerCase();
 
+        this._results = []
+
         if (selectedCollection !== undefined) {
-            // this._currentSelectedCollection = selectedCollection;
             const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
-            const found: [Doc, string[], string[]][] = [];
             SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => {
-                const hlights = new Set<string>();
-                SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
-                Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]);
+                const dtype = StrCast(doc.type, "string") as DocumentType;
+                if (dtype && !blockedTypes.includes(dtype)) {
+                    const hlights = new Set<string>();
+                    SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+                    Array.from(hlights.keys()).length > 0 && this._results.push([doc, Array.from(hlights.keys()), []]);
+                }
             });
-
-            this._results = found;
         }
+
+        this._results = Array.from(new Set(this._results))
+        this._selectedResult = undefined
     }
 
     static documentKeys(doc: Doc) {
@@ -155,7 +151,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     submitSearch = async () => {
         this.resetSearch();
 
-        this.dataDoc[this.fieldKey] = new List<Doc>([]);
+        //this.dataDoc[this.fieldKey] = new List<Doc>([]);
         let query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
         this._results = [];
@@ -169,6 +165,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     resetSearch = action(() => {
         this._results.forEach(result => {
             Doc.UnBrushDoc(result[0]);
+            Doc.UnHighlightDoc(result[0]);
             Doc.ClearSearchMatches();
         });
     });
@@ -178,6 +175,8 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
     }
 
     render() {
+        var validResults = 0;
+
         const results = this._results.map(result => {
             var className = "searchBox-results-scroll-view-result";
 
@@ -186,28 +185,30 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             }
 
             if (this._docTypeString == "all" || this._docTypeString == result[0].type) {
-                return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="titletitle">{result[0].title}</div></div>)
+                validResults++;
+                return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="searchBox-result-title">{result[0].title}</div><div className="searchBox-result-type">{SearchBox.formatType(StrCast(result[0].type))}</div></div>)
             }
 
             return null;
         })
 
-        results.filter(result => result)
+        results.filter(result => result);
+
+        const selectOptions = selectValues.map(value => {
+            return <option key={value} value={value}>{SearchBox.formatType(value)}</option>
+        })
 
         return (
             <div style={{ pointerEvents: "all" }} className="searchBox-container">
                 <div className="searchBox-bar">
                     <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
-                        <option value="all">All</option>
-                        <option value="rtf">Text</option>
-                        <option value="img">Image</option>
-                        <option value="pdf">PDF</option>
+                        {selectOptions}
                     </select>
-                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" ref={this._inputRef} className="searchBox-input" />
+                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" ref={this._inputRef} />
                 </div >
                 <div className="searchBox-results-container">
                     <div className="searchBox-results-count">
-                        {`${results.length}` + " result" + (results.length == 1 ? "" : "s")}
+                        {`${validResults}` + " result" + (validResults == 1 ? "" : "s")}
                     </div>
                     <div className="searchBox-results-scroll-view">
                         {results}
-- 
cgit v1.2.3-70-g09d2


From d511c116330c3a125e6557aa5be70392aca4914b Mon Sep 17 00:00:00 2001
From: dg314 <david_grossman@brown.edu>
Date: Thu, 5 Aug 2021 16:38:00 -0400
Subject: comments

---
 src/client/views/MainView.tsx                      |   6 -
 .../views/collections/CollectionSchemaHeaders.tsx  |   2 +-
 src/client/views/search/SearchBox.scss             |  22 +-
 src/client/views/search/SearchBox.tsx              | 382 +++++++++------------
 4 files changed, 177 insertions(+), 235 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 35222e91f..97c88930e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -178,12 +178,6 @@ export class MainView extends React.Component {
         const targets = document.elementsFromPoint(e.x, e.y);
         if (targets.length) {
             const targClass = targets[0].className.toString();
-            /*if (SearchBox.Instance._resultsOpen) {
-                const check = targets.some((thing) =>
-                (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
-                    thing.className === "collectionSchema-header-menuOptions"));
-                !check && SearchBox.Instance.resetSearch(true);
-            }*/
             !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
             !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
         }
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 3b52e6408..c98dcd1d0 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -424,7 +424,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
                         e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, "remove");
                         e.target.checked === true ? this.closeResultsVisibility = "contents" : console.log("");
                         e.target.checked === true ? this.props.col.setColor("green") : this.updateFilter();
-                        e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, "remove");
+                        e.target.checked === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, "remove");
                     }}
                     checked={bool}
                 />
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index e4d1ac6a3..e7526dac8 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -66,13 +66,13 @@
                 display: inline-block;
                 vertical-align: middle;
                 width: 100%;
-                height: 40px;
+                height: 50px;
                 cursor: pointer;
                 font-size: 15px;
-                padding: 10px;
+                padding: 11px;
 
                 &.searchBox-results-scroll-view-result-selected {
-                    background: gray;
+                    background: #999;
                 }
 
                 .searchBox-result-title {
@@ -84,11 +84,25 @@
 
                 .searchBox-result-type {
                     font-size: 12px;
+                    margin-top: 6px;
                     display: relative;
                     float: right;
                     width: 60px;
                     text-align: right;
-                    color: #333;
+                    color: #222;
+                }
+
+                .searchBox-result-keys {
+                    font-size: 10px;
+                    margin-top: 1px;
+                    display: relative;
+                    float: left;
+                    width: 100%;
+                    text-align: left;
+                    color: #555;
+                    white-space: nowrap;
+                    overflow: hidden;
+                    text-overflow: ellipsis;
                 }
             }
         }
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 3cbb4b3b1..8ef09aa56 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -27,66 +27,105 @@ import { undoBatch } from "../../util/UndoManager";
 import { DocServer } from "../../DocServer";
 import { MainView } from "../MainView";
 import { DocumentManager } from "../../util/DocumentManager";
-import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells";
-import { transpileModule } from "typescript";
 
 export const searchSchema = createSchema({ Document: Doc });
 
 type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
 const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
 
-const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"]
-
+/**
+ * This is the SearchBox component. It represents the search box input and results in
+ * the search panel on the left side of the screen.
+ */
 @observer
 export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
     public static Instance: SearchBox;
 
-    private _resultsSet = new Map<Doc, number>();
     private _inputRef = React.createRef<HTMLInputElement>();
+    private _selectedCollection = CollectionDockingView.Instance;
 
     @observable _searchString = "";
     @observable _docTypeString = "all";
-    @observable _results: [Doc, string[], string[]][] = [];
+    @observable _results: [Doc, string[]][] = [];
     @observable _selectedResult: Doc | undefined = undefined;
     @observable _deletedDocsStatus: boolean = false;
     @observable _onlyAliases: boolean = true;
 
+    /**
+     * This is the constructor for the SearchBox class.
+     */
     constructor(props: any) {
         super(props);
         SearchBox.Instance = this;
-        this._searchString = "reset_search";
-        this.submitSearch();
-        this._searchString = "";
-        this.submitSearch();
     }
 
+    /** 
+     * This method is called when the SearchBox component is first mounted. When the user opens
+     * the search panel, the search input box is automatically selected. This allows the user to
+     * type in the search input box immediately, without needing clicking on it first.
+     */
     componentDidMount = action(() => {
         if (this._inputRef.current) {
             this._inputRef.current.focus();
         }
     });
 
+    /**
+     * This method is called when the SearchBox component is about to be unmounted. When the user
+     * closes the search panel, the search and its results are reset.
+     */
     componentWillUnmount() {
-        this.resetSearch;
+        this.resetSearch();
     }
 
+    /**
+     * This method is called when the text in the search input box is modified by the user. The
+     * _searchString is updated to the new value of the text in the input box and submitSearch
+     * is called to update the search results accordingly.
+     * 
+     * (Note: There is no longer a need to press enter to submit a search. Any update to the input
+     * causes a search to be submitted automatically.)
+     */
     onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
         this._searchString = e.target.value;
         this.submitSearch();
     });
 
+    /**
+     * This method is called when the option in the select drop-down menu is changed. The
+     * _docTypeString is updated to the new value of the option in the drop-down menu. This
+     * is used to filter the results of the search to documents of a specific type.
+     * 
+     * (Note: This doesn't affect the results array, so there is no need to submit a new
+     * search here. The results of the search on the _searchString query are simply filtered
+     * by type directly before rendering them.)
+     */
     onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => {
         this._docTypeString = e.target.value;
     });
 
+    /**
+     * @param {Doc} doc - doc of the search result that has been clicked on
+     *
+     * This method is called when the user clicks on a search result. The _selectedResult is
+     * updated accordingly and the doc is highlighted with the selectElement method.
+     */
     onResultClick = action((doc: Doc) => {
         this.selectElement(doc);
         this._selectedResult = doc;
     });
 
-    static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
+    /**
+     * @param {Doc[]} docs - docs to be searched through recursively
+     * @param {number, Doc => void} func - function to be called on each doc
+     *
+     * This method iterates through an array of docs and all docs within those docs, calling
+     * the function func on each doc.
+     */
+    static foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) {
         let newarray: Doc[] = [];
+        var depth = 0;
         while (docs.length > 0) {
             newarray = [];
             docs.filter(d => d).forEach(d => {
@@ -94,12 +133,45 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
                 const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
                 const data = d[annos ? fieldKey + "-annotations" : fieldKey];
                 data && newarray.push(...DocListCast(data));
-                func(d);
+                func(depth, d);
             });
             docs = newarray;
+            depth++;
         }
     }
 
+    /**
+     * @param {Doc[]} docs - docs to be searched through recursively
+     * @param {number, Doc => void} func - function to be called on each doc
+     *
+     * This method iterates asynchronously through an array of docs and all docs within those
+     * docs, calling the function func on each doc.
+     */
+    static async foreachRecursiveDocAsync(docs: Doc[], func: (depth: number, doc: Doc) => void) {
+        let newarray: Doc[] = [];
+        var depth = 0;
+        while (docs.length > 0) {
+            newarray = [];
+            await Promise.all(docs.filter(d => d).map(async d => {
+                const fieldKey = Doc.LayoutFieldKey(d);
+                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+                const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+                const docs = await DocListCastAsync(data);
+                docs && newarray.push(...docs);
+                func(depth, d);
+            }));
+            docs = newarray;
+            depth++;
+        }
+    }
+
+    /**
+     * @param {String} type - string representing the type of a doc
+     *
+     * This method converts a doc type string of any length to a 3-letter doc type string in
+     * which the first letter is capitalized. This is used when displaying the type on the
+     * right side of each search result.
+     */
     static formatType(type: String): String {
         if (type == "pdf") {
             return "PDF";
@@ -111,57 +183,75 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         return type.charAt(0).toUpperCase() + type.substring(1, 3);
     }
 
+    /**
+     * @param {String} query - search query string
+     *
+     * This method searches the CollectionDockingView instance for a certain query and puts
+     * the matching results in the results array. Docs are considered to be matching results
+     * when the query is a substring of many different pieces of its metadata (title, text,
+     * author, etc).
+     */
     @action
     searchCollection(query: string) {
-        const selectedCollection = CollectionDockingView.Instance;
         const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
+        const blockedKeys = ["x", "y", "proto", "width", "autoHeight", "acl-Override", "acl-Public", "context", "zIndex", "height", "text-scrollHeight", "text-height", "cloneFieldFilter", "isPrototype", "text-annotations",
+            "dragFactory-count", "text-noTemplate", "aliases", "system", "layoutKey", "baseProto", "xMargin", "yMargin", "links", "layout", "layout_keyValue", "fitWidth", "viewType", "title-custom",
+            "panX", "panY", "viewScale"]
         query = query.toLowerCase();
 
         this._results = []
+        this._selectedResult = undefined
 
-        if (selectedCollection !== undefined) {
-            const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
-            SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => {
+        if (this._selectedCollection !== undefined) {
+            const docs = DocListCast(this._selectedCollection.dataDoc[Doc.LayoutFieldKey(this._selectedCollection.dataDoc)]);
+            const docIDs: String[] = []
+            SearchBox.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
                 const dtype = StrCast(doc.type, "string") as DocumentType;
-                if (dtype && !blockedTypes.includes(dtype)) {
+                if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth > 0) {
                     const hlights = new Set<string>();
                     SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
-                    Array.from(hlights.keys()).length > 0 && this._results.push([doc, Array.from(hlights.keys()), []]);
+                    blockedKeys.forEach(key => {
+                        hlights.delete(key);
+                    })
+                    Array.from(hlights.keys()).length > 0 && this._results.push([doc, Array.from(hlights.keys())]);
                 }
+                docIDs.push(doc[Id])
             });
         }
-
-        this._results = Array.from(new Set(this._results))
-        this._selectedResult = undefined
     }
 
+    /**
+     * @param {Doc} doc - doc for which keys are returned
+     * 
+     * This method returns a list of a document doc's keys.
+     */
     static documentKeys(doc: Doc) {
         const keys: { [key: string]: boolean } = {};
-        // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
-        //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
-        //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
-        //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
-        //  is displayed (unlikely) it won't show up until something else changes.
-        //TODO Types
         Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false));
         return Array.from(Object.keys(keys));
     }
 
+    /**
+     * This method submits a search with the _searchString as its query and updates
+     * the results array accordingly.
+     */
     @action
     submitSearch = async () => {
         this.resetSearch();
 
-        //this.dataDoc[this.fieldKey] = new List<Doc>([]);
         let query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
         this._results = [];
-        this._resultsSet.clear();
 
         if (query) {
             this.searchCollection(query);
         }
     }
 
+    /**
+     * This method resets the search by iterating through each result and removing all
+     * brushes and highlights. All search matches are cleared as well.
+     */
     resetSearch = action(() => {
         this._results.forEach(result => {
             Doc.UnBrushDoc(result[0]);
@@ -170,10 +260,32 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
         });
     });
 
+    /**
+     * @param {Doc} doc - doc to be selected
+     * 
+     * This method selects a doc by either jumping to it (centering/zooming in on it)
+     * or opening it in a new tab.
+     */
     selectElement = async (doc: Doc) => {
-        await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right    
+        await DocumentManager.Instance.jumpToDocument(doc, true);
+    }
+
+    /**
+     * This method returns a JSX list of the options in the select drop-down menu, which
+     * is used to filter the types of documents that appear in the search results.
+     */
+    @computed
+    public get selectOptions() {
+        const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"]
+
+        return selectValues.map(value => {
+            return <option key={value} value={value}>{SearchBox.formatType(value)}</option>
+        })
     }
 
+    /**
+     * This method renders the search input box, select drop-down menu, and search results.
+     */
     render() {
         var validResults = 0;
 
@@ -186,7 +298,19 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
             if (this._docTypeString == "all" || this._docTypeString == result[0].type) {
                 validResults++;
-                return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="searchBox-result-title">{result[0].title}</div><div className="searchBox-result-type">{SearchBox.formatType(StrCast(result[0].type))}</div></div>)
+                return (
+                    <div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}>
+                        <div className="searchBox-result-title">
+                            {result[0].title}
+                        </div>
+                        <div className="searchBox-result-type">
+                            {SearchBox.formatType(StrCast(result[0].type))}
+                        </div>
+                        <div className="searchBox-result-keys">
+                            {result[1].join(", ")}
+                        </div>
+                    </div>
+                )
             }
 
             return null;
@@ -194,15 +318,11 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
 
         results.filter(result => result);
 
-        const selectOptions = selectValues.map(value => {
-            return <option key={value} value={value}>{SearchBox.formatType(value)}</option>
-        })
-
         return (
             <div style={{ pointerEvents: "all" }} className="searchBox-container">
                 <div className="searchBox-bar">
                     <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
-                        {selectOptions}
+                        {this.selectOptions}
                     </select>
                     <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" ref={this._inputRef} />
                 </div >
@@ -217,190 +337,4 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
             </div >
         );
     }
-}
-
-
-
-/*
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym, FieldsSym } from '../../../fields/Doc';
-import { documentSchema } from "../../../fields/documentSchemas";
-import { Copy, Id, ToString } from '../../../fields/FieldSymbols';
-import { List } from '../../../fields/List';
-import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
-import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
-import { Docs } from '../../documents/Documents';
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { SetupDrag } from '../../util/DragManager';
-import { SearchUtil } from '../../util/SearchUtil';
-import { Transform } from '../../util/Transform';
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView";
-import { CollectionViewType } from '../collections/CollectionView';
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { FieldView, FieldViewProps } from '../nodes/FieldView';
-import "./SearchBox.scss";
-import { undoBatch } from "../../util/UndoManager";
-import { DocServer } from "../../DocServer";
-import { MainView } from "../MainView";
-import { SelectionManager } from "../../util/SelectionManager";
-import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells";
-import { transpileModule } from "typescript";
-import { DocumentManager } from "../../util/DocumentManager";
-
-export const searchSchema = createSchema({ Document: Doc });
-
-type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
-const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
-
-@observer
-export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
-    public static Instance: SearchBox;
-
-    @observable _searchString = "";
-    @observable _docTypeString = "all";
-    @observable _results: [Doc, string[], string[]][] = [];
-    @observable _selectedResult: Doc | undefined = undefined;
-    @observable _deletedDocsStatus: boolean = false;
-    @observable _onlyAliases: boolean = true;
-
-    constructor(props: any) {
-        super(props);
-        SearchBox.Instance = this;
-    }
-
-    onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
-        this._searchString = e.target.value;
-        this.submitSearch();
-    });
-
-    onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => {
-        this._docTypeString = e.target.value;
-        this.submitSearch();
-    });
-
-    onResultClick = action((doc: Doc) => {
-        this.selectElement(doc);
-        this._selectedResult = doc;
-    });
-
-    static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
-        const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
-        let newarray: Doc[] = [];
-        while (docs.length > 0) {
-            newarray = [];
-            docs.filter(d => d).forEach(d => {
-                const dtype = StrCast(d.type, "string") as DocumentType;
-                if (dtype && !blockedTypes.includes(dtype)) {
-                    const fieldKey = Doc.LayoutFieldKey(d);
-                    const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                    const data = d[annos ? fieldKey + "-annotations" : fieldKey];
-                    data && newarray.push(...DocListCast(data));
-                    func(d);
-                }
-            });
-            docs = newarray;
-        }
-    }
-
-    @action
-    searchCollection(query: string) {
-        const selectedCollection = CollectionDockingView.Instance;
-        query = query.toLowerCase();
-
-        if (selectedCollection !== undefined) {
-            console.log("hello111")
-            // this._currentSelectedCollection = selectedCollection;
-            const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
-            const found: [Doc, string[], string[]][] = [];
-            SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => {
-                console.log("HELLO")
-                if (this._docTypeString == "all" || this._docTypeString == doc.type) {
-                    const hlights = new Set<string>();
-                    SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
-                    Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]);
-                }
-            });
-
-            this._results = found;
-            //this.setSearchFilter(selectedCollection, this.filter && found.length ? this._docsforfilter : undefined);
-        }
-    }
-
-    static documentKeys(doc: Doc) {
-        const keys: { [key: string]: boolean } = {};
-        // bcz: ugh.  this is untracked since otherwise a large collection of documents will blast the server for all their fields.
-        //  then as each document's fields come back, we update the documents _proxies.  Each time we do this, the whole schema will be
-        //  invalidated and re-rendered.   This workaround will inquire all of the document fields before the options button is clicked.
-        //  then by the time the options button is clicked, all of the fields should be in place.  If a new field is added while this menu
-        //  is displayed (unlikely) it won't show up until something else changes.
-        //TODO Types
-        Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false));
-        return Array.from(Object.keys(keys));
-    }
-
-    @action
-    submitSearch = async () => {
-        Doc.ClearSearchMatches();
-        this._results = [];
-
-        this.dataDoc[this.fieldKey] = new List<Doc>([]);
-        let query = StrCast(this._searchString);
-        Doc.SetSearchQuery(query);
-        this._results = [];
-
-        if (query) {
-            this.searchCollection(query);
-        }
-    }
-
-    selectElement = async (doc: Doc) => {
-        await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right
-    }
-
-    render() {
-        const results = this._results.map(result => {
-            var className = "searchBox-results-scroll-view-result";
-
-            if (this._selectedResult == result[0]) {
-                className += " searchBox-results-scroll-view-result-selected"
-            }
-
-            return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="titletitle">{result[0].title}</div></div>)
-        })
-
-        return (
-            <div style={{ pointerEvents: "all" }} className="searchBox-container">
-                <div className="searchBox-bar">
-                    <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
-                        <option value="all">All</option>
-                        <option value="rtf">Text</option>
-                        <option value="img">Img</option>
-                        <option value="pdf">PDF</option>
-                        <option value="video">Vid</option>
-                        <option value="audio">Aud</option>
-                        <option value="inks">Ink</option>
-                        <option value="col">Col</option>
-                    </select>
-                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" />
-                </div >
-                <div className="searchBox-results-container">
-                    <div className="searchBox-results-count">
-                        {`${this._results.length}` + " result" + (this._results.length == 1 ? "" : "s")}
-                    </div>
-                    <div className="searchBox-results-scroll-view">
-                        {results}
-                    </div>
-                </div>
-            </div >
-        );
-    }
-}*/
\ No newline at end of file
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 4d21696c1b49c2c383adcd01974c18461123b83c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 17 Aug 2021 16:55:15 -0400
Subject: made dashboard context menu items context sensitive to whether
 they're called from MyDashboards, MySharedDocs or elsewhere.  Made
 treeViewOpen a playground field so that users don't interfere with each
 other.   Made Docking views not generate alias of their tabs when shared.

---
 src/client/util/CurrentUserUtils.ts                | 35 +++++++++++-----------
 src/client/views/MainView.tsx                      |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 .../views/collections/CollectionTreeView.tsx       | 13 ++++++--
 src/client/views/collections/CollectionView.tsx    | 24 +++++++++------
 src/client/views/collections/TreeView.tsx          | 25 +++++++++++-----
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 7 files changed, 63 insertions(+), 40 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 34990e121..85762a73f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -765,8 +765,13 @@ export class CurrentUserUtils {
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true
             }));
             const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
-            (doc.myDashboards as any as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]);
-            (doc.myDashboards as any as Doc).contextMenuLabels = new List<string>(["Create New Dashboard"]);
+            const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
+            const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
+            const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
+            const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`);
+            const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
+            (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
+            (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
         }
         return doc.myDashboards as any as Doc;
     }
@@ -933,6 +938,11 @@ export class CurrentUserUtils {
             }
             if (sharedDocs instanceof Doc) {
                 Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)";
+                const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
+                const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name });
+                sharedDocs.childContextMenuFilters = new List<ScriptField>([dashboardFilter!,]);
+                sharedDocs.childContextMenuScripts = new List<ScriptField>([addToDashboards!,]);
+                sharedDocs.childContextMenuLabels = new List<string>(["Add to Dashboards",]);
             }
             doc.mySharedDocs = new PrefetchProxy(sharedDocs);
         }
@@ -1228,15 +1238,6 @@ export class CurrentUserUtils {
 
         Doc.AddDocToList(myPresentations, "data", presentation);
         userDoc.activePresentation = presentation;
-        const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
-        const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-        const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
-        const createDashboard = ScriptField.MakeScript(`createNewDashboard()`);
-        const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`);
-        const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
-        const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
-        dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!, shareDashboard!, addToDashboards!, removeDashboard!]);
-        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard", "Share Dashboard", "Add to Dashboards", "Remove Dashboard"]);
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
         CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
@@ -1306,17 +1307,15 @@ Scripting.addGlobal(async function addToDashboards(dashboard: Doc) {
     const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]);
 
     // moves the data-all field from the datadoc to the layoutdoc, necessary for off screen docs tab to function properly
-    dashboard["data-all"] = new List<Doc>(allDocs);
-    dashboardAlias["data-all"] = new List<Doc>((allDocs || []).map(doc => Doc.MakeAlias(doc)));
-
-    const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig));
-    dockingConfig.content = [];
-    dashboardAlias.dockingConfig = JSON.stringify(dockingConfig);
+    // dashboard["data-all"] = new List<Doc>(allDocs);
+    // dashboardAlias["data-all"] = new List<Doc>((allDocs || []).map(doc => Doc.MakeAlias(doc)));
 
+    // const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig));
+    // dashboardAlias.dockingConfig = JSON.stringify(dockingConfig);
 
     dashboardAlias.data = new List<Doc>(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder)));
     DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias);
-    DocListCast(dashboardAlias.data)[0].data = new List<Doc>();
+    //new List<Doc>();
     DocListCast(dashboardAlias.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any;
     Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias);
     CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b0b8d7f41..4d2a0a4c4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -107,7 +107,7 @@ export class MainView extends React.Component {
         new InkStrokeProperties();
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition",
+            DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition",
                 "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap",
                 "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
         }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c0d39b2a2..cae08e1f4 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -170,7 +170,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
                 const aliasDocList = DocListCast(alias["data-all"]);
                 // if aliasDocList contains the alias, don't do anything
                 // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias
-                !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
+                !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
             });
         }
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 3eece0086..c0553ca60 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,10 +1,10 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, reaction, IReactionDisposer, observable } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
-import { Document } from '../../../fields/Schema';
+import { Document, listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
@@ -215,6 +215,11 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             />
         </div>;
     }
+    childContextMenuItems = () => {
+        const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
+        const filterScripts = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
+        return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: filterScripts[i], label }));
+    }
     @computed get treeViewElements() {
         TraceMobx();
         const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
@@ -247,7 +252,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
             this.whenChildContentsActiveChanged,
             this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null),
             this.observerHeight,
-            this.unobserveHeight);
+            this.unobserveHeight,
+            this.childContextMenuItems()
+        );
     }
     @computed get titleBar() {
         const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e65ebf075..a82128e47 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -70,6 +70,7 @@ export interface CollectionViewProps extends FieldViewProps {
     childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
     childFitWidth?: () => boolean;
     childOpacity?: () => number;
+    childContextMenuItems?: () => { script: ScriptField, label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
     childHideDecorationTitle?: () => boolean;
     childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
@@ -186,15 +187,20 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             }
             !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
 
-            optionItems.push({
-                description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
-            });
-            optionItems.push({
-                description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram"
-            });
-            optionItems.push({
-                description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram"
-            });
+            if (!Doc.UserDoc().noviceMode) {
+                optionItems.push({
+                    description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
+                });
+                optionItems.push({
+                    description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram"
+                });
+                optionItems.push({
+                    description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram"
+                });
+            }
+            if (this.Document._viewType === CollectionViewType.Docking) {
+                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "project-diagram" });
+            }
 
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
 
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 206e48aac..0598be49b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { RichTextField } from '../../../fields/RichTextField';
@@ -54,6 +54,7 @@ export interface TreeViewProps {
     indentDocument?: (editTitle: boolean) => void;
     outdentDocument?: (editTitle: boolean) => void;
     ScreenToLocalTransform: () => Transform;
+    contextMenuItems: { script: ScriptField, filter: ScriptField, label: string }[];
     dontRegisterView?: boolean;
     styleProvider?: StyleProviderFunc | undefined;
     treeViewHideHeaderFields: () => boolean;
@@ -336,7 +337,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive,
                     this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
                     [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged,
-                    this.props.dontRegisterView, emptyFunction, emptyFunction);
+                    this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems());
             } else {
                 contentElement = <EditableView key="editableView"
                     contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
@@ -418,7 +419,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform,
                         this.props.isContentActive, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
                         [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged,
-                        this.props.dontRegisterView, emptyFunction, emptyFunction)}
+                        this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems())}
             </ul >;
         } else if (this.treeViewExpandedView === "fields") {
             return <ul key={this.doc[Id] + this.doc.title}>
@@ -507,11 +508,19 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
     contextMenuItems = () => {
         const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" };
-        return this.doc.isFolder ? [makeFolder] :
+        const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" };
+        const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" };
+        return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? [makeFolder] :
             Doc.IsSystem(this.doc) ? [] :
                 this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
-                    [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }, makeFolder] :
-                    [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }, { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }];
+                    [openAlias, makeFolder] :
+                    this.doc.viewType === CollectionViewType.Docking ? [] :
+                        [openAlias, focusDoc])];
+    }
+    childContextMenuItems = () => {
+        const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
+        const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
+        return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], label }));
     }
     onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
 
@@ -817,7 +826,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         whenChildContentsActiveChanged: (isActive: boolean) => void,
         dontRegisterView: boolean | undefined,
         observerHeight: (ref: any) => void,
-        unobserveHeight: (ref: any) => void
+        unobserveHeight: (ref: any) => void,
+        contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string }[])
     ) {
         const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -882,6 +892,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 parentTreeView={parentTreeView}
                 observeHeight={observerHeight}
                 unobserveHeight={unobserveHeight}
+                contextMenuItems={contextMenuItems}
             />;
         });
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 745d58656..a29f2f662 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -664,7 +664,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
 
         const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
-        Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
+        StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
             cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
         this.props.contextMenuItems?.().forEach(item =>
             item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
-- 
cgit v1.2.3-70-g09d2


From 917ebb4a7b6840f57abbf8dbcc197919f3ee3aac Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Wed, 18 Aug 2021 17:27:07 -0400
Subject: updates towards a menu

---
 src/client/documents/Documents.ts                  |    1 +
 src/client/util/CurrentUserUtils.ts                |    6 +-
 src/client/views/MainView.scss                     |    5 -
 src/client/views/MainView.tsx                      |    2 +
 src/client/views/StyleProvider.tsx                 |    3 +-
 src/client/views/collections/CollectionMenu.scss   | 1255 ++++++++++----------
 src/client/views/collections/CollectionMenu.tsx    |  101 +-
 .../collectionLinearView/CollectionLinearView.scss |   33 +-
 .../collectionLinearView/CollectionLinearView.tsx  |   43 +-
 src/client/views/nodes/DocumentView.tsx            |   29 +-
 src/client/views/nodes/button/FontIconBadge.tsx    |   13 +-
 src/client/views/nodes/button/FontIconBox.scss     |  262 ++--
 src/client/views/nodes/button/FontIconBox.tsx      |   81 +-
 13 files changed, 1022 insertions(+), 812 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 36cbd2059..817fbb9d6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -227,6 +227,7 @@ export class DocumentOptions {
     //LINEAR VIEW
     linearViewIsExpanded?: boolean; // is linear view expanded
     linearViewExpandable?: boolean; // can linear view be expanded
+    linearViewToggleButton?: string; // button to open close linear view group
 
     layout_linkView?: Doc; // view template for a link document
     layout_keyValue?: string; // view tempalte for key value docs
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 620602070..cd4c217b5 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -547,7 +547,7 @@ export class CurrentUserUtils {
             { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
-            { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
+            // { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
             // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
             { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
@@ -594,6 +594,7 @@ export class CurrentUserUtils {
                 title: "menuItemPanel",
                 childDropAction: "alias",
                 _chromeHidden: true,
+                backgroundColor: Colors.DARK_GRAY,
                 boxShadow: "rgba(0,0,0,0)",
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
                 ignoreClick: true,
@@ -998,7 +999,7 @@ export class CurrentUserUtils {
                         onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined
                     }));
                 });
-                docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, _height: 30, backgroundColor: "transparent" }, textDocList));
+                docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, icon:"Text", _height: 30, backgroundColor: "transparent" }, textDocList));
             } else {
                 docList.push(Docs.Create.FontIconDocument({
                     _nativeWidth: width ? width : 30,
@@ -1197,6 +1198,7 @@ export class CurrentUserUtils {
         this.setupSearchSidebar(doc); // sets up the search sidebar
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // documents in overlay layer
+        this.setupContextMenuButtons(doc); // set up context menu buttons
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
         await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index d913f2069..817e45699 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -265,11 +265,6 @@
         height: 35px;
         padding: 5px;
     }
-
-    svg {
-        width: 95% !important;
-        height: 95%;
-    }
 }
 
 .mainView-searchPanel {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6a388c5b4..5c1408148 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -439,6 +439,7 @@ export class MainView extends React.Component {
         this._flyoutWidth = (this._flyoutWidth || 250);
         this._sidebarContent.proto = button.target as any;
         this.LastButton = button;
+        console.log(button.title);
     });
 
     closeFlyout = action(() => {
@@ -446,6 +447,7 @@ export class MainView extends React.Component {
         this._panelContent = "none";
         this._sidebarContent.proto = undefined;
         this._flyoutWidth = 0;
+        console.log("close flyout");
     });
 
     remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index c9e532745..85520f6b3 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -96,6 +96,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             !Doc.IsSystem(doc) && doc.type === DocumentType.RTF ?
                 (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") : "") || "";
         case StyleProp.Color:
+            if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
             if (docColor) return docColor;
             const backColor = backgroundCol();
@@ -114,8 +115,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry].includes(doc?._viewType as any) ||
             doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
         case StyleProp.BackgroundColor: {
+            if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
             let docColor: Opt<string> = StrCast(doc?.[fieldKey + "backgroundColor"], StrCast(doc?._backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : ""));
-            if (MainView.Instance.LastButton === doc) return darkScheme() ? Colors.MEDIUM_GRAY : Colors.LIGHT_GRAY;
             switch (doc?.type) {
                 case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break;
                 case DocumentType.PRES: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index f04b19ef7..163566d22 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -1,628 +1,659 @@
 @import "../global/globalCssVariables";
 
-.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;
+.collectionMenu-container {
     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-content: center;
+    justify-content: space-between;
+    background-color: $dark-gray;
+    height: 35px;
+    border-bottom: $standard-border;
+    padding-right: 5px;
     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 {
+    .collectionMenu-hardCodedButton {
         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;
+        width: 31.5px;
+        height: 90%;
+        padding: 5;
+        text-align: center;
         display: flex;
-        flex-direction: column;
         justify-content: center;
-    }
-
-    .collectionSchemaViewChrome-toggler {
-        width: 100px;
-        height: 35px;
-        background-color: $black;
+        align-items: center;
         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;
+        transition: 0.2s;
 
-        &.on {
-            margin-left: 3px;
-        }
-
-        &.off {
-            margin-left: 50px;
+        &:hover {
+            border-radius:5px;
+            background-color: rgba(0,0,0,0.2);
         }
     }
 }
 
-
-.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
+// .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
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 8f4df4a92..55af4650f 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -15,29 +15,31 @@ import { RichTextField } from "../../../fields/RichTextField";
 import { listSpec } from "../../../fields/Schema";
 import { ScriptField } from "../../../fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { DragManager } from "../../util/DragManager";
 import { Scripting } from "../../util/Scripting";
 import { SelectionManager } from "../../util/SelectionManager";
+import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
 import { EditableView } from "../EditableView";
 import { GestureOverlay } from "../GestureOverlay";
-import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, ActiveArrowStart, ActiveArrowEnd } from "../InkingStroke";
+import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
+import { LightboxView } from "../LightboxView";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { DocumentView } from "../nodes/DocumentView";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
 import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
 import { PresBox } from "../nodes/trails/PresBox";
+import { DefaultStyleProvider } from "../StyleProvider";
+import { CollectionDockingView } from "./CollectionDockingView";
+import { CollectionLinearView } from "./CollectionLinearView";
 import "./CollectionMenu.scss";
 import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { TabDocView } from "./TabDocView";
-import { LightboxView } from "../LightboxView";
-import { Docs } from "../../documents/Documents";
-import { DocumentManager } from "../../util/DocumentManager";
-import { CollectionDockingView } from "./CollectionDockingView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
 
 @observer
 export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -46,6 +48,8 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable SelectedCollection: DocumentView | undefined;
     @observable FieldKey: string;
 
+    private _docBtnRef = React.createRef<HTMLDivElement>();
+
     constructor(props: any) {
         super(props);
         this.FieldKey = "";
@@ -82,30 +86,85 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
         }
     }
 
+    buttonBarXf = () => {
+        if (!this._docBtnRef.current) return Transform.Identity();
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
+        return new Transform(-translateX, -translateY, 1 / scale);
+    }
+
+    @computed get contMenuButtons() {
+        const selDoc = Doc.UserDoc().contextMenuBtns;
+        return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: "35px" }} >
+            <CollectionLinearView
+                Document={selDoc}
+                DataDoc={undefined}
+                fieldKey={"data"}
+                dropAction={"alias"}
+                setHeight={returnFalse}
+                styleProvider={DefaultStyleProvider}
+                layerProvider={undefined}
+                rootSelected={returnTrue}
+                bringToFront={emptyFunction}
+                select={emptyFunction}
+                isContentActive={returnFalse}
+                isSelected={returnFalse}
+                docViewPath={returnEmptyDoclist}
+                moveDocument={returnFalse}
+                CollectionView={undefined}
+                addDocument={returnFalse}
+                addDocTab={returnFalse}
+                pinToPres={emptyFunction}
+                removeDocument={returnFalse}
+                ScreenToLocalTransform={this.buttonBarXf}
+                PanelWidth={() => 100}
+                PanelHeight={() => 35}
+                renderDepth={0}
+                focus={() => undefined}
+                whenChildContentsActiveChanged={emptyFunction}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={undefined} />
+        </div>;
+    }
+
     render() {
-        const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
-            <button className="antimodeMenu-button" onClick={this.toggleMenuPin} style={{ backgroundColor: "#121721" }}>
-                <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
-            </button>
-        </Tooltip>;
 
         const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
         const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel";
 
         const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="properties" placement="bottom">
-            <button className="antimodeMenu-button" key="properties" style={{ backgroundColor: "#424242" }}
+            <div className="collectionMenu-hardCodedButton" key="properties"
                 onPointerDown={this.toggleProperties}>
                 <FontAwesomeIcon icon={propIcon} size="lg" />
-            </button>
+            </div>
         </Tooltip>;
 
-        return this.getElement(!this.SelectedCollection ? [/*button*/] :
-            [<CollectionViewBaseChrome key="chrome"
-                docView={this.SelectedCollection}
-                fieldKey={this.SelectedCollection.LayoutFieldKey}
-                type={StrCast(this.SelectedCollection?.props.Document._viewType, CollectionViewType.Invalid) as CollectionViewType} />,
-                prop,
-                /*button*/]);
+        // NEW BUTTONS
+        //dash col linear view buttons
+        const contMenuButtons = 
+        <div className="collectionMenu-container">
+            {this.contMenuButtons}
+            {prop}
+        </div>;
+
+        return contMenuButtons;
+
+        // const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
+        //     <button className="antimodeMenu-button" onClick={this.toggleMenuPin} style={{ backgroundColor: "#121721" }}>
+        //         <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
+        //     </button>
+        // </Tooltip>;
+
+        // OLD BUTTONS
+        // return this.getElement(!this.SelectedCollection ? [/*button*/] :
+        //     [<CollectionViewBaseChrome key="chrome"
+        //         docView={this.SelectedCollection}
+        //         fieldKey={this.SelectedCollection.LayoutFieldKey}
+        //         type={StrCast(this.SelectedCollection?.props.Document._viewType, CollectionViewType.Invalid) as CollectionViewType} />,
+        //         prop,
+        //         /*button*/]);
     }
 }
 
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
index a10d43917..db39e304b 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
@@ -5,12 +5,22 @@
     overflow: visible;
     height: 100%;
     pointer-events: none;
+    // background-color: rgba(0, 0, 0, 0.2);
+    border-radius: 5px;
+    padding-left: 5px;
+    padding-right: 5px;
+    border-left: $standard-border;
+    border-right: $standard-border;
 
     .collectionLinearView {
         display: flex;
         height: 100%;
         align-items: center;
 
+        .collectionView{
+            overflow: visible !important;
+        }
+
         >span {
             background: $dark-gray;
             color: $white;
@@ -67,29 +77,25 @@
         }
 
         >label {
-            margin-top: "auto";
-            margin-bottom: "auto";
             background: $dark-gray;
             color: $white;
-            display: inline-block;
+            display: flex;
             border-radius: 18px;
             font-size: 12.5px;
-            width: 18px;
-            height: 18px;
+            font-weight:100;
+            width: fit-content;
+            height: 100%;
             margin-top: auto;
             margin-bottom: auto;
             margin-right: 3px;
             cursor: pointer;
             transition: transform 0.2s;
-        }
-
-        label p {
-            padding-left: 5px;
-        }
+            align-items: center;
+            justify-content: center;
 
-        label:hover {
-            background: $medium-gray;
-            transform: scale(1.15);
+            &:hover {
+                background: $medium-gray;
+            }
         }
 
         >input {
@@ -110,7 +116,6 @@
             display: flex;
             opacity: 1;
             position: relative;
-            margin-top: auto;
 
             .collectionLinearView-docBtn,
             .collectionLinearView-docBtn-scalable {
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
index 8e2ba2275..e7970758a 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
@@ -2,20 +2,21 @@ import { Tooltip } from '@material-ui/core';
 import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { makeInterface } from '../../../../fields/Schema';
+import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
 import { documentSchema } from '../../../../fields/documentSchemas';
-import { CollectionSubView } from '../CollectionSubView';
+import { Id } from '../../../../fields/FieldSymbols';
+import { makeInterface } from '../../../../fields/Schema';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../../Utils';
 import { DragManager } from '../../../util/DragManager';
-import { ScriptCast, NumCast, StrCast, BoolCast } from '../../../../fields/Types';
-import { HeightSym, Doc, WidthSym } from '../../../../fields/Doc';
-import { Utils, returnFalse, returnTrue, emptyFunction, returnEmptyDoclist } from '../../../../Utils';
+import { Transform } from '../../../util/Transform';
 import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
+import { DocumentView } from '../../nodes/DocumentView';
 import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
 import { StyleProp } from '../../StyleProvider';
-import { CollectionViewType } from '../CollectionView';
-import { Id } from '../../../../fields/FieldSymbols';
-import { DocumentView } from '../../nodes/DocumentView';
-import { Transform } from '../../../util/Transform';
+import "./CollectionLinearView.scss";
+import { CollectionSubView } from '.././CollectionSubView';
+import { CollectionViewType } from '.././CollectionView';
 
 
 type LinearDocument = makeInterface<[typeof documentSchema,]>;
@@ -108,33 +109,37 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
     render() {
         const guid = Utils.GenerateGuid();
         const flexDir: any = StrCast(this.Document.flexDirection);
+        const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const icon: string = StrCast(this.props.Document.icon);
 
         const menuOpener = <label htmlFor={`${guid}`} style={{ pointerEvents: "all", cursor: "pointer", background: backgroundColor === color ? "black" : backgroundColor, }}
             onPointerDown={e => e.stopPropagation()} >
-            <p>{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "–" : "+"}</p>
+            <p>{BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : "–" : icon ? icon : "+"}</p>
         </label>;
 
         return <div className="collectionLinearView-outer">
             <div className="collectionLinearView" ref={this.createDashEventsTarget} >
-                <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "Close menu" : "Open menu"}</div></>} placement="top">
+                {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close menu" : "Open menu"}</div></>} placement="top">
                     {menuOpener}
-                </Tooltip>
-                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.layoutDoc.linearViewIsExpanded)} ref={this.addMenuToggle}
-                    onChange={action(() => this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
+                </Tooltip>}
+                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
+                    onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
 
                 <div className="collectionLinearView-content" style={{ height: this.dimension(), flexDirection: flexDir }}>
                     {this.childLayoutPairs.map((pair, ind) => {
                         const nested = pair.layout._viewType === CollectionViewType.Linear;
                         const dref = React.createRef<HTMLDivElement>();
-                        const scalable = pair.layout.onClick || pair.layout.onDragStart;
-                        return <div className={`collectionLinearView-docBtn` + (scalable ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
+                        // const scalable = pair.layout.onClick || pair.layout.onDragStart;
+                        console.log("Height:" + NumCast(pair.layout._height));
+                        return <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
                             style={{
                                 pointerEvents: "all",
-                                minWidth: 30,
-                                width: nested ? pair.layout[WidthSym]() : this.dimension(),
-                                height: nested && pair.layout.linearViewIsExpanded ? pair.layout[HeightSym]() : this.dimension(),
+                                width: nested ? undefined : NumCast(pair.layout._width),
+                                height: nested ? undefined : NumCast(pair.layout._height),
+                                // width: NumCast(pair.layout._width),
+                                // height: NumCast(pair.layout._height),
                             }}  >
                             <DocumentView
                                 Document={pair.layout}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a29f2f662..ae3107b08 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -48,6 +48,7 @@ import { RadialMenu } from './RadialMenu';
 import React = require("react");
 import { ScriptingBox } from "./ScriptingBox";
 import { FormattedTextBox } from "./formattedText/FormattedTextBox";
+import { ColorScheme } from "../../util/SettingsManager";
 const { Howl } = require('howler');
 
 interface Window {
@@ -132,7 +133,7 @@ export interface DocumentViewSharedProps {
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
     freezeDimensions?: boolean;
-    hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected 
+    hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
     hideTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDecorationTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     treeViewDoc?: Doc;
@@ -420,13 +421,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     focus = (anchor: Doc, options?: DocFocusOptions) => {
         LightboxView.SetCookie(StrCast(anchor["cookies-set"]));
-        // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
-        Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
-            this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
-        });
+              // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
+              Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
+                this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
+            });        
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
-        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here      
+        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
         const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options, afterFocus: (didFocus: boolean) =>
@@ -764,7 +765,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
-
+        
         if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event
         cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
         DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && setTimeout(() => SelectionManager.SelectView(this.props.DocumentView(), false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
@@ -949,12 +950,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     @computed get renderDoc() {
         TraceMobx();
+        const isButton: boolean = this.props.Document.type === DocumentType.FONTICON;
         if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
         return this.docContents ??
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 id={this.props.Document[Id]}
                 style={{
-                    background: this.backgroundColor,
+                    background: isButton ? undefined : this.backgroundColor,
                     opacity: this.opacity,
                     color: StrCast(this.layoutDoc.color, "inherit"),
                     fontFamily: StrCast(this.Document._fontFamily, "inherit"),
@@ -970,7 +972,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     render() {
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        const highlightColor = (CurrentUserUtils.ActiveDashboard?.darkScheme ?
+        const highlightColor = (Doc.UserDoc().colorScheme === ColorScheme.Dark ?
             ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
             ["transparent", "#4476F7", "#4476F7", "yellow", "magenta", "cyan", "orange"])[highlightIndex];
         const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex];
@@ -982,6 +984,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
         const boxShadow = this.props.treeViewDoc ? null : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
             this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
+
+        // Return surrounding highlight 
         return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
             onContextMenu={this.onContextMenu}
             onKeyDown={this.onKeyDown}
@@ -1142,14 +1146,15 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         TraceMobx();
         const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
         const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
+        const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
         return (<div className="contentFittingDocumentView">
             {!this.props.Document || !this.props.PanelWidth() ? (null) : (
                 <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
                     style={{
                         position: this.props.Document.isInkMask ? "absolute" : undefined,
-                        transform: `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                        width: xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
-                        height: yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
+                        transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
+                        width: isButton ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
+                        height: isButton ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
                             `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
                     }}>
                     <DocumentViewInternal {...this.props}
diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx
index 3be723052..3e451eea6 100644
--- a/src/client/views/nodes/button/FontIconBadge.tsx
+++ b/src/client/views/nodes/button/FontIconBadge.tsx
@@ -1,11 +1,10 @@
-
-import { AclPrivate, Doc, DocListCast } from '../../../../fields/Doc';
-import { GetEffectiveAcl } from '../../../../fields/util';
+import { observer } from "mobx-react";
+import React from "react";
+import { AclPrivate, Doc, DocListCast } from "../../../../fields/Doc";
+import { GetEffectiveAcl } from "../../../../fields/util";
 import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils";
-import { DragManager } from '../../../util/DragManager';
-import './FontIconBox.scss';
-import { observer } from 'mobx-react';
-import React from 'react';
+import { DragManager } from "../../../util/DragManager";
+
 interface FontIconBadgeProps {
     collection: Doc | undefined;
 }
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index d4f1b9379..46a499466 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -1,103 +1,205 @@
 @import "../../global/globalCssVariables";
 
-.fontIconBox-label {
-    color: $white;
-    margin-right: 4px;
-    margin-top: 1px;
-    position: relative;
-    text-align: center;
-    font-size: 7px;
-    letter-spacing: normal;
-    background-color: inherit;
-    border-radius: 8px;
-    margin-top: -8px;
-    padding: 0;
-    width: 100%;
-}
+.menuButton {
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 80%;
+    border-radius: 7px;
 
-.fontIconBadge-container {
-    position:absolute;
-    z-index: 1000;
-    top: 12px;
+    .menuButton-wrap {
+        grid-column: 1;
+        justify-content: center;
+        align-items: center;
+        text-align: center;
+    }
 
-    .fontIconBadge {
-        position: absolute;
-        top: -10px;
-        right: -10px;
+    .fontIconBox-label {
         color: $white;
-        background: $pink;
-        font-weight: 300;
-        border-radius: 100%;
-        width: 25px;
-        height: 25px;
+        position: relative;
         text-align: center;
-        padding-top: 4px;
-        font-size: 12px;
+        font-size: 7px;
+        letter-spacing: normal;
+        background-color: inherit;
+        margin-top: 5px;
+        border-radius: 8px;
+        padding: 0;
+        width: 100%;
+        font-family: 'ROBOTO';
+        text-transform: uppercase;
+        font-weight: bold;
     }
-}
 
-.menuButton-circle,
-.menuButton-round {
-    border-radius: 100%;
-    background-color: $dark-gray;
-    padding: 0;
-
-    .fontIconBox-label {
-        //margin-left: -10px; // button padding is 10px;
-        bottom: 0;
-        position: absolute;
+    .fontIconBox-icon {
+        width: 80%;
+        height: 80%;
     }
 
-    &:hover {
-        background-color: $light-gray;
+    &.clickBtn {
+        cursor: pointer;
     }
-}
 
-.menuButton-square {
-    padding-top: 3px;
-    padding-bottom: 3px;
-    background-color: $dark-gray;
+    &.tglBtn {
+        cursor: pointer;
+    }
 
-    .fontIconBox-label {
+    &.menuBtn {
+        cursor: pointer;
         border-radius: 0px;
-        margin-top: 0px;
-        border-radius: "inherit";
+        flex-direction: column;
+
+        svg {
+            width: 45% !important;
+            height: 45%;
+        }
     }
-}
 
-.menuButton,
-.menuButton-circle,
-.menuButton-round,
-.menuButton-square {
-    margin-left: -5%;
-    width: 110%;
-    height: 100%;
-    pointer-events: all;
-    touch-action: none;
+    &.colorBtn {
+        color: black;
+        cursor: pointer;
+        flex-direction: row;
+        background: transparent;
 
-    .menuButton-wrap {
-        touch-action: none;
-        border-radius: 8px;
-        width: 100%;
+        .menuButton-dropdownBox {
+            position: absolute;
+            width: 150px;
+            height: 250px;
+            top: 100%;
+            z-index: 21;
+            background-color: #e3e3e3;
+            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+            border-radius: 3px;
+        }
     }
 
-    .menuButton-icon-square {
-        width: auto;
-        height: 29px;
-        padding: 4px;
+    &.drpdownList {
+        width: 100px;
+        display: grid;
+        grid-auto-columns: 80px 20px;
+        justify-items: flex-start;
+        padding-left: 10px;
+        font-family: 'Roboto';
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        font-size: 13;
+        font-weight: 600;
+        overflow: hidden;
+        cursor: pointer;
+        background: transparent;
+
+        .menuButton-dropdownList {
+            position: absolute;
+            width: 150px;
+            height: fit-content;
+            top: 100%;
+            z-index: 21;
+            background-color: #e3e3e3;
+            box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+            border-radius: 3px;
+
+            .list-item {
+                color: black;
+                width: 100%;
+                height: 25px;
+                display: flex;
+                justify-content: left;
+                align-items: center;
+                padding-left: 5px;
+            }
+
+            .list-item:hover {
+                background-color:lightgrey;
+            }
+        }
     }
 
-    svg {
-        width: 95% !important;
-        height: 95%;
+    &.drpDownBtn {
+        cursor: pointer;
+        background: transparent;
+
+        &.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: 3px;
+        }
     }
+
 }
-.menuButton-round {
-    width: 100%;
-    svg {
-        width: 50% !important;
-        height: 50%;
-        position: relative;
-        bottom: 2px;
-    }
-}
\ No newline at end of file
+
+
+// .fontIconBadge-container {
+//     position: absolute;
+//     z-index: 1000;
+//     top: 12px;
+
+//     .fontIconBadge {
+//         position: absolute;
+//         top: -10px;
+//         right: -10px;
+//         color: $white;
+//         background: $pink;
+//         font-weight: 300;
+//         border-radius: 100%;
+//         width: 25px;
+//         height: 25px;
+//         text-align: center;
+//         padding-top: 4px;
+//         font-size: 12px;
+//     }
+// }
+
+// .menuButton-circle,
+// .menuButton-round {
+//     border-radius: 100%;
+//     background-color: $dark-gray;
+//     padding: 0;
+
+//     .fontIconBox-label {
+//         z-index: 100;
+//         min-width: fit-content;
+//         bottom: 0;
+//         left: 0;
+//         width: 100%;
+//         /* justify-self: center; */
+//         border-radius: 0px;
+//         position: absolute;
+//     }
+
+//     .menuButton-dropdown {
+//         display: flex;
+//         justify-content: center;
+//         align-items: center;
+//         font-size: 15px;
+//         /* background-color: #b9b9b9; */
+//         grid-column: 2;
+//         border-radius: 0px 7px 7px 0px;
+//         /* position: absolute; */
+//         width: 13px;
+//         height: 100%;
+//         right: 0;
+//     }
+
+//     &:hover {
+//         background-color: $light-gray;
+//     }
+
+//     .dropbox-background {
+//         width: 100vw;
+//         height: 100vh;
+//         top: 0;
+//         z-index: 20;
+//         left: 0;
+//         background:transparent;
+//         position: fixed;
+//     }
+// }
+
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index f98d01dfc..2c6369e9f 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -2,20 +2,19 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import React from 'react';
+import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Doc, StrListCast } from '../../../../fields/Doc';
 import { createSchema, makeInterface } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, StrCast, ScriptCast } from '../../../../fields/Types';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { ColorScheme } from '../../../util/SettingsManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
 import { StyleProp } from '../../StyleProvider';
-import { FieldView, FieldViewProps } from '../FieldView';
-import { FontIconBadge } from './FontIconBadge';
+import { FieldView, FieldViewProps } from '.././FieldView';
 import './FontIconBox.scss';
 const FontIconSchema = createSchema({
     icon: "string",
@@ -31,10 +30,14 @@ export enum ButtonType {
     ColorButton = "colorBtn"
 }
 
+export interface ButtonProps extends FieldViewProps {
+    type?: ButtonType;
+}
+
 type FontIconDocument = makeInterface<[typeof FontIconSchema]>;
 const FontIconDocument = makeInterface(FontIconSchema);
 @observer
-export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(FontIconDocument) {
+export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(FontIconDocument) {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); }
     showTemplate = (): void => {
         const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
@@ -56,8 +59,6 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
     // Determining UI Specs
     @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
-    @observable private color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-    @observable private backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
     @observable private icon = StrCast(this.dataDoc.icon, "user") as any;
     @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen);
     @observable private dropdownDirection: string = StrCast(this.rootDoc.dropDownDirection);
@@ -82,16 +83,18 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
      */
     @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: "black", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
-                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={this.color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: this.color, backgroundColor: this.backgroundColor }}> {this.label} </div>}
+                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                {!this.label || !Doc.UserDoc()._showLabel ? (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={"black"} size="sm" />
+                    <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
                 </div>
                 {this.rootDoc.dropDownOpen ?
                     <div className="menuButton-dropdownBox"
@@ -121,14 +124,16 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
      * 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 active: string = StrCast(this.rootDoc.dropDownOpen);
         return (
             <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu}
                 style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
                 <div className="menuButton-wrap">
                     <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
-                    {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: this.color, backgroundColor: this.backgroundColor }}> {this.label} </div>}
-                    <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
+                    {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                    {/* <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} /> */}
                 </div>
             </div>
         )
@@ -136,6 +141,8 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
 
     render() {
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         // Variables called through eval (from button)
         const canUndo: boolean = UndoManager.CanUndo();
         const canRedo: boolean = UndoManager.CanRedo();
@@ -144,7 +151,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
         const userDoc = Doc.UserDoc();
 
         // Toggle and canClick properties as determined from the variable passed into the button doc
-        // const toggle = this.rootDoc.toggle ? ScriptCast(this.rootDoc.toggle) : undefined;
+        const toggle = this.rootDoc.toggle ? ScriptCast(this.rootDoc.toggle) : undefined;
         const canClick: boolean = this.rootDoc.canClick ? eval(StrCast(this.rootDoc.canClick)) : false;
         // if (toggle) {
         //     console.log(StrCast(this.rootDoc.title), toggle);
@@ -154,11 +161,11 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
 
         const active: string = StrCast(this.rootDoc.dropDownOpen);
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: this.color, backgroundColor: this.backgroundColor }}>
+            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
         const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: this.color, backgroundColor: this.backgroundColor }}>
+            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
                 {this.label}
             </div>;
         const dropdownCaret = <div
@@ -166,7 +173,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
             style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
             <FontAwesomeIcon icon={'caret-down'} color={"black"} size="sm" />
         </div>;
-        const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(this.backgroundColor)}
+        const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(backgroundColor)}
             presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
                 '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
                 '#FFFFFF', '#f1efeb', 'transparent']} />;
@@ -207,9 +214,9 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
             case ButtonType.DropdownList:
                 button = (
                     <div className={`menuButton ${this.type} ${active}`}
-                        style={{ backgroundColor: this.rootDoc.dropDownOpen ? "#aeddf880" : undefined, color: this.rootDoc.dropDownOpen ? "#1c5c96" : "black", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                        style={{ backgroundColor: this.rootDoc.dropDownOpen ? "#aeddf880" : backgroundColor, color: this.rootDoc.dropDownOpen ? "#1c5c96" : color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                         onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}>
-                        {/* {toggle} */}
+                        {toggle}
                         {label}
                         {dropdownCaret}
                         {this.rootDoc.dropDownOpen ?
@@ -227,10 +234,10 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
             case ButtonType.ColorButton:
                 button = (
                     <div className={`menuButton ${this.type} ${active}`}
-                        style={{ opacity: selectedDoc ? 1 : 0.5, color: dark ? "white" : "black", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                        style={{ opacity: selectedDoc ? 1 : 0.5, color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                         onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}
                         onPointerDown={e => e.stopPropagation()}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={dark ? "white" : "black"} />
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                         {dropdownCaret}
                         {this.rootDoc.dropDownOpen ?
@@ -247,40 +254,36 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
                     </div>
                 );
                 break;
-            // case ButtonType.ToggleButton:
-            //     button = (
-            //         <div className={`menuButton ${this.type}`} style={{ opacity: canClick ? 1 : 0.4, backgroundColor: toggle ? this.backgroundColor : undefined }}>
-            //             <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={toggle ? this.color : this.backgroundColor} />
-            //             {label}
-            //         </div>
-            //     );
-            //     break;
+            case ButtonType.ToggleButton:
+                button = (
+                    <div className={`menuButton ${this.type}`} style={{ opacity: canClick ? 1 : 0.4, backgroundColor: toggle ? backgroundColor : undefined }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={toggle ? color : backgroundColor} />
+                        {label}
+                    </div>
+                );
+                break;
             case ButtonType.ClickButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: this.color, backgroundColor: this.backgroundColor, opacity: canClick ? 1 : 0.4 }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={this.color} />
+                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: canClick ? 1 : 0.4 }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
                 );
                 break;
             case ButtonType.DoubleButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: this.color, backgroundColor: this.backgroundColor }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={this.color} />
+                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
                 );
                 break;
             case ButtonType.MenuButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: dark ? "white" : "black", backgroundColor: dark ? "black" : "lightgrey" }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={dark ? "white" : "black"} />
+                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {menuLabel}
                     </div >
-                    // <div className={`menuButton ${type}`} style={{ color, backgroundColor }}>
-                    //     <FontAwesomeIcon className={`fontIconBox-icon-${type}`} icon={icon} color={color} />
-                    //     {label}
-                    // </div>
                 );
                 break;
             default:
-- 
cgit v1.2.3-70-g09d2


From 0c5a95f37e91a07041699514155fd214f544a0de Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Tue, 24 Aug 2021 19:06:43 -0400
Subject: updates

---
 src/client/util/CurrentUserUtils.ts            | 14 ++++++-----
 src/client/views/MainView.tsx                  |  2 +-
 src/client/views/nodes/button/FontIconBox.scss | 15 ++++++------
 src/client/views/nodes/button/FontIconBox.tsx  | 32 ++++++++++++++++++--------
 4 files changed, 40 insertions(+), 23 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 8044f59f0..20274672a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -967,11 +967,11 @@ export class CurrentUserUtils {
 
     static inkTools(doc: Doc) {
         const tools:Button[] = [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")'},
-            { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' },
-            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")' },
-            { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")' },
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', script: 'setActiveInkTool("pen" , true)'},
+            { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', script:'setActiveInkTool("highlighter", true)' },
+            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', script: 'setActiveInkTool("circle" , true)' },
+            { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', script: 'setActiveInkTool("square" , true)' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' },
             { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, ignoreClick: true, script: 'setStrokeWidth'},
         ];
         return tools;
@@ -987,7 +987,7 @@ export class CurrentUserUtils {
                 width: 50,
                 switchToggle: true, 
                 icon: "eye", 
-                click: 'toggleSchemaShow()', 
+                ignoreClick: true, 
                 script: 'toggleSchemaShow' 
             },
         ];
@@ -1071,6 +1071,7 @@ export class CurrentUserUtils {
                         ignoreClick: ignoreClick,
                         _stayInCollection: true,
                         _hideContextMenu: true,
+                        _lockedPosition: true,
                         system: true,
                         dontUndo: true,
                         title,
@@ -1097,6 +1098,7 @@ export class CurrentUserUtils {
                     ignoreClick: ignoreClick,
                     _stayInCollection: true,
                     _hideContextMenu: true,
+                    _lockedPosition: true,
                     system: true,
                     dontUndo: true,
                     title,
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5c1408148..75c57909d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -331,7 +331,7 @@ export class MainView extends React.Component {
                         PanelHeight={this.getContentsHeight}
                         renderDepth={0}
                         isContentActive={returnTrue}
-                        scriptContext={CollectionDockingView.Instance.props.Document}
+                        scriptContext={CollectionDockingView.Instance?.props.Document}
                         focus={DocUtils.DefaultFocus}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index 7a6887d38..dfb549b95 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -240,7 +240,9 @@
             flex-direction: column;
             background: transparent;
 
-            .numberBtn-slider {}
+            .menu-slider {
+                width: 100px;
+            }
 
             .menuButton-dropdownBox {
                 position: absolute;
@@ -255,20 +257,20 @@
         }
 
         .button {
-            width: 30%;
+            width: 25%;
             display: flex;
             align-items: center;
             justify-content: center;
 
             &.number {
-                width: 40%;
+                width: 50%;
 
                 .button-input {
                     background: none;
                     border: none;
                     text-align: right;
                     width: 100%;
-                    color: black;
+                    color: $white;
                     height: 100%;
                     text-align: center;
                 }
@@ -288,9 +290,9 @@
                 position: absolute;
                 width: fit-content;
                 height: fit-content;
-                min-width: 50px;
+                min-width: 50%;
                 max-height: 50vh;
-                overflow: scroll;
+                overflow-y: scroll;
                 top: 100%;
                 z-index: 21;
                 background-color: $white;
@@ -323,7 +325,6 @@
         &:hover {
             background-color: $close-red;
         }
-
     }
 
     &.drpDownBtn {
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 4778c2f10..744064b9f 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -25,6 +25,7 @@ import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
 import './FontIconBox.scss';
+import { SetActiveInkWidth } from '../../InkingStroke';
 const FontIconSchema = createSchema({
     icon: "string",
 });
@@ -177,7 +178,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                     {this.rootDoc.dropDownOpen ?
                         <div>
                             <div className="menuButton-dropdownList"
-                                style={{ left: 0 }}>
+                                style={{ left: "25%" }}>
                                 {list}
                             </div>
                             <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
@@ -383,12 +384,19 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle);
 
         // Script for running the toggle
-        const script: string = StrCast(this.rootDoc.script) + "()";
+        const script: string = StrCast(this.rootDoc.script);
+
         // Script for checking the outcome of the toggle
-        const checkScript: string = StrCast(this.rootDoc.script) + "(true)";
+        let checkScript:string;
+        if (StrCast(this.rootDoc.script).includes("(")){
+            checkScript = StrCast(this.rootDoc.script)
+
+        } else {
+            checkScript = StrCast(this.rootDoc.script) + "(true)";
+        }
 
         // Function to run the script
-        const runScript = () => ScriptField.MakeScript(script)?.script.run();
+        const runScript = () => ScriptField.MakeScript(script + "()")?.script.run();
         const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result;
 
         // Colors
@@ -456,9 +464,10 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         return (
             <div className={`menuButton ${this.type}`}>
-                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
+                HELLO
+                {/* <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
                 <EditableView GetValue={() => ""} SetValue={setValue} contents="...">
-                </EditableView>
+                </EditableView> */}
             </div>
         );
     }
@@ -492,6 +501,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         switch (this.type) {
             case ButtonType.EditableText:
+                console.log("Editable text");
                 button = this.editableText;
             case ButtonType.NumberButton:
                 button = this.numberButton;
@@ -657,7 +667,11 @@ Scripting.addGlobal(function setActiveInkTool(tool: InkTool, checkResult?: boole
         return Doc.UserDoc().activeInkTool === tool;
     }
     if (tool) {
-        Doc.UserDoc().activeInkTool = tool;
+        if (Doc.UserDoc().activeInkTool === tool){
+            Doc.UserDoc().activeInkTool = InkTool.None;
+        } else {
+            Doc.UserDoc().activeInkTool = tool;
+        }
     } else {
         Doc.UserDoc().activeInkTool = InkTool.None;
     }
@@ -668,6 +682,6 @@ Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean
         const width: number = NumCast(Doc.UserDoc().activeInkWidth);
         return width;
     }
-    console.log("[width]: " + width);
-    Doc.UserDoc().activeInkWidth = width;
+    SetActiveInkWidth(StrCast(width));
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(width));
 });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From bc90c2b624733c7175f9859a0f1fa3040dafe74b Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Tue, 24 Aug 2021 20:15:20 -0400
Subject: updates (final)

---
 src/client/util/CurrentUserUtils.ts | 2 --
 src/client/views/MainView.tsx       | 2 +-
 2 files changed, 1 insertion(+), 3 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 20274672a..bae4e3825 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1071,7 +1071,6 @@ export class CurrentUserUtils {
                         ignoreClick: ignoreClick,
                         _stayInCollection: true,
                         _hideContextMenu: true,
-                        _lockedPosition: true,
                         system: true,
                         dontUndo: true,
                         title,
@@ -1098,7 +1097,6 @@ export class CurrentUserUtils {
                     ignoreClick: ignoreClick,
                     _stayInCollection: true,
                     _hideContextMenu: true,
-                    _lockedPosition: true,
                     system: true,
                     dontUndo: true,
                     title,
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 75c57909d..5c1408148 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -331,7 +331,7 @@ export class MainView extends React.Component {
                         PanelHeight={this.getContentsHeight}
                         renderDepth={0}
                         isContentActive={returnTrue}
-                        scriptContext={CollectionDockingView.Instance?.props.Document}
+                        scriptContext={CollectionDockingView.Instance.props.Document}
                         focus={DocUtils.DefaultFocus}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
-- 
cgit v1.2.3-70-g09d2


From 685f31eee39ba076c659fb90c5cb9e29d2d0e4dc Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Tue, 24 Aug 2021 23:27:24 -0400
Subject: Revert "updates (final)"

This reverts commit bc90c2b624733c7175f9859a0f1fa3040dafe74b.
---
 src/client/util/CurrentUserUtils.ts | 2 ++
 src/client/views/MainView.tsx       | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index bae4e3825..20274672a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1071,6 +1071,7 @@ export class CurrentUserUtils {
                         ignoreClick: ignoreClick,
                         _stayInCollection: true,
                         _hideContextMenu: true,
+                        _lockedPosition: true,
                         system: true,
                         dontUndo: true,
                         title,
@@ -1097,6 +1098,7 @@ export class CurrentUserUtils {
                     ignoreClick: ignoreClick,
                     _stayInCollection: true,
                     _hideContextMenu: true,
+                    _lockedPosition: true,
                     system: true,
                     dontUndo: true,
                     title,
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5c1408148..75c57909d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -331,7 +331,7 @@ export class MainView extends React.Component {
                         PanelHeight={this.getContentsHeight}
                         renderDepth={0}
                         isContentActive={returnTrue}
-                        scriptContext={CollectionDockingView.Instance.props.Document}
+                        scriptContext={CollectionDockingView.Instance?.props.Document}
                         focus={DocUtils.DefaultFocus}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
-- 
cgit v1.2.3-70-g09d2


From 6376a4b6d6a730c6c170fa25dd5f19dea6b8f82a Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Wed, 25 Aug 2021 05:20:50 -0400
Subject: many updates

---
 src/client/documents/Documents.ts                  |   4 +-
 src/client/util/CurrentUserUtils.ts                |  73 ++++---
 src/client/util/LinkManager.ts                     |   7 +-
 src/client/util/Scripting.ts                       |   6 -
 src/client/views/EditableView.tsx                  |   2 +
 src/client/views/GestureOverlay.tsx                |   5 +-
 src/client/views/InkingStroke.tsx                  |   4 +-
 src/client/views/MainView.tsx                      |   1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   2 +-
 .../collectionLinearView/CollectionLinearView.tsx  |   3 +-
 src/client/views/nodes/DocumentView.tsx            |  12 +-
 src/client/views/nodes/button/FontIconBox.scss     |   3 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 238 ++++++++++++++-------
 .../views/nodes/formattedText/RichTextMenu.tsx     |   1 +
 src/fields/InkField.ts                             |   6 +-
 15 files changed, 234 insertions(+), 133 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1c4b5218c..f3a472329 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -698,14 +698,14 @@ export namespace Docs {
             return linkDoc;
         }
 
-        export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+        export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
             const I = new Doc();
             I[Initializing] = true;
             I.type = DocumentType.INK;
             I.layout = InkingStroke.LayoutString("data");
             I.color = color;
             I.fillColor = fillColor;
-            I.strokeWidth = Number(strokeWidth);
+            I.strokeWidth = strokeWidth;
             I.strokeBezier = strokeBezier;
             I.strokeStartMarker = arrowStart;
             I.strokeEndMarker = arrowEnd;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 20274672a..7d1cbebdf 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -946,21 +946,19 @@ export class CurrentUserUtils {
                 title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
                 list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia",
                     "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"],
-                script: 'changeFont'
+                script: 'setFont'
             },
-            { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'changeFontSize'},
+            { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize'},
+            { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script:'setFontColor' },
             { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' },
             { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' },
             { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' },
             // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
             // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
             // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
-            { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", ignoreClick: true, script:'changeAlignment("left")' },
-            { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", ignoreClick: true, script:'changeAlignment("center")' },
-            { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", ignoreClick: true, script:'changeAlignment("right")' },
-
-            { title: "Highlight", toolTip: "Highlight", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script:'changeFontColor' },
-            { title: "Text color", toolTip: "Text color", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, script:'changeHighlightColor' },
+            { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", ignoreClick: true, script:'setAlignment("left")' },
+            { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", ignoreClick: true, script:'setAlignment("center")' },
+            { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", ignoreClick: true, script:'setAlignment("right")' },
         ];
         return tools;
     }
@@ -971,8 +969,10 @@ export class CurrentUserUtils {
             { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', script:'setActiveInkTool("highlighter", true)' },
             { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', script: 'setActiveInkTool("circle" , true)' },
             { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', script: 'setActiveInkTool("square" , true)' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' },
             { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, ignoreClick: true, script: 'setStrokeWidth'},
+            { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "minus", ignoreClick: true, script:'setStrokeColor' },
+
         ];
         return tools;
     }
@@ -1015,18 +1015,18 @@ export class CurrentUserUtils {
                     CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
                     CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
                     CollectionViewType.Grid],
-                script: 'changeView',
-            },
+                script: 'setView',
+            }, // Always show
             {
                 title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
-                script: "changeBackgroundColor"
-            },
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', script:'toggleOverlay'},
-            { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy" },
-            { title: "Text", type: "textTools", subMenu: true },
-            { title: "Ink & GFX", type: "inkTools", subMenu: true },
-            { title: "Web", type: "webTools", subMenu: true },
-            { title: "Schema", type: "schemaTools", subMenu: true }
+                script: "setBackgroundColor", hidden:'selectedDocumentType()'
+            }, // Only when a document is selected
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', script:'toggleOverlay', hidden:'selectedDocumentType(undefined, "freeform", true)'}, // Only when floating document is selected in freeform
+            { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden:'selectedDocumentType()' }, // Only when a document is selected 
+            { title: "Text", type: "textTools", subMenu: true, expanded:'selectedDocumentType("rtf")' }, // Always available
+            { title: "Ink & GFX", type: "inkTools", subMenu: true, expanded:'selectedDocumentType("ink")' }, // Always available
+            { title: "Web", type: "webTools", subMenu: true, hidden:'selectedDocumentType("web")' }, // Only when Web is selected
+            { title: "Schema", type: "schemaTools", subMenu: true, hidden:'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected
         ];
     }
 
@@ -1034,7 +1034,7 @@ export class CurrentUserUtils {
     static async setupContextMenuButtons(doc: Doc) {
         const docList: Doc[] = [];
 
-        (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu }) => {
+        (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
             const menuDocList: Doc[] = [];
             if (subMenu) {
                 // default is textTools
@@ -1083,7 +1083,17 @@ export class CurrentUserUtils {
                         onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined
                     }));
                 });
-                docList.push(CurrentUserUtils.linearButtonList({ linearViewSubMenu: true, flexGap: 5, ignoreClick: true, linearViewExpandable: true, icon:title, _height: 30, backgroundColor: "transparent" }, menuDocList));
+                docList.push(CurrentUserUtils.linearButtonList({ 
+                    linearViewSubMenu: true, 
+                    flexGap: 5, 
+                    ignoreClick: true, 
+                    linearViewExpandable: true, 
+                    icon:title, 
+                    _height: 30, 
+                    backgroundColor: "transparent",
+                    linearViewIsExpanded: expanded ? ComputedField.MakeFunction(expanded) as any : undefined,
+                    hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
+                }, menuDocList));
             } else {
                 docList.push(Docs.Create.FontIconDocument({
                     _nativeWidth: width ? width : 25,
@@ -1105,6 +1115,7 @@ export class CurrentUserUtils {
                     color: Colors.WHITE,
                     backgroundColor: "transparent",
                     _dropAction: "alias",
+                    hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
                     _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
                     onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined
                 }));
@@ -1177,7 +1188,7 @@ export class CurrentUserUtils {
         }
         if (doc.myImportPanel === undefined) {
             const uploads = Cast(doc.myImportDocs, Doc, null);
-            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", type: ButtonType.ToolButton, icon: "upload", system: true });
+            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ToolButton, icon: "upload", system: true });
             doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" }));
         }
     }
@@ -1257,8 +1268,8 @@ export class CurrentUserUtils {
         doc._raiseWhenDragged = true;
         doc._showLabel = false;
         doc._showMenuLabel = true;
-        doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
-        doc.activeInkWidth = StrCast(doc.activeInkWidth, "1");
+        doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0, 0)");
+        doc.activeInkWidth = NumCast(doc.activeInkWidth, 1);
         doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
         doc.activeFillColor = StrCast(doc.activeFillColor, "");
         doc.activeArrowStart = StrCast(doc.activeArrowStart, "");
@@ -1279,7 +1290,7 @@ export class CurrentUserUtils {
         doc.filterDocCount = 0;
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
-        this.setupImportSidebar(doc);
+        this.setupImportSidebar(doc); // sets up the import sidebar
         this.setupSearchSidebar(doc); // sets up the search sidebar
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // documents in overlay layer
@@ -1567,3 +1578,15 @@ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
     }
     return [];
 });
+Scripting.addGlobal(function selectedDocumentType(docType?:DocumentType, colType?:CollectionViewType, checkParent?:boolean) {
+    let selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    if (selected && checkParent){
+        const parentDoc: Doc = Cast(selected.context, Doc, null);
+        selected = parentDoc;
+    }
+    console.log(selected, docType, colType);
+    if (selected && docType && selected.type === docType) return false;
+    else if (selected && colType && selected.viewType === colType) return false;
+    else if (selected && !colType && !docType) return false;
+    else return true;
+});
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 3579083e4..219c83b12 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -107,7 +107,12 @@ export class LinkManager {
 
     public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor
     public getAllDirectLinks(anchor: Doc): Doc[] {
-        return Array.from(Doc.GetProto(anchor)[DirectLinksSym]);
+        // FIXME:glr Why is Doc undefined?
+        if (Doc.GetProto(anchor)[DirectLinksSym]){
+            return Array.from(Doc.GetProto(anchor)[DirectLinksSym]);
+        } else {
+            return [];
+        }
     } // finds all links that contain the given anchor
 
     relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index aa7c23527..efee37419 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -171,18 +171,12 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
             if (!options.editable) {
                 batch = Doc.MakeReadOnly();
             }
-            const result1 = compiledFunction.apply(thisParam, params);
             
             const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray);
             if (batch) {
                 batch.end();
             }
 
-            if (script.includes('toggleOverlay(true)')){
-                console.log("[Scripting.ts] r1: ", result1);
-                console.log("[Scripting.ts]: ", result);
-            }
-
             return { success: true, result };
         } catch (error) {
 
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index d5f8d3fcf..83336c180 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -155,6 +155,8 @@ export class EditableView extends React.Component<EditableProps> {
         return wasFocused !== this._editing;
     }
 
+    
+
     renderEditor() {
         console.log("render editor", this.props.autosuggestProps);
         return this.props.autosuggestProps
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index bbf21f22c..24f9501ce 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -14,6 +14,7 @@ import { DocUtils } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { InteractionUtils } from "../util/InteractionUtils";
 import { Scripting } from "../util/Scripting";
+import { SelectionManager } from "../util/SelectionManager";
 import { Transform } from "../util/Transform";
 import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
 import "./GestureOverlay.scss";
@@ -23,7 +24,6 @@ import { RadialMenu } from "./nodes/RadialMenu";
 import HorizontalPalette from "./Palette";
 import { Touchable } from "./Touchable";
 import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
-import { SelectionManager } from "../util/SelectionManager";
 
 @observer
 export class GestureOverlay extends Touchable {
@@ -588,8 +588,9 @@ export class GestureOverlay extends Touchable {
                 this.makePolygon(this.InkShape, false);
                 this.dispatchGesture(GestureUtils.Gestures.Stroke);
                 this._points = [];
-                if (!CollectionFreeFormViewChrome.Instance._keepPrimitiveMode) {
+                if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
                     this.InkShape = "";
+                    Doc.UserDoc().activeInkTool = InkTool.None;
                 }
             }
             // if we're not drawing in a toolglass try to recognize as gesture
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 5fc159f14..db09849fd 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -5,7 +5,7 @@ import { Doc } from "../../fields/Doc";
 import { documentSchema } from "../../fields/documentSchemas";
 import { InkData, InkField, InkTool } from "../../fields/InkField";
 import { makeInterface } from "../../fields/Schema";
-import { Cast, StrCast } from "../../fields/Types";
+import { Cast, StrCast, NumCast } from "../../fields/Types";
 import { TraceMobx } from "../../fields/util";
 import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils";
 import { CognitiveServices } from "../cognitive_services/CognitiveServices";
@@ -168,7 +168,7 @@ export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activ
 export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); }
 export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); }
 export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); }
-export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); }
+export function ActiveInkWidth(): number { return NumCast(ActiveInkPen()?.activeInkWidth); }
 export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); }
 Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
     CurrentUserUtils.SelectedTool = pen ? InkTool.Highlighter : InkTool.None;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 75c57909d..dcdf6b9c3 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -447,7 +447,6 @@ export class MainView extends React.Component {
         this._panelContent = "none";
         this._sidebarContent.proto = undefined;
         this._flyoutWidth = 0;
-        console.log("close flyout");
     });
 
     remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d09d9b9d7..3c5a63c77 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -506,7 +506,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
                 const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
-                    { title: "ink stroke", x: B.x - Number(ActiveInkWidth()) / 2, y: B.y - Number(ActiveInkWidth()) / 2, _width: B.width + Number(ActiveInkWidth()), _height: B.height + Number(ActiveInkWidth()) });
+                    { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
                 this.addDocument(inkDoc);
                 e.stopPropagation();
                 break;
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
index 990c2e4b2..5c16f8929 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
@@ -147,8 +147,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
                     {this.childLayoutPairs.map((pair, ind) => {
                         const nested = pair.layout._viewType === CollectionViewType.Linear;
                         const dref = React.createRef<HTMLDivElement>();
+                        const hidden = pair.layout.hidden === true;
                         // const scalable = pair.layout.onClick || pair.layout.onDragStart;
-                        return <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
+                        return hidden ? (null) : <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
                             style={{
                                 pointerEvents: "all",
                                 width: nested ? undefined : NumCast(pair.layout._width),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 6ab25dd6a..5d9f9c6db 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -13,7 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty
 import { AudioField } from "../../../fields/URLField";
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, OmitKeys, returnVal, Utils, returnTrue } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -25,6 +25,7 @@ import { InteractionUtils } from '../../util/InteractionUtils';
 import { LinkManager } from '../../util/LinkManager';
 import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from "../../util/SelectionManager";
+import { ColorScheme } from "../../util/SettingsManager";
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from "../../util/Transform";
@@ -41,14 +42,13 @@ import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView
 import { DocumentContentsView } from "./DocumentContentsView";
 import { DocumentLinksButton } from './DocumentLinksButton';
 import "./DocumentView.scss";
+import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import { LinkAnchorBox } from './LinkAnchorBox';
 import { LinkDocPreview } from "./LinkDocPreview";
-import { PresBox } from './trails/PresBox';
 import { RadialMenu } from './RadialMenu';
-import React = require("react");
 import { ScriptingBox } from "./ScriptingBox";
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
-import { ColorScheme } from "../../util/SettingsManager";
+import { PresBox } from './trails/PresBox';
+import React = require("react");
 const { Howl } = require('howler');
 
 interface Window {
@@ -795,7 +795,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             </div>;
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: (this.props.contentPointerEvents as any) || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none"),
+                pointerEvents: (this.props.contentPointerEvents as any), // || (CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() || this.isContentActive() ? "all" : "none")
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             <DocumentContentsView key={1} {...this.props}
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index dfb549b95..8de97ea89 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -167,6 +167,7 @@
             position: absolute;
             width: fit-content;
             height: fit-content;
+            color: black;
             top: 100%;
             z-index: 21;
             background-color: #e3e3e3;
@@ -316,7 +317,7 @@
         }
     }
 
-    &.editableTxt {
+    &.editableText {
         cursor: pointer;
         background: transparent;
         width: 100%;
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 744064b9f..53cd017b0 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -25,7 +25,9 @@ import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
 import './FontIconBox.scss';
-import { SetActiveInkWidth } from '../../InkingStroke';
+import { SetActiveInkWidth, SetActiveInkColor } from '../../InkingStroke';
+import { GestureOverlay } from '../../GestureOverlay';
+import { WebField } from '../../../../fields/URLField';
 const FontIconSchema = createSchema({
     icon: "string",
 });
@@ -40,7 +42,7 @@ export enum ButtonType {
     ColorButton = "colorBtn",
     ToolButton = "toolBtn",
     NumberButton = "numBtn",
-    EditableText = "editableTxt"
+    EditableText = "editableText"
 }
 
 export enum NumButtonType {
@@ -115,6 +117,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             const dropdown =
                 <div
                     className="menuButton-dropdownBox"
+                    onPointerDown={e => e.stopPropagation()}
                     style={{ left: 0 }}
                 >
                     <input type="range" step="1" min="0" max="100" value={checkResult}
@@ -240,7 +243,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         let icon: IconProp = "caret-down";
 
 
-        if (script === 'changeView') {
+        if (script === 'setView') {
             const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
             if (selected && StrCast(selected.Document.type) === DocumentType.COL) {
                 text = StrCast(selected.Document._viewType);
@@ -249,23 +252,24 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 text = StrCast(selected.Document.type);
                 icon = Doc.toIcon(selected.Document);
             } else {
+                dropdown = false;
                 text = "None selected";
             }
             noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
-        } else if (script === 'changeFont') {
+        } else if (script === 'setFont') {
             const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
             if (selected && StrCast(selected.Document.type) === DocumentType.RTF) {
                 text = StrCast(selected.Document._fontFamily);
             } else {
                 const fontFamily = StrCast(Doc.UserDoc()._fontFamily);
-                console.log(fontFamily);
                 text = fontFamily;
             }
             noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
                 "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
         }
 
-        const items = this.buttonList.map((value) => {
+        // Get items to place into the list
+        const list = this.buttonList.map((value) => {
             if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
                 return;
             }
@@ -277,7 +281,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             };
             return <div className="list-item" key={`${value}`}
                 style={{
-                    fontFamily: script === 'changeFont' ? value : undefined,
+                    fontFamily: script === 'setFont' ? value : undefined,
                     backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined
                 }}
                 onClick={click}>
@@ -292,22 +296,21 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         return (
             <div className={`menuButton ${this.type} ${active}`}
-                style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
                 onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
+                {dropdown ? (null) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
                 <div className="menuButton-dropdown-header">
                     {text && text[0].toUpperCase() + text.slice(1)}
                 </div>
                 {label}
-                <div
-                    className="menuButton-dropDown"
-                    style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
+                {!dropdown ? (null) : <div className="menuButton-dropDown">
                     <FontAwesomeIcon icon={icon} color={color} size="sm" />
-                </div>
+                </div>}
                 {this.rootDoc.dropDownOpen ?
                     <div>
                         <div className="menuButton-dropdownList"
                             style={{ left: 0 }}>
-                            {items}
+                            {list}
                         </div>
                         <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
                     </div>
@@ -323,29 +326,38 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         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 numSelected = SelectionManager.Views().length;
-        const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined;
 
         const script: string = StrCast(this.rootDoc.script);
         const scriptCheck: string = script + "(undefined, true)";
         const boolResult = ScriptField.MakeScript(scriptCheck)?.script.run().result;
-        console.log("[boolResul]: ", boolResult);
+
+        let stroke: boolean = false;
+        let strokeIcon: any;
+        if (script === "setStrokeColor"){
+            stroke = true;
+            const checkWidth = ScriptField.MakeScript("setStrokeWidth(0, true)")?.script.run().result;
+            strokeIcon = (<div style={{borderRadius: "100%", width: Number(checkWidth)+'%', height: Number(checkWidth)+'%', backgroundColor: boolResult }}/>)
+        }
 
         const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
             '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-            '#FFFFFF', '#f1efeb', 'transparent'];
+            '#FFFFFF', '#f1efeb'];
 
-        const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={selectedDoc ? selectedDoc._backgroundColor : backgroundColor}
+        const colorBox = (func: (color: ColorState) => void) => <SketchPicker 
+            disableAlpha={!stroke}
+            onChange={func} color={boolResult ? boolResult : "#FFFFFF"}
             presetColors={colorOptions} />;
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
             <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
+
         const dropdownCaret = <div
             className="menuButton-dropDown"
             style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
             <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
         </div>;
+
         const click = (value: ColorState) => {
             const hex: string = value.hex;
             const s = ScriptField.MakeScript(script + '("' + hex + '", false)');
@@ -358,10 +370,10 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}
                 onPointerDown={e => e.stopPropagation()}>
-                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                {stroke ? strokeIcon : <><FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                 <div className="colorButton-color"
-                    style={{ backgroundColor: selectedDoc ? selectedDoc._backgroundColor : backgroundColor }}
-                ></div>
+                    style={{ backgroundColor: boolResult ? boolResult : "#FFFFFF" }}
+                ></div></>}
                 {label}
                 {/* {dropdownCaret} */}
                 {this.rootDoc.dropDownOpen ?
@@ -408,7 +420,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
-        console.log("switchToggle", switchToggle);
+
         if (switchToggle) {
             return (
                 <div className={`menuButton ${this.type} ${'switch'}`}
@@ -457,17 +469,24 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
     }
 
     @computed get editableText() {
+        // Script for running the toggle
+        const script: string = StrCast(this.rootDoc.script);
+
+        // Script for checking the outcome of the toggle
+        let checkScript:string = StrCast(this.rootDoc.script) + "('', true)";
+
+        // Function to run the script
+        const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result;
+
         const setValue = (value: string, shiftDown?: boolean): boolean => {
-            console.log("setValue");
+            ScriptField.MakeScript(script + "('"+value+"')")?.script.run();
             return true;
         };
-
         return (
-            <div className={`menuButton ${this.type}`}>
-                HELLO
-                {/* <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
-                <EditableView GetValue={() => ""} SetValue={setValue} contents="...">
-                </EditableView> */}
+            <div className="menuButton editableText">
+                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
+                <EditableView GetValue={() => checkResult} SetValue={setValue} contents="...">
+                </EditableView>
             </div>
         );
     }
@@ -477,12 +496,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         // Variables called through eval (from button)
-        const canUndo: boolean = UndoManager.CanUndo();
-        const canRedo: boolean = UndoManager.CanRedo();
         const numSelected = SelectionManager.Views().length;
-        const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined;
-        const userDoc = Doc.UserDoc();
-
 
         const dark: boolean = Doc.UserDoc().colorScheme === ColorScheme.Dark;
 
@@ -503,6 +517,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             case ButtonType.EditableText:
                 console.log("Editable text");
                 button = this.editableText;
+                break;
             case ButtonType.NumberButton:
                 button = this.numberButton;
                 break;
@@ -534,14 +549,6 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                     </div>
                 );
                 break;
-            case ButtonType.DoubleButton:
-                button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                        {label}
-                    </div>
-                );
-                break;
             case ButtonType.MenuButton:
                 button = (
                     <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
@@ -554,7 +561,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 break;
         }
 
-        return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton ? button :
+        return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button :
             <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
                 {button}
             </Tooltip>;
@@ -563,18 +570,25 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeView(view: string) {
+Scripting.addGlobal(function setView(view: string) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeBackgroundColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
     if (checkResult) {
-        return selected && selected.Document._backgroundColor;
+        if (selected) {
+            console.log("[Background] (selected): " + StrCast(selected._backgroundColor));
+            return selected._backgroundColor;
+        } else {
+            return "#FFFFFF";
+        }
     }
-    selected ? selected.Document._backgroundColor = color : console.log("[FontIconBox.tsx] changeBackgroundColor failed");
+    if (selected && selected.type === DocumentType.INK) selected.fillColor = color;
+    if (selected) selected._backgroundColor = color;
+    Doc.UserDoc()._fontColor = color;
 });
 
 // toggle: Set overlay status of selected document
@@ -586,52 +600,58 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
 });
 
+/** TEXT
+ *      setFont
+ *      setFontSize
+ *      toggleBold
+ *      toggleUnderline
+ *      toggleItalic
+ **/ 
+
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeFont(font: string) {
+Scripting.addGlobal(function setFont(font: string) {
     SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font);
     Doc.UserDoc()._fontFamily = font;
-    console.log(Doc.UserDoc()._fontFamily);
     return Doc.UserDoc()._fontFamily;
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeFontColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
     if (checkResult) {
         if (selected) {
-            console.log("[Font color] (selected): " + StrCast(selected.Document._fontColor));
-            return selected.Document._fontColor;
+            console.log("[Font color] (selected): " + StrCast(selected._fontColor));
+            return selected._fontColor;
         } else {
             console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor));
             return Doc.UserDoc()._fontColor;
         }
     }
+    if (selected) selected._fontColor = color;
     Doc.UserDoc()._fontColor = color;
 });
 
-// toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeFontHighlight(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    if (checkResult) {
-        if (selected) {
-            console.log("[Font color] (selected): " + StrCast(selected.Document._fontColor));
-            return selected.Document._fontColor;
-        } else {
-            console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor));
-            return Doc.UserDoc()._fontColor;
-        }
-    }
-    Doc.UserDoc()._fontColor = color;
-});
+// // toggle: Set overlay status of selected document
+// Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
+//     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+//     if (checkResult) {
+//         if (selected) {
+//             return selected.Document._fontColor;
+//         } else {
+//             return Doc.UserDoc()._fontColor;
+//         }
+//     }
+//     Doc.UserDoc()._fontColor = color;
+// });
+
 
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeFontSize(size: string, checkResult?: boolean) {
+Scripting.addGlobal(function setFontSize(size: string, checkResult?: boolean) {
     if (checkResult) {
         const size: number = parseInt(StrCast(Doc.UserDoc()._fontSize), 10);
         return size;
     }
-    console.log(size);
     Doc.UserDoc()._fontSize = size + "px";
 });
 
@@ -639,7 +659,7 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
     if (checkResult) {
         return Doc.UserDoc().bold;
     }
-    SelectionManager.Views().map(dv => dv.props.Document.bold = !dv.props.Document.bold);
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
     Doc.UserDoc().bold = !Doc.UserDoc().bold;
     return Doc.UserDoc().bold;
 });
@@ -648,8 +668,8 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
     if (checkResult) {
         return Doc.UserDoc().underline;
     }
-    SelectionManager.Views().map(dv => dv.props.Document.underline = !dv.props.Document.underline);
-    Doc.UserDoc().bold = !Doc.UserDoc().underline;
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
+    Doc.UserDoc().underline = !Doc.UserDoc().underline;
     return Doc.UserDoc().underline;
 });
 
@@ -657,17 +677,36 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
     if (checkResult) {
         return Doc.UserDoc().italic;
     }
-    SelectionManager.Views().map(dv => dv.props.Document.italic = !dv.props.Document.italic);
-    Doc.UserDoc().bold = !Doc.UserDoc().italic;
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
+    Doc.UserDoc().italic = !Doc.UserDoc().italic;
     return Doc.UserDoc().italic;
 });
 
-Scripting.addGlobal(function setActiveInkTool(tool: InkTool, checkResult?: boolean) {
+
+
+
+/** INK
+ *      setActiveInkTool
+ *      setStrokeWidth
+ *      setStrokeColor
+ **/ 
+
+Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) {
     if (checkResult) {
-        return Doc.UserDoc().activeInkTool === tool;
+        return Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool;
     }
-    if (tool) {
-        if (Doc.UserDoc().activeInkTool === tool){
+    if (tool === "circle") {
+        Doc.UserDoc().activeInkTool = "pen";
+        GestureOverlay.Instance.InkShape = tool;
+    } else if (tool === "square") {
+        Doc.UserDoc().activeInkTool = "pen";
+        GestureOverlay.Instance.InkShape = tool;
+    } else if (tool === "line") {
+        Doc.UserDoc().activeInkTool = "pen";
+        GestureOverlay.Instance.InkShape = tool;
+    } else if (tool) {
+        if (Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool){
+            GestureOverlay.Instance.InkShape = "";
             Doc.UserDoc().activeInkTool = InkTool.None;
         } else {
             Doc.UserDoc().activeInkTool = tool;
@@ -679,9 +718,44 @@ Scripting.addGlobal(function setActiveInkTool(tool: InkTool, checkResult?: boole
 
 Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) {
     if (checkResult) {
-        const width: number = NumCast(Doc.UserDoc().activeInkWidth);
-        return width;
+        const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+        if (selected && selected.type === DocumentType.INK){
+            return Number(selected.strokeWidth);
+        } else {
+            const width: number = NumCast(Doc.UserDoc().activeInkWidth);
+            return width;
+        }
     }
-    SetActiveInkWidth(StrCast(width));
+    Doc.UserDoc().activeInkWidth = width;
     SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(width));
+});
+
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) {
+    if (checkResult) {
+        const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+        if (selected && selected.type === DocumentType.INK){
+            return selected.color;
+        } else {
+            const color: string = StrCast(Doc.UserDoc().activeInkColor);
+            return color;
+        }
+    }
+    SetActiveInkColor(StrCast(color));
+    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.color = String(color));
+});
+
+
+/** WEB
+ *      webSetURL
+ **/ 
+Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    console.log("URL: ", url);
+    if (checkResult && selected && selected.type === DocumentType.WEB){
+        return Cast(selected.data, WebField, null).url;
+    }
+    else if (selected && selected.type === DocumentType.WEB){
+        selected.data = url;
+    }
 });
\ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 82ad2b7db..c081b8e26 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -277,6 +277,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         const found = new Set<Mark>();
         const { from, to } = state.selection as TextSelection;
         state.doc.nodesBetween(from, to, (node) => node.marks.forEach(m => found.add(m)));
+        console.log("Marks: " + found);
         return found;
     }
 
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 1270a2dab..f16e143d8 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -1,8 +1,8 @@
+import { createSimpleSchema, list, object, serializable } from "serializr";
+import { Scripting } from "../client/util/Scripting";
 import { Deserializable } from "../client/util/SerializationHelper";
-import { serializable, custom, createSimpleSchema, list, object, map } from "serializr";
+import { Copy, ToScriptString, ToString } from "./FieldSymbols";
 import { ObjectField } from "./ObjectField";
-import { Copy, ToScriptString, ToString, Update } from "./FieldSymbols";
-import { Scripting } from "../client/util/Scripting";
 
 // Helps keep track of the current ink tool in use.
 export enum InkTool {
-- 
cgit v1.2.3-70-g09d2


From c1fb227c364af5defb99adc793e850d681caa36a Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Thu, 26 Aug 2021 06:34:54 -0400
Subject: really close

---
 src/client/util/CurrentUserUtils.ts                |  40 +-
 src/client/views/MainView.tsx                      |   2 +
 .../collectionLinearView/CollectionLinearView.scss |   6 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 160 ++++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 494 ++++-----------------
 src/server/index.ts                                |   2 +-
 7 files changed, 227 insertions(+), 479 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index cd2803373..8f75947d1 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -51,6 +51,7 @@ interface Button {
     numBtnMax?: number;
     switchToggle?: boolean;
     script?: string;
+    checkResult?: string;
     width?: number;
     list?: string[];
     ignoreClick?: boolean;
@@ -958,29 +959,28 @@ export class CurrentUserUtils {
                 },
                 { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize' },
                 { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor' },
-                { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' },
-                { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' },
-                { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' },
+                { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', checkResult: 'toggleBold(true)' },
+                { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', checkResult: 'toggleItalic(true)' },
+                { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', checkResult: 'toggleUnderline(true)' },
                 // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
                 // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
                 // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
-                { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", ignoreClick: true, script: 'setAlignment("left")' },
-                { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", ignoreClick: true, script: 'setAlignment("center")' },
-                { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", ignoreClick: true, script: 'setAlignment("right")' },
+                { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left")', checkResult: 'setAlignment("left", true)' },
+                { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center")', checkResult: 'setAlignment("center", true)' },
+                { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right")', checkResult: 'setAlignment("right", true)' },
             ];
         return tools;
     }
 
     static inkTools(doc: Doc) {
         const tools: Button[] = [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', script: 'setActiveInkTool("pen" , true)' },
-            { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', script: 'setActiveInkTool("highlighter", true)' },
-            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', script: 'setActiveInkTool("circle" , true)' },
-            { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', script: 'setActiveInkTool("square" , true)' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' },
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', checkResult: 'setActiveInkTool("pen" , true)' },
+            { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', checkResult: 'setActiveInkTool("highlighter", true)' },
+            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', checkResult: 'setActiveInkTool("circle" , true)' },
+            { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', checkResult: 'setActiveInkTool("square" , true)' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', checkResult: 'setActiveInkTool("line" , true)' },
             { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, ignoreClick: true, script: 'setStrokeWidth' },
             { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "minus", ignoreClick: true, script: 'setStrokeColor' },
-
         ];
         return tools;
     }
@@ -995,8 +995,8 @@ export class CurrentUserUtils {
                     width: 50,
                     switchToggle: true,
                     icon: "eye",
-                    ignoreClick: true,
-                    script: 'toggleSchemaShow'
+                    click: 'toggleSchemaShow()',
+                    checkResult: 'toggleSchemaShow(true)'
                 },
             ];
         return tools;
@@ -1029,7 +1029,7 @@ export class CurrentUserUtils {
                 title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
                 script: "setBackgroundColor", hidden: 'selectedDocumentType()'
             }, // Only when a document is selected
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', script: 'toggleOverlay', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', checkResult: 'toggleOverlay(true)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
             { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
             { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
             { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available
@@ -1042,7 +1042,7 @@ export class CurrentUserUtils {
     static async setupContextMenuButtons(doc: Doc) {
         const docList: Doc[] = [];
 
-        (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
+        (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded, checkResult }) => {
             const menuDocList: Doc[] = [];
             if (subMenu) {
                 // default is textTools
@@ -1064,7 +1064,7 @@ export class CurrentUserUtils {
                         tools = CurrentUserUtils.textTools(doc);
                         break;
                 }
-                tools.map(({ title, toolTip, icon, btnType, numBtnType, click, script, width, list, ignoreClick, switchToggle }) => {
+                tools.map(({ title, toolTip, icon, btnType, numBtnType, click, script, width, list, ignoreClick, switchToggle, checkResult }) => {
                     menuDocList.push(Docs.Create.FontIconDocument({
                         _nativeWidth: width ? width : 25,
                         _nativeHeight: 25,
@@ -1085,7 +1085,7 @@ export class CurrentUserUtils {
                         title,
                         switchToggle,
                         color: Colors.WHITE,
-                        backgroundColor: "transparent",
+                        backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any  : "transparent",
                         _dropAction: "alias",
                         _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
                         onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined
@@ -1098,7 +1098,7 @@ export class CurrentUserUtils {
                     linearViewExpandable: true,
                     icon: title,
                     _height: 30,
-                    backgroundColor: "transparent",
+                    backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any  : "transparent",
                     linearViewIsExpanded: expanded ? ComputedField.MakeFunction(expanded) as any : undefined,
                     hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
                 }, menuDocList));
@@ -1276,6 +1276,7 @@ export class CurrentUserUtils {
         doc._raiseWhenDragged = true;
         doc._showLabel = false;
         doc._showMenuLabel = true;
+        doc.textAlign = StrCast(doc.textAlign, "left");
         doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0, 0)");
         doc.activeInkWidth = NumCast(doc.activeInkWidth, 1);
         doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
@@ -1592,7 +1593,6 @@ Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colTyp
         const parentDoc: Doc = Cast(selected.context, Doc, null);
         selected = parentDoc;
     }
-    console.log(selected, docType, colType);
     if (selected && docType && selected.type === docType) return false;
     else if (selected && colType && selected.viewType === colType) return false;
     else if (selected && !colType && !docType) return false;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dcdf6b9c3..52dbd451c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -62,6 +62,7 @@ import { PreviewCursor } from './PreviewCursor';
 import { PropertiesView } from './PropertiesView';
 import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
+import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -596,6 +597,7 @@ export class MainView extends React.Component {
             <MarqueeOptionsMenu />
             <OverlayView />
             <TimelineMenu />
+            <RichTextMenu />
             {this.snapLines}
             <div className="mainView-webRef" ref={this.makeWebRef} />
             <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
index 24c9bc9cb..f249eb1f5 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
@@ -98,11 +98,7 @@
             transition: transform 0.2s;
             align-items: center;
             justify-content: center;
-            transition: 0.1s;
-
-            &:hover {
-                transform: scale(1.05);
-            }
+            transition: 0.15s;
         }
 
         >input {
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 53cd017b0..e09e15a60 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -3,7 +3,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import { StringifyOptions } from 'querystring';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Doc, StrListCast } from '../../../../fields/Doc';
@@ -11,6 +10,7 @@ import { InkTool } from '../../../../fields/InkField';
 import { createSchema, makeInterface } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { WebField } from '../../../../fields/URLField';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { Scripting } from "../../../util/Scripting";
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -20,14 +20,13 @@ import { CollectionViewType } from '../../collections/CollectionView';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
 import { EditableView } from '../../EditableView';
+import { GestureOverlay } from '../../GestureOverlay';
 import { Colors } from '../../global/globalEnums';
+import { SetActiveInkColor } from '../../InkingStroke';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
-import { RichTextMenu } from '../formattedText/RichTextMenu';
 import './FontIconBox.scss';
-import { SetActiveInkWidth, SetActiveInkColor } from '../../InkingStroke';
-import { GestureOverlay } from '../../GestureOverlay';
-import { WebField } from '../../../../fields/URLField';
+import { RichTextMenu } from '../formattedText/RichTextMenu';
 const FontIconSchema = createSchema({
     icon: "string",
 });
@@ -241,7 +240,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         let text: string | undefined;
         let dropdown = true;
         let icon: IconProp = "caret-down";
-
+        let noneSelected:boolean = false;
 
         if (script === 'setView') {
             const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
@@ -253,6 +252,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 icon = Doc.toIcon(selected.Document);
             } else {
                 dropdown = false;
+                noneSelected = true;
                 text = "None selected";
             }
             noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
@@ -298,7 +298,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             <div className={`menuButton ${this.type} ${active}`}
                 style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
                 onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
-                {dropdown ? (null) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
+                {dropdown || noneSelected ? (null) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
                 <div className="menuButton-dropdown-header">
                     {text && text[0].toUpperCase() + text.slice(1)}
                 </div>
@@ -395,22 +395,6 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         // Determine the type of toggle button
         const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle);
 
-        // Script for running the toggle
-        const script: string = StrCast(this.rootDoc.script);
-
-        // Script for checking the outcome of the toggle
-        let checkScript:string;
-        if (StrCast(this.rootDoc.script).includes("(")){
-            checkScript = StrCast(this.rootDoc.script)
-
-        } else {
-            checkScript = StrCast(this.rootDoc.script) + "(true)";
-        }
-
-        // Function to run the script
-        const runScript = () => ScriptField.MakeScript(script + "()")?.script.run();
-        const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result;
-
         // Colors
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
@@ -423,13 +407,10 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         if (switchToggle) {
             return (
-                <div className={`menuButton ${this.type} ${'switch'}`}
-                    onClick={runScript}
-                >
+                <div className={`menuButton ${this.type} ${'switch'}`}>
                     <label className="switch">
                         <input type="checkbox"
-                            checked={checkResult}
-                            onChange={runScript}
+                            checked={backgroundColor === Colors.MEDIUM_BLUE}
                         />
                         <span className="slider round"></span>
                     </label>
@@ -438,10 +419,8 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         } else {
             return (
                 <div className={`menuButton ${this.type}`}
-                    style={{ opacity: 1, backgroundColor: checkResult ? Colors.MEDIUM_BLUE : "transparent" }}
-                    onClick={this.layoutDoc.ignoreClick ? runScript : undefined}
-                >
-                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={checkResult ? Colors.LIGHT_GRAY : Colors.LIGHT_GRAY} />
+                    style={{ opacity: 1, backgroundColor: backgroundColor, color: color }}>
+                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                     {label}
                 </div>
             );
@@ -595,7 +574,8 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo
 Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     if (checkResult && selected) {
-        return NumCast(selected.Document.z) === 1;
+        if(NumCast(selected.Document.z) === 1) return Colors.MEDIUM_BLUE;
+        else return "transparent";
     }
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
 });
@@ -606,43 +586,109 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
  *      toggleBold
  *      toggleUnderline
  *      toggleItalic
+ *      setAlignment
+ *      toggleBold
+ *      toggleItalic
+ *      toggleUnderline
  **/ 
+Scripting.addGlobal(function toggleBold(){
+    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+});
+
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFont(font: string) {
+Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
     SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font);
+    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+    editorView?.state && RichTextMenu.Instance.setFontFamily(font, editorView);
     Doc.UserDoc()._fontFamily = font;
     return Doc.UserDoc()._fontFamily;
 });
 
+Scripting.addGlobal(function getActiveTextInfo(info:"family" | "size" | "color" | "highlight"){
+    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+    const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
+    switch(info){
+        case "family": return style?.activeColors[0];
+        case "size": return style?.activeSizes[0];
+        case "color": return style?.activeColors[0];
+        case "highlight": return style?.activeHighlights[0];
+    }
+})
+
+Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+    if (checkResult) {
+        let active:string;
+        if (editorView){
+            active = editorView?.state && RichTextMenu.Instance.getActiveAlignment();
+        } else {
+            active = StrCast(Doc.UserDoc().textAlign); 
+        }
+        if (active === align) return Colors.MEDIUM_BLUE;
+        else return "transparent";
+    }
+    SelectionManager.Views().map(dv => dv.props.Document.textAlign = align);
+    switch(align){
+        case "left":
+            editorView?.state && RichTextMenu.Instance.alignLeft( editorView, editorView.dispatch);
+            break;
+        case "center":
+            editorView?.state && RichTextMenu.Instance.alignCenter(editorView, editorView.dispatch);
+            break;
+        case "right":
+            editorView?.state && RichTextMenu.Instance.alignRight(editorView, editorView.dispatch);
+            break;
+        default:
+            break;
+    }
+    Doc.UserDoc().textAlign = align;
+});
+
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+
     if (checkResult) {
         if (selected) {
-            console.log("[Font color] (selected): " + StrCast(selected._fontColor));
-            return selected._fontColor;
+            console.log("[Font color] (selected): " + StrCast(selected.rootDoc._fontColor));
+            return selected.rootDoc._fontColor;
         } else {
             console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor));
             return Doc.UserDoc()._fontColor;
         }
     }
-    if (selected) selected._fontColor = color;
+
+    if (selected) {
+        selected.rootDoc._fontColor = color;
+        if (color) {
+            editorView?.state && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
+        }
+    }
     Doc.UserDoc()._fontColor = color;
 });
 
-// // toggle: Set overlay status of selected document
-// Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
-//     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-//     if (checkResult) {
-//         if (selected) {
-//             return selected.Document._fontColor;
-//         } else {
-//             return Doc.UserDoc()._fontColor;
-//         }
-//     }
-//     Doc.UserDoc()._fontColor = color;
-// });
+// toggle: Set overlay status of selected document
+Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+
+    if (checkResult) {
+        if (selected) {
+            return selected.Document._fontHighlight;
+        } else {
+            return Doc.UserDoc()._fontHighlight;
+        }
+    }
+    if (selected) {
+        selected.rootDoc._fontColor = color;
+        if (color) {
+            editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch);
+        }
+    }
+    Doc.UserDoc()._fontHighlight = color;
+});
 
 
 
@@ -652,12 +698,15 @@ Scripting.addGlobal(function setFontSize(size: string, checkResult?: boolean) {
         const size: number = parseInt(StrCast(Doc.UserDoc()._fontSize), 10);
         return size;
     }
+    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+    editorView?.state && RichTextMenu.Instance.setFontSize(Number(size), editorView);
     Doc.UserDoc()._fontSize = size + "px";
 });
 
 Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
     if (checkResult) {
-        return Doc.UserDoc().bold;
+        if (Doc.UserDoc().bold) return Colors.MEDIUM_BLUE;
+        else return "transparent";
     }
     SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
     Doc.UserDoc().bold = !Doc.UserDoc().bold;
@@ -666,7 +715,8 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
 
 Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
     if (checkResult) {
-        return Doc.UserDoc().underline;
+        if (Doc.UserDoc().underline) return Colors.MEDIUM_BLUE;
+        else return "transparent";
     }
     SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
     Doc.UserDoc().underline = !Doc.UserDoc().underline;
@@ -675,7 +725,8 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
 
 Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
     if (checkResult) {
-        return Doc.UserDoc().italic;
+        if (Doc.UserDoc().italic) return Colors.MEDIUM_BLUE;
+        else return "transparent";
     }
     SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
     Doc.UserDoc().italic = !Doc.UserDoc().italic;
@@ -693,7 +744,8 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
 
 Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) {
     if (checkResult) {
-        return Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool;
+        if (Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool) return Colors.MEDIUM_BLUE;
+        else return "transparent";
     }
     if (tool === "circle") {
         Doc.UserDoc().activeInkTool = "pen";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d1027dfd7..f4053d69f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -214,7 +214,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     setupAnchorMenu = () => {
         AnchorMenu.Instance.Status = "marquee";
         AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
-            this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch);
+            this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch);
             return undefined;
         });
         AnchorMenu.Instance.onMakeAnchor = this.getAnchor;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index c081b8e26..e32eccb62 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -37,11 +37,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     public editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
 
     public _brushMap: Map<string, Set<Mark>> = new Map();
-    private fontSizeOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[];
-    private fontFamilyOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[];
-    private listTypeOptions: { node: NodeType | any | null, title: string, label: string, command: any, style?: {} }[];
-    private fontColors: (string | undefined)[];
-    private highlightColors: (string | undefined)[];
 
     @observable private collapsed: boolean = false;
     @observable private boldActive: boolean = false;
@@ -76,70 +71,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         this._canFade = false;
         //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
         runInAction(() => this.Pinned = true);
-
-        this.fontSizeOptions = [
-            { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48px", command: this.changeFontSize },
-            { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72px", command: this.changeFontSize },
-            { mark: null, title: "", label: "...", command: unimplementedFunction, hidden: true },
-            { mark: null, title: "", label: "13px", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
-        ];
-
-        this.fontFamilyOptions = [
-            { mark: schema.marks.pFontFamily.create({ family: "Times New Roman" }), title: "Set font family", label: "Times New Roman", command: this.changeFontFamily, style: { fontFamily: "Times New Roman" } },
-            { mark: schema.marks.pFontFamily.create({ family: "Arial" }), title: "Set font family", label: "Arial", command: this.changeFontFamily, style: { fontFamily: "Arial" } },
-            { mark: schema.marks.pFontFamily.create({ family: "Georgia" }), title: "Set font family", label: "Georgia", command: this.changeFontFamily, style: { fontFamily: "Georgia" } },
-            { mark: schema.marks.pFontFamily.create({ family: "Comic Sans MS" }), title: "Set font family", label: "Comic Sans MS", command: this.changeFontFamily, style: { fontFamily: "Comic Sans MS" } },
-            { mark: schema.marks.pFontFamily.create({ family: "Tahoma" }), title: "Set font family", label: "Tahoma", command: this.changeFontFamily, style: { fontFamily: "Tahoma" } },
-            { mark: schema.marks.pFontFamily.create({ family: "Impact" }), title: "Set font family", label: "Impact", command: this.changeFontFamily, style: { fontFamily: "Impact" } },
-            { mark: schema.marks.pFontFamily.create({ family: "Crimson Text" }), title: "Set font family", label: "Crimson Text", command: this.changeFontFamily, style: { fontFamily: "Crimson Text" } },
-            { mark: null, title: "", label: "various", command: unimplementedFunction, hidden: true },
-            // { mark: null, title: "", label: "default", command: unimplementedFunction, hidden: true },
-        ];
-
-        this.listTypeOptions = [
-            { node: schema.nodes.ordered_list.create({ mapStyle: "bullet" }), title: "Set list type", label: ":", command: this.changeListType },
-            { node: schema.nodes.ordered_list.create({ mapStyle: "decimal" }), title: "Set list type", label: "1.1", command: this.changeListType },
-            { node: schema.nodes.ordered_list.create({ mapStyle: "multi" }), title: "Set list type", label: "A.1", command: this.changeListType },
-            { node: schema.nodes.ordered_list.create({ mapStyle: "" }), title: "Set list type", label: "<none>", command: this.changeListType },
-            //{ node: undefined, title: "Set list type", label: "Remove", command: this.changeListType },
-        ];
-
-        this.fontColors = [
-            DarkPastelSchemaPalette.get("pink2"),
-            DarkPastelSchemaPalette.get("purple4"),
-            DarkPastelSchemaPalette.get("bluegreen1"),
-            DarkPastelSchemaPalette.get("yellow4"),
-            DarkPastelSchemaPalette.get("red2"),
-            DarkPastelSchemaPalette.get("bluegreen7"),
-            DarkPastelSchemaPalette.get("bluegreen5"),
-            DarkPastelSchemaPalette.get("orange1"),
-            "#757472",
-            "#000"
-        ];
-
-        this.highlightColors = [
-            PastelSchemaPalette.get("pink2"),
-            PastelSchemaPalette.get("purple4"),
-            PastelSchemaPalette.get("bluegreen1"),
-            PastelSchemaPalette.get("yellow4"),
-            PastelSchemaPalette.get("red2"),
-            PastelSchemaPalette.get("bluegreen7"),
-            PastelSchemaPalette.get("bluegreen5"),
-            PastelSchemaPalette.get("orange1"),
-            "white",
-            "transparent"
-        ];
     }
 
     componentDidMount() {
@@ -348,102 +279,38 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         });
     }
 
-    createButton(faIcon: string, title: string, isActive: boolean = false, command?: any, onclick?: any) {
-        const self = this;
-        function onClick(e: React.PointerEvent) {
-            e.preventDefault();
-            e.stopPropagation();
-            self.TextView?.endUndoTypingBatch();
-            UndoManager.RunInBatch(() => {
-                self.view && command && command(self.view.state, self.view.dispatch, self.view);
-                self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view);
-            }, "rich text menu command");
-            self.setActiveMarkButtons(self.getActiveMarksOnSelection());
-        }
-
-        return (
-            <Tooltip title={<div className="dash-tooltip">{title}</div>} key={title} placement="bottom">
-                <button className={"antimodeMenu-button" + (isActive ? " active" : "")} onPointerDown={onClick}>
-                    <FontAwesomeIcon icon={faIcon as IconProp} size="lg" />
-                </button>
-            </Tooltip>
-        );
-    }
-
-    createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => void): JSX.Element {
-        const items = options.map(({ title, label, hidden, style }) => {
-            if (hidden) {
-                return <option value={label} title={title} key={label} style={style ? style : {}} hidden>{label}</option>;
-            }
-            return <option value={label} title={title} key={label} style={style ? style : {}}>{label}</option>;
-        });
-
-        const self = this;
-        function onChange(e: React.ChangeEvent<HTMLSelectElement>) {
-            e.stopPropagation();
-            e.preventDefault();
-            self.TextView?.endUndoTypingBatch();
-            UndoManager.RunInBatch(() => {
-                options.forEach(({ label, mark, command }) => {
-                    if (e.target.value === label && mark) {
-                        if (!self.TextView?.props.isSelected(true)) {
-                            switch (mark.type) {
-                                case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break;
-                                case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break;
-                            }
-                        }
-                        else self.view && mark && command(mark, self.view);
-                    }
-                });
-            }, "text mark dropdown");
-        }
-
-        return <Tooltip key={key} title={<div className="dash-tooltip">{key}</div>} placement="bottom">
-            <select onChange={onChange} value={activeOption}>{items}</select>
-        </Tooltip>;
-    }
-
-    createNodesDropdown(activeMap: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => {}): JSX.Element {
-        const activeOption = activeMap === "bullet" ? ":" : activeMap === "decimal" ? "1.1" : activeMap === "multi" ? "A.1" : "<none>";
-        const items = options.map(({ title, label, hidden, style }) => {
-            if (hidden) {
-                return <option value={label} title={title} key={label} style={style ? style : {}} hidden>{label}</option>;
-            }
-            return <option value={label} title={title} key={label} style={style ? style : {}}>{label}</option>;
-        });
 
-        const self = this;
-        function onChange(val: string) {
-            self.TextView.endUndoTypingBatch();
-            options.forEach(({ label, node, command }) => {
-                if (val === label && node) {
-                    if (self.TextView.props.isSelected(true)) {
-                        UndoManager.RunInBatch(() => self.view && node && command(node), "nodes dropdown");
-                        setter(val);
-                    }
-                }
-            });
-        }
-
-        return <Tooltip key={key} title={<div className="dash-tooltip">{key}</div>} placement="bottom">
-            <select value={activeOption} onChange={e => onChange(e.target.value)}>{items}</select>
-        </Tooltip>;
-    }
-
-    changeFontSize = (mark: Mark, view: EditorView) => {
-        const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize });
+    setFontSize = (size:number, view: EditorView) => {
+        const fmark = view.state.schema.marks.pFontSize.create({ fontSize: size });
         this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
         view.focus();
         this.updateMenu(view, undefined, this.props);
     }
 
-    changeFontFamily = (mark: Mark, view: EditorView) => {
-        const fmark = view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family });
+    setFontFamily = (family:string, view: EditorView) => {
+        const fmark = view.state.schema.marks.pFontFamily.create({ family: family });
         this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
         view.focus();
         this.updateMenu(view, undefined, this.props);
     }
 
+    setHighlight(color: String, view: EditorView, dispatch: any) {
+        const highlightMark = view.state.schema.mark(view.state.schema.marks.marker, { highlight: color });
+        if (view.state.selection.empty) return false;
+        view.focus();
+        this.setMark(highlightMark, view.state, dispatch, false);
+    }
+
+    setColor(color: String, view: EditorView, dispatch: any) {
+        const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color });
+        if (view.state.selection.empty) {
+            dispatch(view.state.tr.addStoredMark(colorMark));
+            return false;
+        }
+        this.setMark(colorMark, view.state, dispatch, true);
+        view.focus();
+    }
+
     // TODO: remove doesn't work
     //remove all node type and apply the passed-in one to the selected text
     changeListType = (nodeType: Node | undefined) => {
@@ -491,23 +358,24 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
         return true;
     }
-    alignCenter = (state: EditorState<any>, dispatch: any) => {
-        return this.TextView.props.isSelected(true) && this.alignParagraphs(state, "center", dispatch);
+    alignCenter = (view: EditorView, dispatch: any) => {
+        return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "center", dispatch);
     }
-    alignLeft = (state: EditorState<any>, dispatch: any) => {
-        return this.TextView.props.isSelected(true) && this.alignParagraphs(state, "left", dispatch);
+    alignLeft = (view: EditorView, dispatch: any) => {
+        return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "left", dispatch);
     }
-    alignRight = (state: EditorState<any>, dispatch: any) => {
-        return this.TextView.props.isSelected(true) && this.alignParagraphs(state, "right", dispatch);
+    alignRight = (view: EditorView, dispatch: any) => {
+        return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "right", dispatch);
     }
 
-    alignParagraphs(state: EditorState<any>, align: "left" | "right" | "center", dispatch: any) {
-        var tr = state.tr;
-        state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
+    alignParagraphs(view: EditorView, align: "left" | "right" | "center", dispatch: any) {
+        var tr = view.state.tr;
+        view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => {
             if (node.type === schema.nodes.paragraph || node.type === schema.nodes.heading) {
                 tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, align }, node.marks);
                 return false;
             }
+            view.focus();
             return true;
         });
         dispatch?.(tr);
@@ -598,47 +466,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     }
     _brushNameRef = React.createRef<HTMLInputElement>();
 
-    createBrushButton() {
-        const self = this;
-        const onBrushClick = (e: React.MouseEvent) => {
-            e.preventDefault();
-            e.stopPropagation();
-            self.TextView.endUndoTypingBatch();
-            UndoManager.RunInBatch(() => self.view && self.fillBrush(self.view.state, self.view.dispatch), "rt brush");
-        };
-
-        let label = "Stored marks: ";
-        if (this.brushMarks && this.brushMarks.size > 0) {
-            this.brushMarks.forEach((mark: Mark) => {
-                const markType = mark.type;
-                label += markType.name;
-                label += ", ";
-            });
-            label = label.substring(0, label.length - 2);
-        } else {
-            label = "No marks are currently stored";
-        }
-
-        //onPointerDown={onBrushClick}
-
-        const button = <Tooltip title={<div className="dash-tooltip">style brush</div>} placement="bottom">
-            <button className="antimodeMenu-button" onClick={onBrushClick} style={this.brushMarks?.size > 0 ? { backgroundColor: "121212" } : {}}>
-                <FontAwesomeIcon icon="paint-roller" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.brushMarks?.size > 0 ? 45 : 0}deg)` }} />
-            </button>
-        </Tooltip>;
-
-        const dropdownContent =
-            <div className="dropdown">
-                <p>{label}</p>
-                <button onPointerDown={this.clearBrush}>Clear brush</button>
-                <input placeholder="-brush name-" ref={this._brushNameRef} onKeyPress={this.onBrushNameKeyPress} />
-            </div>;
-
-        return (
-            <ButtonDropdown view={this.view} key={"brush dropdown"} button={button} openDropdownOnButton={false} dropdownContent={dropdownContent} />
-        );
-    }
-
     @action
     clearBrush() {
         RichTextMenu.Instance.brushMarks = new Set();
@@ -667,123 +494,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         }
     }
 
-    @action toggleColorDropdown() { this.showColorDropdown = !this.showColorDropdown; }
-    @action setActiveColor(color: string) { this.activeFontColor = color; }
     get TextView() { return (this.view as any)?.TextView as FormattedTextBox; }
     get TextViewFieldKey() { return this.TextView?.props.fieldKey; }
 
-    createColorButton() {
-        const self = this;
-        function onColorClick(e: React.PointerEvent) {
-            e.preventDefault();
-            e.stopPropagation();
-            self.TextView.endUndoTypingBatch();
-            if (self.view) {
-                UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
-                self.view.focus();
-                self.updateMenu(self.view, undefined, self.props);
-            }
-        }
-        function changeColor(e: React.PointerEvent, color: string) {
-            e.preventDefault();
-            e.stopPropagation();
-            self.setActiveColor(color);
-            self.TextView.endUndoTypingBatch();
-            if (self.view) {
-                UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color");
-                self.view.focus();
-                self.updateMenu(self.view, undefined, self.props);
-            }
-        }
-
-        // onPointerDown={onColorClick}
-        const button = <Tooltip title={<div className="dash-tooltip">set font color</div>} placement="bottom">
-            <button className="antimodeMenu-button color-preview-button">
-                <FontAwesomeIcon icon="palette" size="lg" />
-                <div className="color-preview" style={{ backgroundColor: this.activeFontColor }}></div>
-            </button>
-        </Tooltip>;
+    
 
-        const dropdownContent =
-            <div className="dropdown" >
-                <p>Change font color:</p>
-                <div className="color-wrapper">
-                    {this.fontColors.map(color => {
-                        if (color) {
-                            return this.activeFontColor === color ?
-                                <button className="color-button active" key={"active" + color} style={{ backgroundColor: color }} onPointerDown={e => changeColor(e, color)}></button> :
-                                <button className="color-button" key={"other" + color} style={{ backgroundColor: color }} onPointerDown={e => changeColor(e, color)}></button>;
-                        }
-                    })}
-                </div>
-            </div>;
-
-        return (
-            <ButtonDropdown view={this.view} key={"color dropdown"} button={button} dropdownContent={dropdownContent} openDropdownOnButton={true} />
-        );
-    }
-
-    public insertColor(color: String, state: EditorState<any>, dispatch: any) {
-        const colorMark = state.schema.mark(state.schema.marks.pFontColor, { color: color });
-        if (state.selection.empty) {
-            dispatch(state.tr.addStoredMark(colorMark));
-            return false;
-        }
-        this.setMark(colorMark, state, dispatch, true);
-    }
-
-    @action toggleHighlightDropdown() { this.showHighlightDropdown = !this.showHighlightDropdown; }
     @action setActiveHighlight(color: string) { this.activeHighlightColor = color; }
 
-    createHighlighterButton() {
-        const self = this;
-        function onHighlightClick(e: React.PointerEvent) {
-            e.preventDefault();
-            e.stopPropagation();
-            self.TextView.endUndoTypingBatch();
-            UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highligher");
-        }
-        function changeHighlight(e: React.PointerEvent, color: string) {
-            e.preventDefault();
-            e.stopPropagation();
-            self.setActiveHighlight(color);
-            self.TextView.endUndoTypingBatch();
-            UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highlighter");
-        }
-
-        //onPointerDown={onHighlightClick}
-        const button = <Tooltip title={<div className="dash-tooltip">set highlight color</div>} placement="bottom">
-            <button className="antimodeMenu-button color-preview-button" key="highilghter-button" >
-                <FontAwesomeIcon icon="highlighter" size="lg" />
-                <div className="color-preview" style={{ backgroundColor: this.activeHighlightColor }}></div>
-            </button>
-        </Tooltip>;
-
-        const dropdownContent =
-            <div className="dropdown">
-                <p>Change highlight color:</p>
-                <div className="color-wrapper">
-                    {this.highlightColors.map(color => {
-                        if (color) {
-                            return this.activeHighlightColor === color ?
-                                <button className="color-button active" key={`active ${color}`} style={{ backgroundColor: color }} onPointerDown={e => changeHighlight(e, color)}>{color === "transparent" ? "X" : ""}</button> :
-                                <button className="color-button" key={`inactive ${color}`} style={{ backgroundColor: color }} onPointerDown={e => changeHighlight(e, color)}>{color === "transparent" ? "X" : ""}</button>;
-                        }
-                    })}
-                </div>
-            </div>;
-
-        return (
-            <ButtonDropdown view={this.view} key={"highlighter"} button={button} dropdownContent={dropdownContent} openDropdownOnButton={true} />
-        );
-    }
 
-    insertHighlight(color: String, state: EditorState<any>, dispatch: any) {
-        if (state.selection.empty) return false;
-        toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch);
-    }
-
-    @action toggleLinkDropdown() { this.showLinkDropdown = !this.showLinkDropdown; }
     @action setCurrentLink(link: string) { this.currentLink = link; }
 
     createLinkButton() {
@@ -922,93 +640,73 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         return ref_node;
     }
 
-    @action onPointerEnter(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; }
-    @action onPointerLeave(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; }
-
-    @action
-    toggleMenuPin = (e: React.MouseEvent) => {
-        Doc.UserDoc()["menuRichText-pinned"] = this.Pinned = !this.Pinned;
-        if (!this.Pinned) {
-            this.fadeOut(true);
-        }
-    }
-
-    @action
-    protected toggleCollapse = (e: React.MouseEvent) => {
-        this.collapsed = !this.collapsed;
-        setTimeout(() => {
-            const x = Math.min(this._left, window.innerWidth - RichTextMenu.Instance.width);
-            RichTextMenu.Instance.jumpTo(x, this._top, true);
-        }, 0);
-    }
-
     render() {
-        TraceMobx();
-        const row1 = <div className="antimodeMenu-row" key="row 1" style={{ display: this.collapsed ? "none" : undefined }}>{[
-            //!this.collapsed ? this.getDragger() : (null),
-            // !this.Pinned ? (null) : <div key="frag1"> {[
-            //     this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
-            //     this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
-            //     this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
-            //     this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
-            //     this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
-            //     this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
-            //     <div className="richTextMenu-divider" key="divider" />
-            // ]}</div>,
-            this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
-            this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
-            this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
-            this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
-            this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
-            this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
-            this.createColorButton(),
-            this.createHighlighterButton(),
-            this.createLinkButton(),
-            this.createBrushButton(),
-            <div className="collectionMenu-divider" key="divider 2" />,
-            this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft),
-            this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter),
-            this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight),
-            this.createButton("indent", "Inset More", undefined, this.insetParagraph),
-            this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph),
-            this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph),
-            this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph),
-        ]}</div>;
-
-        const row2 = <div className="antimodeMenu-row row-2" key="row2">
-            {this.collapsed ? this.getDragger() : (null)}
-            <div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}>
-                <div className="collectionMenu-divider" key="divider 3" />
-                {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
-                    this.activeFontSize = val;
-                    SelectionManager.Views().map(dv => dv.props.Document._fontSize = val);
-                })),
-                this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
-                    this.activeFontFamily = val;
-                    SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val);
-                })),
-                <div className="collectionMenu-divider" key="divider 4" />,
-                this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
-                this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
-                this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
-                this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule)
-                ]}
-            </div>
-            {/* <div key="collapser">
-                {<div key="collapser">
-                    <button className="antimodeMenu-button" key="collapse menu" title="Collapse menu" onClick={this.toggleCollapse} style={{ backgroundColor: this.collapsed ? "#121212" : "", width: 25 }}>
-                        <FontAwesomeIcon icon="chevron-left" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.3s", transform: `rotate(${this.collapsed ? 180 : 0}deg)` }} />
-                    </button>
-                </div> }
-                <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}>
-                    <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
-                </button>
-            </div> */}
-        </div>;
+        // TraceMobx();
+        // const row1 = <div className="antimodeMenu-row" key="row 1" style={{ display: this.collapsed ? "none" : undefined }}>{[
+        //     //!this.collapsed ? this.getDragger() : (null),
+        //     // !this.Pinned ? (null) : <div key="frag1"> {[
+        //     //     this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
+        //     //     this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
+        //     //     this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
+        //     //     this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
+        //     //     this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
+        //     //     this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
+        //     //     <div className="richTextMenu-divider" key="divider" />
+        //     // ]}</div>,
+        //     this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
+        //     this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
+        //     this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
+        //     this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
+        //     this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
+        //     this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
+        //     this.createColorButton(),
+        //     this.createHighlighterButton(),
+        //     this.createLinkButton(),
+        //     this.createBrushButton(),
+        //     <div className="collectionMenu-divider" key="divider 2" />,
+        //     this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft),
+        //     this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter),
+        //     this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight),
+        //     this.createButton("indent", "Inset More", undefined, this.insetParagraph),
+        //     this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph),
+        //     this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph),
+        //     this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph),
+        // ]}</div>;
+
+        // const row2 = <div className="antimodeMenu-row row-2" key="row2">
+        //     {this.collapsed ? this.getDragger() : (null)}
+        //     <div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}>
+        //         <div className="collectionMenu-divider" key="divider 3" />
+        //         {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
+        //             this.activeFontSize = val;
+        //             SelectionManager.Views().map(dv => dv.props.Document._fontSize = val);
+        //         })),
+        //         this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
+        //             this.activeFontFamily = val;
+        //             SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val);
+        //         })),
+        //         <div className="collectionMenu-divider" key="divider 4" />,
+        //         this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
+        //         this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
+        //         this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
+        //         this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule)
+        //         ]}
+        //     </div>
+        //     {/* <div key="collapser">
+        //         {<div key="collapser">
+        //             <button className="antimodeMenu-button" key="collapse menu" title="Collapse menu" onClick={this.toggleCollapse} style={{ backgroundColor: this.collapsed ? "#121212" : "", width: 25 }}>
+        //                 <FontAwesomeIcon icon="chevron-left" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.3s", transform: `rotate(${this.collapsed ? 180 : 0}deg)` }} />
+        //             </button>
+        //         </div> }
+        //         <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}>
+        //             <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
+        //         </button>
+        //     </div> */}
+        // </div>;
 
         return (
-            <div className="richTextMenu" onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} >
-                {this.getElementWithRows([row1, row2], 2, false)}
+            <div className="richTextMenu">
+                {/* {this.getElementWithRows([row1, row2], 2, false)} */}
             </div>
         );
     }
diff --git a/src/server/index.ts b/src/server/index.ts
index 9687c3b23..f8c32103b 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -16,6 +16,7 @@ import UserManager from './ApiManagers/UserManager';
 import UtilManager from './ApiManagers/UtilManager';
 import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader';
 import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils";
+import { DashSessionAgent } from "./DashSession/DashSessionAgent";
 import { AppliedSessionAgent } from "./DashSession/Session/agents/applied_session_agent";
 import { DashUploadUtils } from './DashUploadUtils';
 import { Database } from './database';
@@ -23,7 +24,6 @@ import { Logger } from "./ProcessFactory";
 import RouteManager, { Method, PublicHandler } from './RouteManager';
 import RouteSubscriber from './RouteSubscriber';
 import initializeServer, { resolvedPorts } from './server_Initialization';
-import { DashSessionAgent } from "./DashSession/DashSessionAgent";
 
 export const AdminPriviliges: Map<string, boolean> = new Map();
 export const onWindows = process.platform === "win32";
-- 
cgit v1.2.3-70-g09d2


From c42f909188aff5398de31e443500eb64b8690ac4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 26 Aug 2021 08:57:20 -0400
Subject: fixed warnings/errors

---
 src/client/util/SelectionManager.ts                |  2 +-
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/TemplateMenu.tsx                  |  1 +
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 src/client/views/collections/TreeView.tsx          |  1 +
 .../CollectionFreeFormLinkView.tsx                 |  8 ++--
 .../collections/collectionSchema/SchemaTable.tsx   |  4 +-
 src/client/views/linking/LinkEditor.tsx            |  6 +--
 .../views/linking/LinkRelationshipSearch.tsx       | 12 ++---
 src/client/views/nodes/FilterBox.tsx               |  1 +
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/LinkBox.tsx                 |  1 +
 src/client/views/pdf/AnchorMenu.tsx                |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 src/client/views/search/SearchBox.tsx              | 56 ++++++++++------------
 15 files changed, 50 insertions(+), 51 deletions(-)

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

diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index dbcc49f3d..0cfaebbf2 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,10 +1,10 @@
 import { action, observable, ObservableMap } from "mobx";
 import { computedFn } from "mobx-utils";
 import { Doc, Opt } from "../../fields/Doc";
+import { DocumentType } from "../documents/DocumentTypes";
 import { CollectionSchemaView } from "../views/collections/collectionSchema/CollectionSchemaView";
 import { CollectionViewType } from "../views/collections/CollectionView";
 import { DocumentView } from "../views/nodes/DocumentView";
-import { DocumentType } from "../documents/DocumentTypes";
 
 export namespace SelectionManager {
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6a388c5b4..8b5e18fb2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -473,6 +473,7 @@ export class MainView extends React.Component {
                     bringToFront={emptyFunction}
                     select={emptyFunction}
                     isContentActive={returnFalse}
+                    isAnyChildContentActive={returnFalse}
                     isSelected={returnFalse}
                     docViewPath={returnEmptyDoclist}
                     moveDocument={this.moveButtonDoc}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 5491a81e6..ff3f92364 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -141,6 +141,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 onCheckedClick={this.scriptField}
                 onChildClick={this.scriptField}
                 dropAction={undefined}
+                isAnyChildContentActive={returnFalse}
                 isContentActive={returnTrue}
                 bringToFront={emptyFunction}
                 focus={emptyFunction}
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1efea96be..6bdeaf722 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -18,6 +18,7 @@ import { SnappingManager } from "../../util/SnappingManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
 import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/global/globalCssVariables.scss';
+import { SchemaTable } from "../collections/collectionSchema/SchemaTable";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import '../DocumentDecorations.scss';
@@ -25,7 +26,6 @@ import { DocumentView } from "../nodes/DocumentView";
 import { DefaultStyleProvider } from "../StyleProvider";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
-import { SchemaTable } from "../collections/collectionSchema/SchemaTable";
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
 
 export enum ColumnType {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 3ee9dbf59..566083e7e 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -698,6 +698,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             isDocumentActive={asText ? this.props.isContentActive : returnFalse}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
             hideTitle={asText}
+            treeViewDoc={this.props.treeView?.props.Document}
             fitContentsToDoc={returnTrue}
             hideDecorationTitle={this.props.treeView.outlineMode}
             hideResizeHandles={this.props.treeView.outlineMode}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 59891b7a1..16258404d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -2,15 +2,15 @@ import { action, computed, IReactionDisposer, observable, reaction } from "mobx"
 import { observer } from "mobx-react";
 import { Doc } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
+import { List } from "../../../../fields/List";
 import { NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { DocumentType } from "../../../documents/DocumentTypes";
+import { LinkManager } from "../../../util/LinkManager";
 import { SnappingManager } from "../../../util/SnappingManager";
 import { DocumentView } from "../../nodes/DocumentView";
 import "./CollectionFreeFormLinkView.scss";
 import React = require("react");
-import { LinkManager } from "../../../util/LinkManager";
-import { List } from "../../../fields/List";
 
 
 export interface CollectionFreeFormLinkViewProps {
@@ -177,11 +177,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         if (!this.renderData) return (null);
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
         LinkManager.currentLink = this.props.LinkDocs[0];
-        const linkRelationship = LinkManager.currentLink?.linkRelationship; //get string representing relationship
+        const linkRelationship = StrCast(LinkManager.currentLink?.linkRelationship); //get string representing relationship
         const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List<string>;
         const linkColorList = Doc.UserDoc().linkColorList as List<string>;
         //access stroke color using index of the relationship in the color list (default black)
-        const strokeColor = linkRelationshipList.indexOf(linkRelationship) == -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)];
+        const strokeColor = linkRelationshipList.indexOf(linkRelationship) === -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)];
         return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
             <path className="collectionfreeformlinkview-linkLine" style={{ opacity: this._opacity, strokeDasharray: "2 2", stroke: strokeColor }}
                 d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`} />
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index abe549072..631f623c6 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -197,7 +197,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                 <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
                     <FontAwesomeIcon icon={sortIcon} size="lg" />
                 </div>
-                {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
+                {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
             </div>;
 
             return {
@@ -562,7 +562,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
             onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()}
             onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
             {this.reactTable}
-            {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
+            {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
             {!this._showDoc ? (null) :
                 <div className="collectionSchemaView-documentPreview" ref="overlay"
                     style={{
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index fba95cad2..240a71c3e 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -48,7 +48,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
             if (linkRelationshipList && !linkRelationshipList.includes(value)) {
                 linkRelationshipList.push(value);
                 const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")";
-                linkColorList.push(randColor)
+                linkColorList.push(randColor);
             }
             this.relationshipButtonColor = "rgb(62, 133, 55)";
             setTimeout(action(() => this.relationshipButtonColor = ""), 750);
@@ -62,7 +62,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     @action
     getRelationshipResults = () => {
         const query = this.relationship; //current content in input box
-        const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList)
+        const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList);
         if (linkRelationshipList) {
             return linkRelationshipList.filter(rel => rel.includes(query));
         }
@@ -73,7 +73,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
      */
     @action
     toggleRelationshipResults = () => {
-        this.relationshipSearchVisibility = this.relationshipSearchVisibility == "none" ? "block" : "none";
+        this.relationshipSearchVisibility = this.relationshipSearchVisibility === "none" ? "block" : "none";
     }
 
     @undoBatch
diff --git a/src/client/views/linking/LinkRelationshipSearch.tsx b/src/client/views/linking/LinkRelationshipSearch.tsx
index 8f83f0e6e..53da880e4 100644
--- a/src/client/views/linking/LinkRelationshipSearch.tsx
+++ b/src/client/views/linking/LinkRelationshipSearch.tsx
@@ -15,7 +15,7 @@ export class LinkRelationshipSearch extends React.Component<LinkRelationshipSear
     handleResultClick = (e: React.MouseEvent) => {
         const relationship = (e.target as HTMLParagraphElement).textContent;
         if (relationship) {
-            this.props.handleRelationshipSearchChange(relationship)
+            this.props.handleRelationshipSearchChange(relationship);
         }
     }
 
@@ -31,8 +31,8 @@ export class LinkRelationshipSearch extends React.Component<LinkRelationshipSear
      * Render an empty div to increase the height of LinkEditor to accommodate 2+ results 
     */
     emptyDiv = () => {
-        if (this.props.results && this.props.results.length > 2 && this.props.display == "block") {
-            return <div style={{ height: "50px" }}></div>
+        if (this.props.results && this.props.results.length > 2 && this.props.display === "block") {
+            return <div style={{ height: "50px" }} />;
         }
     }
 
@@ -47,9 +47,9 @@ export class LinkRelationshipSearch extends React.Component<LinkRelationshipSear
                     {   // return a dropdown of relationship results if there exist results
                         this.props.results
                             ? this.props.results.map(result => {
-                                return (<p key={result} onClick={this.handleResultClick}>
+                                return <p key={result} onClick={this.handleResultClick}>
                                     {result}
-                                </p>)
+                                </p>;
                             })
                             : <p>No matching relationships</p>
                     }
@@ -58,6 +58,6 @@ export class LinkRelationshipSearch extends React.Component<LinkRelationshipSear
                 {/*Render an empty div to increase the height of LinkEditor to accommodate 2+ results */}
                 {this.emptyDiv()}
             </div>
-        )
+        );
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 1e13d1b5a..7ad03e055 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -417,6 +417,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     renderDepth={1}
                     dropAction={this.props.dropAction}
                     ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                    isAnyChildContentActive={returnFalse}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
                     isSelected={returnFalse}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index b392ddb95..38deb4a73 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -334,7 +334,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     marqueeDown = (e: React.PointerEvent) => {
         if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations)
+                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
                 return true;
             }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index c65ba9c69..55ea45bb8 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -30,6 +30,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
                 dontRegisterView={true}
                 renderDepth={this.props.renderDepth + 1}
                 CollectionView={undefined}
+                isAnyChildContentActive={returnFalse}
                 isContentActive={this.isContentActiveFunc}
                 addDocument={returnFalse}
                 removeDocument={returnFalse}
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 42bec38da..95e318738 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -69,7 +69,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         this._disposer = reaction(() => SelectionManager.Views(),
             selected => {
                 this._showLinkPopup = false;
-                AnchorMenu.Instance.fadeOut(true)
+                AnchorMenu.Instance.fadeOut(true);
             });
     }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index bc35d2126..e77fdde3d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -525,7 +525,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 CollectionView={undefined}
                 ScreenToLocalTransform={this.overlayTransform}
                 renderDepth={this.props.renderDepth + 1}
-                childPointerEvents={true} />
+                childPointerEvents={true} />;
         return <div>
             <div className={`pdfViewerDash-overlay${CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`}
                 style={{
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 07186fe8d..260ddfc90 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -14,7 +14,7 @@ import "./SearchBox.scss";
 import { DocumentManager } from '../../util/DocumentManager';
 import { DocUtils } from '../../documents/Documents';
 
-export const searchSchema = createSchema({ 
+export const searchSchema = createSchema({
     Document: Doc
 });
 
@@ -109,13 +109,12 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
     });
 
     makeLink = action((linkTo: Doc) => {
-        console.log("[makeLink-1] got here!")
         console.log(linkTo.title);
-        if (this.props.linkFrom){
+        if (this.props.linkFrom) {
             const linkFrom = this.props.linkFrom();
-            if (linkFrom){
+            if (linkFrom) {
                 console.log(linkFrom.title);
-                DocUtils.MakeLink({doc: linkFrom}, {doc:linkTo});
+                DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo });
             }
         }
     });
@@ -177,10 +176,10 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
      * right side of each search result.
      */
     static formatType(type: String): String {
-        if (type == "pdf") {
+        if (type === "pdf") {
             return "PDF";
         }
-        else if (type == "image") {
+        else if (type === "image") {
             return "Img";
         }
 
@@ -200,28 +199,25 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
         const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
         const blockedKeys = ["x", "y", "proto", "width", "autoHeight", "acl-Override", "acl-Public", "context", "zIndex", "height", "text-scrollHeight", "text-height", "cloneFieldFilter", "isPrototype", "text-annotations",
             "dragFactory-count", "text-noTemplate", "aliases", "system", "layoutKey", "baseProto", "xMargin", "yMargin", "links", "layout", "layout_keyValue", "fitWidth", "viewType", "title-custom",
-            "panX", "panY", "viewScale"]
+            "panX", "panY", "viewScale"];
         const collection = CollectionDockingView.Instance;
         query = query.toLowerCase();
 
-        this._results = []
-        this._selectedResult = undefined
+        this._results = [];
+        this._selectedResult = undefined;
 
         if (collection !== undefined) {
             const docs = DocListCast(collection.rootDoc[Doc.LayoutFieldKey(collection.rootDoc)]);
-            const docIDs: String[] = []
-            console.log(docs.length)
+            const docIDs: String[] = [];
             SearchBox.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
                 const dtype = StrCast(doc.type, "string") as DocumentType;
                 if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth > 0) {
                     const hlights = new Set<string>();
                     SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
-                    blockedKeys.forEach(key => {
-                        hlights.delete(key);
-                    })
+                    blockedKeys.forEach(key => hlights.delete(key));
                     Array.from(hlights.keys()).length > 0 && this._results.push([doc, Array.from(hlights.keys())]);
                 }
-                docIDs.push(doc[Id])
+                docIDs.push(doc[Id]);
             });
         }
     }
@@ -245,7 +241,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
     submitSearch = async () => {
         this.resetSearch();
 
-        let query = StrCast(this._searchString);
+        const query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
         this._results = [];
 
@@ -282,11 +278,9 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
      */
     @computed
     public get selectOptions() {
-        const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"]
+        const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"];
 
-        return selectValues.map(value => {
-            return <option key={value} value={value}>{SearchBox.formatType(value)}</option>
-        })
+        return selectValues.map(value => <option key={value} value={value}>{SearchBox.formatType(value)}</option>);
     }
 
     /**
@@ -295,17 +289,17 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
     render() {
         var validResults = 0;
 
-        const isLinkSearch:boolean = this.props.linkSearch;
+        const isLinkSearch: boolean = this.props.linkSearch;
 
 
         const results = this._results.map(result => {
             var className = "searchBox-results-scroll-view-result";
 
-            if (this._selectedResult == result[0]) {
-                className += " searchBox-results-scroll-view-result-selected"
+            if (this._selectedResult === result[0]) {
+                className += " searchBox-results-scroll-view-result-selected";
             }
 
-            if (this._docTypeString == "all" || this._docTypeString == result[0].type) {
+            if (this._docTypeString === "all" || this._docTypeString === result[0].type) {
                 validResults++;
                 return (
                     <div key={result[0][Id]} onClick={isLinkSearch ? () => this.makeLink(result[0]) : () => this.onResultClick(result[0])} className={className}>
@@ -319,25 +313,25 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDoc
                             {result[1].join(", ")}
                         </div>
                     </div>
-                )
+                );
             }
 
             return null;
-        })
+        });
 
         results.filter(result => result);
 
         return (
             <div style={{ pointerEvents: "all" }} className="searchBox-container">
-                 <div className="searchBox-bar" >
-                 {isLinkSearch ? (null) : <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
+                <div className="searchBox-bar" >
+                    {isLinkSearch ? (null) : <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
                         {this.selectOptions}
                     </select>}
-                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" style={{width: isLinkSearch ? "100%" : undefined, borderRadius: isLinkSearch ? "5px" : undefined}} ref={this._inputRef} />
+                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" style={{ width: isLinkSearch ? "100%" : undefined, borderRadius: isLinkSearch ? "5px" : undefined }} ref={this._inputRef} />
                 </div >
                 <div className="searchBox-results-container">
                     <div className="searchBox-results-count">
-                        {`${validResults}` + " result" + (validResults == 1 ? "" : "s")}
+                        {`${validResults}` + " result" + (validResults === 1 ? "" : "s")}
                     </div>
                     <div className="searchBox-results-scroll-view">
                         {results}
-- 
cgit v1.2.3-70-g09d2


From 702ba59ac0235e24121ffff9fcca7c35717867ab Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Thu, 26 Aug 2021 12:40:42 -0400
Subject: hopefully ready!

---
 src/client/util/CurrentUserUtils.ts                |  9 ++--
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 53 ++++++++++++++++++++--
 .../views/nodes/formattedText/RichTextMenu.tsx     | 32 +++++++------
 4 files changed, 72 insertions(+), 24 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 8f75947d1..4975b749c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -962,6 +962,9 @@ export class CurrentUserUtils {
                 { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', checkResult: 'toggleBold(true)' },
                 { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', checkResult: 'toggleItalic(true)' },
                 { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', checkResult: 'toggleUnderline(true)' },
+                { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet")', checkResult: 'setBulletList("bullet", true)' },
+                { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal")', checkResult: 'setBulletList("decimal", true)' },
+
                 // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
                 // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
                 // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
@@ -992,11 +995,9 @@ export class CurrentUserUtils {
                     title: "Show preview",
                     toolTip: "Show preview of selected document",
                     btnType: ButtonType.ToggleButton,
-                    width: 50,
-                    switchToggle: true,
                     icon: "eye",
-                    click: 'toggleSchemaShow()',
-                    checkResult: 'toggleSchemaShow(true)'
+                    click: 'toggleSchemaPreview()',
+                    checkResult: 'toggleSchemaPreview(true)'
                 },
             ];
         return tools;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 52dbd451c..c95e56584 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -173,7 +173,7 @@ export class MainView extends React.Component {
             fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
             fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
             fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-            fa.faSave, fa.faBookmark);
+            fa.faSave, fa.faBookmark, fa.faList, fa.faListOl);
         this.initAuthenticationRouters();
     }
 
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 5d82fe123..745a85b21 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -164,7 +164,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                             e.stopPropagation();
                             e.preventDefault();
                         }}
-                        onDoubleClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
+                        onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
                     >
                         <input
                             style={{ width: 30 }}
@@ -641,6 +641,25 @@ Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", ch
     Doc.UserDoc().textAlign = align;
 });
 
+Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?:boolean){
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+    if (checkResult){
+        const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
+        console.log(active, mapStyle);
+        if (active === mapStyle) return Colors.MEDIUM_BLUE;
+        else return "transparent";
+    }
+    if (editorView){
+        const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
+        if (active === mapStyle) {
+            console.log("set bullet list");
+            editorView?.state && RichTextMenu.Instance.changeListType(editorView.state.schema.nodes.ordered_list.create({ mapStyle: "" }));
+        } else {
+            editorView?.state && RichTextMenu.Instance.changeListType(editorView.state.schema.nodes.ordered_list.create({ mapStyle: mapStyle }));
+        }
+    }
+});
+
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
@@ -707,7 +726,7 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
     if (editorView){
         console.log("editorView");
-        editorView?.state && RichTextMenu.Instance.toggleBold(editorView);
+        editorView?.state && RichTextMenu.Instance.toggleBold(editorView, true);
     }
     SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.bold = !dv.props.Document.bold);
     Doc.UserDoc().bold = !Doc.UserDoc().bold;
@@ -720,7 +739,10 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
         else return "transparent";
     }
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
-
+    if (editorView){
+        console.log("editorView");
+        editorView?.state && RichTextMenu.Instance.toggleUnderline(editorView, true);
+    }
     SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.underline = !dv.props.Document.underline);
     Doc.UserDoc().underline = !Doc.UserDoc().underline;
     return Doc.UserDoc().underline;
@@ -732,7 +754,10 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
         else return "transparent";
     }
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
-
+    if (editorView){
+        console.log("editorView");
+        editorView?.state && RichTextMenu.Instance.toggleItalic(editorView, true);
+    }
     SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.RTF).map(dv => dv.props.Document.italic = !dv.props.Document.italic);
     Doc.UserDoc().italic = !Doc.UserDoc().italic;
     return Doc.UserDoc().italic;
@@ -815,4 +840,24 @@ Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
     else if (selected && selected.type === DocumentType.WEB){
         selected.data = url;
     }
+});
+
+
+/** Schema
+ *      toggleSchemaPreview
+ **/ 
+ Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
+    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    if (checkResult && selected){
+        const result:boolean = NumCast(selected.schemaPreviewWidth) > 0;
+        if (result) return Colors.MEDIUM_BLUE;
+        else return "transparent";
+    }
+    else if (selected){
+        if (NumCast(selected.schemaPreviewWidth) > 0){
+            selected.schemaPreviewWidth = 200;
+        } else {
+            selected.schemaPreviewWidth = 0;
+        }
+    }
 });
\ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 4f40b59f2..3d9d9543d 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -280,14 +280,21 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     }
 
     toggleBold = (view: EditorView, forceBool?:boolean) => {
-        if (forceBool) { 
-            const mark = view.state.schema.marks.bold.create({bold: true});
-            this.setMark(mark, view.state, view.dispatch, true);
-        }
-        else {
-            console.log(toggleMark);
-            toggleMark(view.state.schema.marks.bold);
-        }
+        const mark = view.state.schema.mark(view.state.schema.marks.strong, {strong: forceBool});
+        this.setMark(mark, view.state, view.dispatch, false);
+        view.focus();
+    }
+
+    toggleUnderline = (view: EditorView, forceBool?:boolean) => {
+        const mark = view.state.schema.mark(view.state.schema.marks.underline, {underline: forceBool});
+        this.setMark(mark, view.state, view.dispatch, false);
+        view.focus();
+    }
+
+    toggleItalic = (view: EditorView, forceBool?:boolean) => {
+        const mark = view.state.schema.mark(view.state.schema.marks.em, {em: forceBool});
+        this.setMark(mark, view.state, view.dispatch, false);
+        view.focus();
     }
 
 
@@ -323,7 +330,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     }
 
     // TODO: remove doesn't work
-    //remove all node type and apply the passed-in one to the selected text
+    // remove all node type and apply the passed-in one to the selected text
     changeListType = (nodeType: Node | undefined) => {
         if (!this.view || (nodeType as any)?.attrs.mapStyle === "") return;
 
@@ -653,6 +660,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     }
 
     render() {
+        return null;
         // TraceMobx();
         // const row1 = <div className="antimodeMenu-row" key="row 1" style={{ display: this.collapsed ? "none" : undefined }}>{[
         //     //!this.collapsed ? this.getDragger() : (null),
@@ -715,12 +723,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         //         </button>
         //     </div> */}
         // </div>;
-
-        return (
-            <div className="richTextMenu">
-                {/* {this.getElementWithRows([row1, row2], 2, false)} */}
-            </div>
-        );
     }
 }
 
-- 
cgit v1.2.3-70-g09d2


From 64fae6de1373f0db18ff98054227f217640952c2 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Tue, 31 Aug 2021 01:28:58 -0400
Subject: "Untitled Presentation" only created after a document is pinned

---
 src/client/util/CurrentUserUtils.ts         | 3 +--
 src/client/views/MainView.tsx               | 1 -
 src/client/views/collections/TabDocView.tsx | 6 ------
 src/server/DashUploadUtils.ts               | 2 +-
 4 files changed, 2 insertions(+), 10 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index bcc47c355..aebcbcdff 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -579,7 +579,7 @@ export class CurrentUserUtils {
             { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
-            // { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
+            { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
             // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
             { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
@@ -1490,7 +1490,6 @@ export class CurrentUserUtils {
         DocListCast(dashboardDoc.data).forEach(doc => doc.dashboard = dashboardDoc);
         DocListCast(dashboardDoc.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any;
 
-        Doc.AddDocToList(myPresentations, "data", presentation);
         userDoc.activePresentation = presentation;
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f082ba2c0..9047951ee 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -279,7 +279,6 @@ export class MainView extends React.Component {
             style={{
                 minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`,
                 transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
-                //TODO:glr width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`
             }}>
             {!this.mainContainer ? (null) : this.mainDocView}
         </div>;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 28b074d35..889db1ef6 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -134,12 +134,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         moreInfoDrag.style.backgroundColor = textColor;
                         tab.element[0].style.background = !layer ? color : "dimgrey";
                     }, { fireImmediately: true });
-                // TODO:glr fix
-                // tab.element[0].style.borderTopRightRadius = "8px";
-                // tab.element[0].children[1].appendChild(toggle);
-                // tab._disposers.layerDisposer = reaction(() =>
-                //     ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
-                //     ({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true });
             }
             // shifts the focus to this tab when another tab is dragged over it
             tab.element[0].onmouseenter = (e: MouseEvent) => {
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index c32790df3..794126657 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -56,7 +56,7 @@ export namespace DashUploadUtils {
     const size = "content-length";
     const type = "content-type";
 
-    const { imageFormats, videoFormats, applicationFormats, audioFormats } = AcceptableMedia; //TODO:glr ASK BOB ABOUT THIS!
+    const { imageFormats, videoFormats, applicationFormats, audioFormats } = AcceptableMedia; //TODO:glr 
 
     export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> {
         console.log("UPLOAD " + videoId);
-- 
cgit v1.2.3-70-g09d2


From 59458f5c11e7e0891391da65a54269a56c5c71b6 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Tue, 31 Aug 2021 15:03:37 -0400
Subject: there will be issues as a result of this

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/CurrentUserUtils.ts                | 59 +++++++++--------
 src/client/views/MainView.tsx                      |  6 +-
 src/client/views/PropertiesView.scss               |  5 +-
 src/client/views/PropertiesView.tsx                | 19 +++---
 .../collectionLinearView/CollectionLinearView.tsx  |  2 +-
 src/client/views/nodes/button/ButtonInterface.ts   | 12 ++++
 src/client/views/nodes/button/FontIconBox.scss     |  4 ++
 src/client/views/nodes/button/FontIconBox.tsx      | 49 ++++++++++----
 .../nodes/button/colorDropdown/ColorDropdown.tsx   | 77 ++++++++++++++++++++++
 .../views/nodes/button/colorDropdown/index.ts      |  1 +
 .../views/nodes/button/textButton/TextButton.tsx   | 17 +++++
 src/client/views/nodes/button/textButton/index.ts  |  1 +
 .../nodes/button/toggleButton/ToggleButton.tsx     | 34 ++++++++++
 .../views/nodes/button/toggleButton/index.ts       |  1 +
 src/client/views/nodes/trails/PresBox.tsx          |  2 +-
 16 files changed, 234 insertions(+), 56 deletions(-)
 create mode 100644 src/client/views/nodes/button/ButtonInterface.ts
 create mode 100644 src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
 create mode 100644 src/client/views/nodes/button/colorDropdown/index.ts
 create mode 100644 src/client/views/nodes/button/textButton/TextButton.tsx
 create mode 100644 src/client/views/nodes/button/textButton/index.ts
 create mode 100644 src/client/views/nodes/button/toggleButton/ToggleButton.tsx
 create mode 100644 src/client/views/nodes/button/toggleButton/index.ts

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9d5dcb217..c4a713dc6 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -219,6 +219,7 @@ export class DocumentOptions {
     _backgroundGridShow?: boolean;
 
     //BUTTONS
+    buttonText?: string;
     iconShape?: string; // shapes of the fonticon border
     btnType?: string;
     btnList?: List<string>;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index aebcbcdff..cc0c19ab0 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -576,10 +576,10 @@ export class CurrentUserUtils {
             { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
             { title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' },
             { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
-            { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+            { title: "Import", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
-            { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
+            { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
             // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
             { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
@@ -815,32 +815,34 @@ export class CurrentUserUtils {
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true
             }));
             const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
-            const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
-            const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-            const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
+            // const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
+            // const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
+            // const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
             const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`);
             const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
-            (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
-            (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
+            (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]);
+            (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]);
+            // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
+            // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
         }
         return doc.myDashboards as any as Doc;
     }
 
     static async setupPresentations(doc: Doc) {
-        await doc.myPresentations;
-        if (doc.myPresentations === undefined) {
-            doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
+        await doc.myTrails;
+        if (doc.myTrails === undefined) {
+            doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Trails", _showTitle: "title", _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
             }));
             const newPresentations = ScriptField.MakeScript(`createNewPresentation()`);
-            (doc.myPresentations as any as Doc).contextMenuScripts = new List<ScriptField>([newPresentations!]);
-            (doc.myPresentations as any as Doc).contextMenuLabels = new List<string>(["Create New Presentation"]);
-            const presentations = doc.myPresentations as any as Doc;
+            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newPresentations!]);
+            (doc.myTrails as any as Doc).contextMenuLabels = new List<string>(["Create New Trail"]);
+            const presentations = doc.myTrails as any as Doc;
         }
-        return doc.myPresentations as any as Doc;
+        return doc.myTrails as any as Doc;
     }
 
     static async setupFilesystem(doc: Doc) {
@@ -862,8 +864,9 @@ export class CurrentUserUtils {
     static setupRecentlyClosedDocs(doc: Doc) {
         // setup Recently Closed library item
         if (doc.myRecentlyClosedDocs === undefined) {
-            doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true,
+            const clearDocs = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript(`getProto(self).data = new List([])`), toolTip: "Empty", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.TextButton, _width: 200, buttonText: "Empty Recently Closed", icon: "trash", system: true });
+            doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([clearDocs], {
+                title: "My Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
@@ -878,7 +881,7 @@ export class CurrentUserUtils {
         // setup Filter item
         if (doc.currentFilter === undefined) {
             doc.currentFilter = Docs.Create.FilterDocument({
-                title: "unnamed filter", _height: 150,
+                title: "Unnamed Filter", _height: 150,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "none",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, _autoHeight: true, _fitWidth: true
@@ -982,8 +985,12 @@ export class CurrentUserUtils {
             { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', checkResult: 'setActiveInkTool("circle" , true)' },
             // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', checkResult: 'setActiveInkTool("square" , true)' },
             { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', checkResult: 'setActiveInkTool("line" , true)' },
+            {
+                title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
+                script: "setBackgroundColor"
+            },
             { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth' },
-            { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "minus", ignoreClick: true, script: 'setStrokeColor' },
+            { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor' },
         ];
         return tools;
     }
@@ -1027,7 +1034,7 @@ export class CurrentUserUtils {
                 script: 'setView',
             }, // Always show
             {
-                title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
+                title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
                 script: "setBackgroundColor", hidden: 'selectedDocumentType()'
             }, // Only when a document is selected
             { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', checkResult: 'toggleOverlay(true)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
@@ -1192,16 +1199,12 @@ export class CurrentUserUtils {
     // Import sidebar is where shared documents are contained
     static setupImportSidebar(doc: Doc) {
         if (doc.myImportDocs === undefined) {
-            doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
+            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _width: 100, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.TextButton, _width: 200, buttonText: "Upload From Computer", icon: "upload", system: true });
+            doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([newUpload], {
                 title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
-                childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, _lockedPosition: true, system: true, _chromeHidden: true,
+                childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
             }));
         }
-        if (doc.myImportPanel === undefined) {
-            const uploads = Cast(doc.myImportDocs, Doc, null);
-            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ToolButton, icon: "upload", system: true });
-            doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" }));
-        }
     }
 
     // Search sidebar is where searches within the document are performed
@@ -1209,7 +1212,7 @@ export class CurrentUserUtils {
         if (doc.mySearchPanel === undefined) {
             doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
                 backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
-                childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Sidebar", system: true
+                childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true
             })) as any as Doc;
         }
     }
@@ -1453,7 +1456,7 @@ export class CurrentUserUtils {
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
-        const myPresentations = await userDoc.myPresentations as Doc;
+        const myTrails = await userDoc.myTrails as Doc;
         const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
         const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9047951ee..60016fc3d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -227,8 +227,8 @@ export class MainView extends React.Component {
 
     @action
     createNewPresentation = async () => {
-        if (!await this.userDoc.myPresentations) {
-            this.userDoc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
+        if (!await this.userDoc.myTrails) {
+            this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "PRESENTATION TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
             }));
         }
@@ -236,7 +236,7 @@ export class MainView extends React.Component {
             { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "right");
         this.userDoc.activePresentation = pres;
-        Doc.AddDocToList(this.userDoc.myPresentations as Doc, "data", pres);
+        Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
     }
 
     getPWidth = () => this._panelWidth - this.propertiesWidth();
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 321b83f52..554f137cb 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -825,9 +825,8 @@
 }
 
 .editable-title {
-    padding: 6px;
-    padding-bottom: 2px;
-    border: solid 1px $dark-gray;
+    border: solid 1px #323232;
+    height: fit-content;
 
     &:hover {
         border: 0.75px solid $medium-blue;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 17b137c31..09e12443b 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -2,17 +2,19 @@ import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Checkbox, Tooltip } from "@material-ui/core";
 import { intersection } from "lodash";
-import { action, autorun, computed, Lambda, observable, reaction, runInAction } from "mobx";
+import { action, autorun, computed, Lambda, observable } from "mobx";
 import { observer } from "mobx-react";
 import { ColorState, SketchPicker } from "react-color";
-import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym, AclSelfEdit } from "../../fields/Doc";
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSelfEdit, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { InkField } from "../../fields/InkField";
+import { List } from "../../fields/List";
 import { ComputedField } from "../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
 import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from "../../fields/util";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
 import { DocumentType } from "../documents/DocumentTypes";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { DocumentManager } from "../util/DocumentManager";
 import { SelectionManager } from "../util/SelectionManager";
 import { SharingManager } from "../util/SharingManager";
@@ -29,8 +31,6 @@ import { PropertiesButtons } from "./PropertiesButtons";
 import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
 import "./PropertiesView.scss";
 import { DefaultStyleProvider } from "./StyleProvider";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { List } from "../../fields/List";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -1086,6 +1086,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     // }
 
     render() {
+        const isNovice = BoolCast(Doc.UserDoc().noviceMode);
         if (!this.selectedDoc && !this.isPres) {
             return <div className="propertiesView" style={{ width: this.props.width }}>
                 <div className="propertiesView-title" style={{ width: this.props.width }}>
@@ -1110,15 +1111,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
                     {this.sharingSubMenu}
 
-                    {this.filtersSubMenu}
+                    {isNovice ? null : this.filtersSubMenu}
 
                     {this.inkSubMenu}
 
-                    {this.fieldsSubMenu}
+                    {isNovice ? null : this.fieldsSubMenu}
 
-                    {this.contextsSubMenu}
+                    {isNovice ? null : this.contextsSubMenu}
 
-                    {this.layoutSubMenu}
+                    {isNovice ? null : this.layoutSubMenu}
                 </div>;
             }
             if (this.isPres) {
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
index 6b1fd9e1b..f3ffb4e0b 100644
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
@@ -126,7 +126,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
             }}
             onPointerDown={e => e.stopPropagation()} >
             <div className="collectionLinearView-menuOpener">
-                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"plus"}/> : icon ? icon :  <FontAwesomeIcon icon={"minus"}/>}
+                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"}/> : icon ? icon :  <FontAwesomeIcon icon={"plus"}/>}
             </div>
         </label>;
 
diff --git a/src/client/views/nodes/button/ButtonInterface.ts b/src/client/views/nodes/button/ButtonInterface.ts
new file mode 100644
index 000000000..0aa2ac8e1
--- /dev/null
+++ b/src/client/views/nodes/button/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/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index b080f1dab..48fb2c8dc 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -49,6 +49,10 @@
         }
     }
 
+    &.textBtn {
+        width: 100%;
+    }
+
     &.tglBtn {
         cursor: pointer;
 
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index a6887cbba..96db929eb 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -27,11 +27,15 @@ import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
 import './FontIconBox.scss';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
+import { TextButton } from './textButton';
+import { ToggleButton } from './toggleButton';
+import { IButtonProps } from './ButtonInterface';
 const FontIconSchema = createSchema({
     icon: "string",
 });
 
 export enum ButtonType {
+    TextButton = "textBtn",
     MenuButton = "menuBtn",
     DropdownList = "drpdownList",
     DropdownButton = "drpdownBtn",
@@ -331,13 +335,13 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         let stroke: boolean = false;
         let strokeIcon: any;
-        if (script === "setStrokeColor") {
-            stroke = true;
-            const checkWidth = ScriptField.MakeScript("setStrokeWidth(0, true)")?.script.run().result;
-            const width = 20 + (checkWidth / 100) * 70;
-            const height = 20 + (checkWidth / 100) * 70;
-            strokeIcon = (<div style={{ borderRadius: "100%", width: width + '%', height: height + '%', backgroundColor: boolResult ? boolResult : "#FFFFFF" }} />);
-        }
+        // if (script === "setStrokeColor") {
+        //     stroke = true;
+        //     const checkWidth = ScriptField.MakeScript("setStrokeWidth(0, true)")?.script.run().result;
+        //     const width = 20 + (checkWidth / 100) * 70;
+        //     const height = 20 + (checkWidth / 100) * 70;
+        //     strokeIcon = (<div style={{ borderRadius: "100%", width: width + '%', height: height + '%', backgroundColor: boolResult ? boolResult : "#FFFFFF" }} />);
+        // }
 
         const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
             '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -473,25 +477,47 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
     render() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        // Variables called through eval (from button)
-        const numSelected = SelectionManager.Views().length;
-
         const dark: boolean = Doc.UserDoc().colorScheme === ColorScheme.Dark;
 
-        const active: string = StrCast(this.rootDoc.dropDownOpen);
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
             <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
+
         const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
             <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
                 {this.label}
             </div>;
 
+        const buttonProps:IButtonProps = {
+            type: this.type,
+            rootDoc: this.rootDoc,
+            label: label,
+            backgroundColor: backgroundColor,
+            icon: this.icon,
+            color: color
+        }
+
+        const buttonText = StrCast(this.rootDoc.buttonText);
+
         // TODO:glr Add label of button type
         let button = this.defaultButton;
 
         switch (this.type) {
+            case ButtonType.TextButton:
+                button = (
+                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1 }}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                        {buttonText ? 
+                            <div className="button-text">
+                                    {buttonText}
+                            </div>
+                            : null}
+                        {label}
+                    </div>
+                );
+                // button = <TextButton {...buttonProps}></TextButton>
+                break;
             case ButtonType.EditableText:
                 console.log("Editable text");
                 button = this.editableText;
@@ -518,6 +544,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 break;
             case ButtonType.ToggleButton:
                 button = this.toggleButton;
+                // button = <ToggleButton {...buttonProps}></ToggleButton>
                 break;
             case ButtonType.ClickButton:
                 button = (
diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
new file mode 100644
index 000000000..1809f4e2e
--- /dev/null
+++ b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
@@ -0,0 +1,77 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import React, { Component } from 'react';
+import { BoolCast, StrCast } from '../../../../../fields/Types';
+import { IButtonProps } from '../ButtonInterface';
+import { ColorState, SketchPicker } from 'react-color';
+import { ScriptField } from '../../../../../fields/ScriptField';
+import { Doc } from '../../../../../fields/Doc';
+
+export class ColorDropdown extends Component<IButtonProps> {
+    render() {
+        const active: string = StrCast(this.props.rootDoc.dropDownOpen);
+
+        const script: string = StrCast(this.props.rootDoc.script);
+        const scriptCheck: string = script + "(undefined, true)";
+        const boolResult = ScriptField.MakeScript(scriptCheck)?.script.run().result;
+
+        let stroke: boolean = false;
+        let strokeIcon: any;
+        // if (script === "setStrokeColor") {
+        //     stroke = true;
+        //     const checkWidth = ScriptField.MakeScript("setStrokeWidth(0, true)")?.script.run().result;
+        //     const width = 20 + (checkWidth / 100) * 70;
+        //     const height = 20 + (checkWidth / 100) * 70;
+        //     strokeIcon = (<div style={{ borderRadius: "100%", width: width + '%', height: height + '%', backgroundColor: boolResult ? boolResult : "#FFFFFF" }} />);
+        // }
+
+        const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
+            '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
+            '#FFFFFF', '#f1efeb'];
+
+        const colorBox = (func: (color: ColorState) => void) => <SketchPicker
+            disableAlpha={!stroke}
+            onChange={func} color={boolResult ? boolResult : "#FFFFFF"}
+            presetColors={colorOptions} />;
+        const label = !this.props.label || !Doc.UserDoc()._showLabel ? (null) :
+            <div className="fontIconBox-label" style={{ color: this.props.color, backgroundColor: this.props.backgroundColor, position: "absolute" }}>
+                {this.props.label}
+            </div>;
+
+        const dropdownCaret = <div
+            className="menuButton-dropDown"
+            style={{ borderBottomRightRadius: active ? 0 : undefined }}>
+            <FontAwesomeIcon icon={'caret-down'} color={this.props.color} size="sm" />
+        </div>;
+
+        const click = (value: ColorState) => {
+            const hex: string = value.hex;
+            const s = ScriptField.MakeScript(script + '("' + hex + '", false)');
+            if (s) {
+                s.script.run().result;
+            }
+        };
+        return (
+            <div className={`menuButton ${this.props.type} ${active}`}
+                style={{ color: this.props.color, borderBottomLeftRadius: active ? 0 : undefined }}
+                onClick={() => this.props.rootDoc.dropDownOpen = !this.props.rootDoc.dropDownOpen}
+                onPointerDown={e => e.stopPropagation()}>
+                {stroke ? strokeIcon : <><FontAwesomeIcon className={`fontIconBox-icon-${this.props.type}`} icon={this.props.icon} color={this.props.color} />
+                    <div className="colorButton-color"
+                        style={{ backgroundColor: boolResult ? boolResult : "#FFFFFF" }}
+                    ></div></>}
+                {label}
+                {/* {dropdownCaret} */}
+                {this.props.rootDoc.dropDownOpen ?
+                    <div>
+                        <div className="menuButton-dropdownBox"
+                            onPointerDown={e => e.stopPropagation()}
+                            onClick={e => e.stopPropagation()}>
+                            {colorBox(click)}
+                        </div>
+                        <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.props.rootDoc.dropDownOpen = false; }} />
+                    </div>
+                    : null}
+            </div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/button/colorDropdown/index.ts b/src/client/views/nodes/button/colorDropdown/index.ts
new file mode 100644
index 000000000..1147d6457
--- /dev/null
+++ b/src/client/views/nodes/button/colorDropdown/index.ts
@@ -0,0 +1 @@
+export * from './ColorDropdown';
\ No newline at end of file
diff --git a/src/client/views/nodes/button/textButton/TextButton.tsx b/src/client/views/nodes/button/textButton/TextButton.tsx
new file mode 100644
index 000000000..414b50dcb
--- /dev/null
+++ b/src/client/views/nodes/button/textButton/TextButton.tsx
@@ -0,0 +1,17 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import React, { Component } from 'react';
+import { BoolCast } from '../../../../../fields/Types';
+import { IButtonProps } from '../ButtonInterface';
+
+export class TextButton extends Component<IButtonProps> {
+    render() {
+        const type = this.props.type;
+        // Determine the type of toggle button
+        const buttonText: boolean = BoolCast(this.props.rootDoc.switchToggle);
+
+        return (<div className={`menuButton ${this.props.type}`} style={{ opacity: 1, backgroundColor: this.props.backgroundColor, color: this.props.color }}>
+            <FontAwesomeIcon className={`fontIconBox-icon-${this.props.type}`} icon={this.props.icon} color={this.props.color} />
+            {this.props.label}
+        </div>)
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/button/textButton/index.ts b/src/client/views/nodes/button/textButton/index.ts
new file mode 100644
index 000000000..01fcde54f
--- /dev/null
+++ b/src/client/views/nodes/button/textButton/index.ts
@@ -0,0 +1 @@
+export * from './TextButton'
\ No newline at end of file
diff --git a/src/client/views/nodes/button/toggleButton/ToggleButton.tsx b/src/client/views/nodes/button/toggleButton/ToggleButton.tsx
new file mode 100644
index 000000000..dca6487d8
--- /dev/null
+++ b/src/client/views/nodes/button/toggleButton/ToggleButton.tsx
@@ -0,0 +1,34 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import React, { Component } from 'react';
+import { BoolCast } from '../../../../../fields/Types';
+import { Colors } from '../../../global/globalEnums';
+import { IButtonProps } from '../ButtonInterface';
+
+export class ToggleButton extends Component<IButtonProps> {
+    render() {
+        const type = this.props.type;
+        // Determine the type of toggle button
+        const switchToggle: boolean = BoolCast(this.props.rootDoc.switchToggle);
+
+        if (switchToggle) {
+            return (
+                <div className={`menuButton ${type} ${'switch'}`}>
+                    <label className="switch">
+                        <input type="checkbox"
+                            checked={this.props.backgroundColor === Colors.MEDIUM_BLUE}
+                        />
+                        <span className="slider round"></span>
+                    </label>
+                </div>
+            );
+        } else {
+            return (
+                <div className={`menuButton ${type}`}
+                    style={{ opacity: 1, backgroundColor: this.props.backgroundColor, color: this.props.color }}>
+                    <FontAwesomeIcon className={`fontIconBox-icon-${type}`} icon={this.props.icon} color={this.props.color} />
+                    {this.props.label}
+                </div>
+            );
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/button/toggleButton/index.ts b/src/client/views/nodes/button/toggleButton/index.ts
new file mode 100644
index 000000000..48b28fb4c
--- /dev/null
+++ b/src/client/views/nodes/button/toggleButton/index.ts
@@ -0,0 +1 @@
+export * from './ToggleButton'
\ No newline at end of file
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index cfc3e75cc..b805aa6ae 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -166,7 +166,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         this.layoutDoc._gridGap = 0;
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
-        DocListCastAsync((Doc.UserDoc().myPresentations as Doc).data).then(pres =>
+        DocListCastAsync((Doc.UserDoc().myTrails as Doc).data).then(pres =>
             !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc));
         this._disposers.selection = reaction(() => SelectionManager.Views(),
             views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation());
-- 
cgit v1.2.3-70-g09d2


From 4ba82ba609d51354958e8346c65673c4cf172c52 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Wed, 1 Sep 2021 15:55:28 -0400
Subject: making more changes

---
 src/client/util/CurrentUserUtils.ts           | 6 ++----
 src/client/views/MainView.tsx                 | 6 +++---
 src/client/views/nodes/button/FontIconBox.tsx | 4 +++-
 3 files changed, 8 insertions(+), 8 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index afe3940b6..f12ac02f3 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -580,8 +580,6 @@ export class CurrentUserUtils {
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
             { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
             { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
-            // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
-            // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
             { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
         ];
     }
@@ -1199,9 +1197,9 @@ export class CurrentUserUtils {
     // Import sidebar is where shared documents are contained
     static setupImportSidebar(doc: Doc) {
         if (doc.myImportDocs === undefined) {
-            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _width: 100, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.TextButton, _width: 200, buttonText: "Upload From Computer", icon: "upload", system: true });
+            const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Upload from computer", _width: 100, _stayInCollection: true, _hideContextMenu: true, title: "Upload", btnType: ButtonType.TextButton, buttonText: "Upload from computer", icon: "upload", system: true });
             doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([newUpload], {
-                title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
+                title: "My Uploads", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
                 childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
             }));
         }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 60016fc3d..9047951ee 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -227,8 +227,8 @@ export class MainView extends React.Component {
 
     @action
     createNewPresentation = async () => {
-        if (!await this.userDoc.myTrails) {
-            this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
+        if (!await this.userDoc.myPresentations) {
+            this.userDoc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "PRESENTATION TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
             }));
         }
@@ -236,7 +236,7 @@ export class MainView extends React.Component {
             { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "right");
         this.userDoc.activePresentation = pres;
-        Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
+        Doc.AddDocToList(this.userDoc.myPresentations as Doc, "data", pres);
     }
 
     getPWidth = () => this._panelWidth - this.propertiesWidth();
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index fbb336445..bccf733f0 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -557,9 +557,11 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 );
                 break;
             case ButtonType.MenuButton:
+                const trailsIcon = <img src={`/assets/${"presTrails.png"}`}
+                    style={{ width: 20, height: 20, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />;
                 button = (
                     <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                        {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
                         {menuLabel}
                         <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
                     </div >
-- 
cgit v1.2.3-70-g09d2


From aee30b5ffc52e90559fa3c1786ce477943abcfb3 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Thu, 2 Sep 2021 06:23:15 -0400
Subject: Placing collection menu in Lightbox (commented out) and others

- Set toggle properties off
- added link that goes directly to the dash documentation
---
 src/client/util/CurrentUserUtils.ts                | 36 ++++++++++++++--------
 src/client/views/LightboxView.tsx                  |  3 ++
 src/client/views/MainView.tsx                      | 12 ++++----
 src/client/views/PropertiesButtons.scss            |  9 +++---
 src/client/views/PropertiesButtons.tsx             |  8 ++---
 .../views/collections/CollectionStackingView.tsx   |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  4 ++-
 src/client/views/nodes/trails/PresBox.tsx          |  4 +--
 8 files changed, 47 insertions(+), 31 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index f6ec74482..56807c63b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -55,6 +55,7 @@ interface Button {
     width?: number;
     list?: string[];
     ignoreClick?: boolean;
+    buttonText?: string;
 }
 
 export let resolvedPorts: { server: number, socket: number };
@@ -830,16 +831,17 @@ export class CurrentUserUtils {
     static async setupPresentations(doc: Doc) {
         await doc.myTrails;
         if (doc.myTrails === undefined) {
+            const newTrail = ScriptField.MakeScript(`createNewPresentation()`);
+            const newTrailButton:Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "New trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
             doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Trails", _showTitle: "title", _height: 100,
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
+                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+                treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newTrailButton,
+                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
+                explainer: "All of the trails that you have created will appear here."
             }));
-            const newPresentations = ScriptField.MakeScript(`createNewPresentation()`);
-            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newPresentations!]);
+            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newTrail!]);
             (doc.myTrails as any as Doc).contextMenuLabels = new List<string>(["Create New Trail"]);
-            const presentations = doc.myTrails as any as Doc;
         }
         return doc.myTrails as any as Doc;
     }
@@ -848,14 +850,19 @@ export class CurrentUserUtils {
         await doc.myFilesystem;
         if (doc.myFilesystem === undefined) {
             doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
-            doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true });
-            doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileRoot as Doc, doc.myFileOrphans as Doc], {
-                title: "My Documents", _showTitle: "title", _height: 100,
+            // doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true });
+            const newFolder = ScriptField.MakeFunction(`doc.makeFolder()`, { doc: doc.myFilesystem })!;
+            const newFolderButton:Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
+            doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], {
+                title: "My Documents", _showTitle: "title",  buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true
+                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
+                explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
             }));
+            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder!]);
+            (doc.myTrails as any as Doc).contextMenuLabels = new List<string>(["Create new folder"]);
         }
         return doc.myFilesystem as any as Doc;
     }
@@ -869,7 +876,7 @@ export class CurrentUserUtils {
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
-                explainer: "Documents that you close will appear here so you can easily access them again. You can choose to clear this menu in which case you might lose these documents."
+                explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
 
             }));
             (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
@@ -1004,6 +1011,9 @@ export class CurrentUserUtils {
                     title: "Show preview",
                     toolTip: "Show preview of selected document",
                     btnType: ButtonType.ToggleButton,
+                    switchToggle: true,
+                    width: 100,
+                    buttonText: "Show Preview",
                     icon: "eye",
                     click: 'toggleSchemaPreview()',
                     checkResult: 'toggleSchemaPreview(true)'
@@ -1184,7 +1194,7 @@ export class CurrentUserUtils {
                     title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
                     _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
                     _chromeHidden: true, boxShadow: "0 0",
-                    explainer: "All of the documents that you receive will appear here. They must be shared by other users. If you receive a Dashboard you can add it to your Dashboards by right clicking and clicking 'Add to Dashboards'"
+                    explainer: "This is where documents or dashboards that other users have shared with you will appear."
                 }, sharingDocumentId + "outer", sharingDocumentId);
                 (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment;
             }
@@ -1209,7 +1219,7 @@ export class CurrentUserUtils {
             doc.myUploadDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
                 title: "My Uploads", _forceActive: true, buttonMenu: true, buttonMenuDoc: newUploadButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
                 childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
-                explainer: "All of the documents that are imported into Dash will go here and stay here unless they are explicitly removed."
+                explainer: "This is where documents that are uploaded into Dash will go."
             }));
         }
     }
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 88739fe91..ec30a6a5d 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -16,6 +16,7 @@ import { TabDocView } from './collections/TabDocView';
 import "./LightboxView.scss";
 import { DocumentView } from './nodes/DocumentView';
 import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
+import { CollectionMenu } from './collections/CollectionMenu';
 
 interface LightboxViewProps {
     PanelWidth: number;
@@ -213,6 +214,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         LightboxView.SetLightboxDoc(undefined);
                     }
                 }}  >
+                
                 <div className="lightboxView-contents" style={{
                     left: this.leftBorder,
                     top: this.topBorder,
@@ -220,6 +222,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                     height: this.lightboxHeight(),
                     clipPath: `path('${Doc.UserDoc().renderStyle === "comic" ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`
                 }}>
+                    {/* <CollectionMenu /> TODO:glr This is where it would go*/}
                     <DocumentView ref={action((r: DocumentView | null) => {
                         LightboxView._docView = r !== null ? r : undefined;
                         r && setTimeout(action(() => {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9047951ee..440bb6bd5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -173,7 +173,7 @@ export class MainView extends React.Component {
             fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
             fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
             fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-            fa.faSave, fa.faBookmark, fa.faList, fa.faListOl);
+            fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen);
         this.initAuthenticationRouters();
     }
 
@@ -227,16 +227,16 @@ export class MainView extends React.Component {
 
     @action
     createNewPresentation = async () => {
-        if (!await this.userDoc.myPresentations) {
-            this.userDoc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "PRESENTATION TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
+        if (!await this.userDoc.myTrails) {
+            this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
             }));
         }
         const pres = Docs.Create.PresDocument(new List<Doc>(),
-            { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
+            { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "right");
         this.userDoc.activePresentation = pres;
-        Doc.AddDocToList(this.userDoc.myPresentations as Doc, "data", pres);
+        Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
     }
 
     getPWidth = () => this._panelWidth - this.propertiesWidth();
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
index 77686a710..36b2df73e 100644
--- a/src/client/views/PropertiesButtons.scss
+++ b/src/client/views/PropertiesButtons.scss
@@ -44,14 +44,15 @@ $linkGap : 3px;
     }
 }
 .propertiesButtons-linkButton-empty.toggle-on {
-    background-color: white;
-    color: $dark-gray;
+    background-color: $medium-blue;
+    color: $white;
 }
 .propertiesButtons-linkButton-empty.toggle-hover {
-    background-color: gray;
-    color: $dark-gray;
+    background-color: $light-blue;
+    color: $black;
 }
 .propertiesButtons-linkButton-empty.toggle-off {
+    background-color: $dark-gray;
     color: white;
 }
 
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 4fde1124f..286365fa6 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -66,10 +66,10 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return this.propertyToggleBtn("Lock\xA0View", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => "lock");
     }
     @computed get fitContentButton() {
-        return this.propertyToggleBtn("View All", "_fitToBox", on => `${on ? "Don't" : ""} fit content to container visible area`, on => "eye");
+        return this.propertyToggleBtn("View All", "_fitToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
     }
     @computed get fitWidthButton() {
-        return this.propertyToggleBtn("Fit\xA0Width", "_fitWidth", on => `${on ? "Don't" : ""} fit content to width of container`, on => "arrows-alt-h");
+        return this.propertyToggleBtn("Fit\xA0Width", "_fitWidth", on => `${on ? "Don't" : "Do"} fit content to width of container`, on => "arrows-alt-h");
     }
     @computed get captionButton() {
         return this.propertyToggleBtn("Caption", "_showCaption", on => `${on ? "Hide" : "Show"} caption footer`, on => "closed-captioning", (dv, doc) => (dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? "caption" : undefined);
@@ -211,13 +211,13 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.titleButton)}
                 {toggle(this.captionButton)}
                 {toggle(this.lockButton)}
-                {toggle(this.dictationButton)}
+                {toggle(this.dictationButton, { display: isNovice ? "none" : "" })}
                 {toggle(this.onClickButton)}
                 {toggle(this.fitWidthButton, { display: isNovice ? "none" : "" })}
                 {toggle(this.fitContentButton, { display: !isFreeForm ? "none" : "" })}
                 {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })}
                 {toggle(this.maskButton, { display: !isInk ? "none" : "" })}
-                {toggle(this.chromeButton, { display: isCollection ? "" : "none" })}
+                {toggle(this.chromeButton, { display: !isCollection || isNovice ? "none" : "" })}
                 {toggle(this.gridButton, { display: isCollection ? "" : "none" })}
                 {toggle(this.snapButton, { display: isCollection ? "" : "none" })}
                 {toggle(this.clustersButton, { display: !isFreeForm ? "none" : "" })}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 3c9084f64..c92b259d0 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -579,7 +579,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
             <>
                 {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
                     {buttonMenu ? this.buttonMenu : null}
-                    {Doc.UserDoc().isNovice && noviceExplainer ? 
+                    {Doc.UserDoc().noviceMode && noviceExplainer ? 
                         <div className="documentExplanation">
                             {noviceExplainer}
                         </div>
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 1b27d562a..b72f31ef8 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -400,7 +400,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
     @computed get toggleButton() {
         // Determine the type of toggle button
         const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle);
-
+        const buttonText: string = StrCast(this.rootDoc.buttonText);
         // Colors
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
@@ -414,6 +414,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         if (switchToggle) {
             return (
                 <div className={`menuButton ${this.type} ${'switch'}`}>
+                    {buttonText ? buttonText : null}
                     <label className="switch">
                         <input type="checkbox"
                             checked={backgroundColor === Colors.MEDIUM_BLUE}
@@ -880,6 +881,7 @@ Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
  **/
 Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined;
+    console.log(selected && selected.title);
     if (checkResult && selected) {
         const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
         if (result) return Colors.MEDIUM_BLUE;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index b805aa6ae..1abe26c20 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -167,7 +167,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
         DocListCastAsync((Doc.UserDoc().myTrails as Doc).data).then(pres =>
-            !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc));
+            !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myTrails as Doc, "data", this.rootDoc));
         this._disposers.selection = reaction(() => SelectionManager.Views(),
             views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation());
     }
@@ -2229,7 +2229,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
         const isMini: boolean = this.toolbarWidth <= 100;
         return (
             <div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? "none" : undefined }}>
-                {isMini ? (null) : <select className="presBox-viewPicker"
+                {isMini || Doc.UserDoc().noviceMode ? (null) : <select className="presBox-viewPicker"
                     style={{ display: this.layoutDoc.presStatus === "edit" ? "block" : "none" }}
                     onPointerDown={e => e.stopPropagation()}
                     onChange={this.viewChanged}
-- 
cgit v1.2.3-70-g09d2


From 6a2be14c25313058ff466ed7c2a6631126616263 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Thu, 2 Sep 2021 07:24:18 -0400
Subject: set fit width to true for presntation trails

---
 src/client/views/MainView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 440bb6bd5..dadf5b390 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -233,7 +233,7 @@ export class MainView extends React.Component {
             }));
         }
         const pres = Docs.Create.PresDocument(new List<Doc>(),
-            { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
+            { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "right");
         this.userDoc.activePresentation = pres;
         Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
-- 
cgit v1.2.3-70-g09d2


From 8cb650d9fb542465525c198c1f93f6aba7256e0b Mon Sep 17 00:00:00 2001
From: vkalev <vjk1883@gmail.com>
Date: Thu, 2 Sep 2021 12:04:02 -0400
Subject: working on moving ink UI to DocumentDecorations

---
 src/client/views/DocumentDecorations.tsx | 40 ++++++++++++++++++++++++++++----
 src/client/views/GestureOverlay.tsx      | 19 ++++++++-------
 src/client/views/InkingStroke.tsx        |  6 ++---
 src/client/views/MainView.tsx            |  3 ++-
 4 files changed, 50 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 118d2e7c7..244d14f3a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -28,9 +28,11 @@ import { DocumentView } from "./nodes/DocumentView";
 import React = require("react");
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { DateField } from '../../fields/DateField';
+import { InteractionUtils } from '../util/InteractionUtils';
+import { Colors } from './global/globalEnums';
 
 @observer
-export class DocumentDecorations extends React.Component<{ boundsLeft: number, boundsTop: number }, { value: string }> {
+export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> {
     static Instance: DocumentDecorations;
     private _resizeHdlId = "";
     private _keyinput = React.createRef<HTMLInputElement>();
@@ -391,10 +393,10 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
         this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
             .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
                 Doc.GetProto(doc).data = new InkField(inkPts.map(ipt =>  // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
-                    ({
-                        X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
-                        Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
-                    })));
+                ({
+                    X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
+                    Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
+                })));
                 Doc.SetNativeWidth(doc, undefined);
                 Doc.SetNativeHeight(doc, undefined);
             });
@@ -453,7 +455,34 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
         const borderRadiusDraggerWidth = 15;
         bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
         bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
+
         const useRotation = seldoc.rootDoc.type === DocumentType.INK;
+        const docView = SelectionManager.Views().lastElement().docView;
+        let selectedLine = null;
+
+        if (useRotation && docView) {
+            const inkData = Cast(SelectionManager.Views().lastElement().rootDoc.data, InkField)?.inkData ?? [{ X: 0, Y: 0 }];
+            const inkDoc = SelectionManager.Views().lastElement().layoutDoc;
+
+            inkData.map((point) => { docView.screenToLocal().inverse().transformPoint(point.X, point.Y); });
+
+            const strokeWidth = 4;
+            const lineTop = Math.min(...inkData.map(p => p.Y));
+            const lineBottom = Math.max(...inkData.map(p => p.Y));
+            const lineLeft = Math.min(...inkData.map(p => p.X));
+            const lineRight = Math.max(...inkData.map(p => p.X));
+            const left = lineLeft - strokeWidth / 2;
+            const top = lineTop - strokeWidth / 2;
+            const right = lineRight + strokeWidth / 2;
+            const bottom = lineBottom + strokeWidth / 2;
+            const width = Math.max(1, right - left);
+            const height = Math.max(1, bottom - top);
+            const scaleX = width === strokeWidth ? 1 : (this.props.PanelWidth - strokeWidth) / (width - strokeWidth);
+            const scaleY = height === strokeWidth ? 1 : (this.props.PanelHeight - strokeWidth) / (height - strokeWidth);
+
+            selectedLine = InteractionUtils.CreatePolyline(inkData, left, top, Colors.MEDIUM_BLUE, 4, 4, StrCast(inkDoc.strokeBezier), StrCast(inkDoc.fillColor, "none"),
+                StrCast(inkDoc.strokeStartMarker), StrCast(inkDoc.strokeEndMarker), StrCast(inkDoc.strokeDash), 1, 1, "", "none", 1.0, false);
+        }
 
         return (<div className="documentDecorations" style={{ background: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : "" }} >
             <div className="documentDecorations-background" style={{
@@ -473,6 +502,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
                 }}>
                     {!canDelete ? <div /> : topBtn("close", "times", undefined, this.onCloseClick, "Close")}
                     {seldoc.props.hideDecorationTitle || seldoc.props.Document.type === DocumentType.EQUATION ? (null) : titleArea}
+                    {selectedLine}
                     {seldoc.props.hideResizeHandles || seldoc.props.Document.type === DocumentType.EQUATION ? (null) :
                         <>
                             {SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index c77521572..d0059dacc 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -688,25 +688,26 @@ export class GestureOverlay extends Touchable {
             case "rectangle":
                 this._points.push({ X: left, Y: top });
                 this._points.push({ X: left, Y: top });
-
                 this._points.push({ X: right, Y: top });
                 this._points.push({ X: right, Y: top });
+
                 this._points.push({ X: right, Y: top });
                 this._points.push({ X: right, Y: top });
-
                 this._points.push({ X: right, Y: bottom });
                 this._points.push({ X: right, Y: bottom });
+
                 this._points.push({ X: right, Y: bottom });
                 this._points.push({ X: right, Y: bottom });
-
                 this._points.push({ X: left, Y: bottom });
                 this._points.push({ X: left, Y: bottom });
+
                 this._points.push({ X: left, Y: bottom });
                 this._points.push({ X: left, Y: bottom });
-
                 this._points.push({ X: left, Y: top });
                 this._points.push({ X: left, Y: top });
+
                 break;
+                
             case "triangle":
                 this._points.push({ X: left, Y: bottom });
                 this._points.push({ X: left, Y: bottom });
@@ -736,11 +737,6 @@ export class GestureOverlay extends Touchable {
                 const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom));
 
                 // Dividing the circle into four equal sections, and fitting each section to a cubic Bézier curve.   
-                this._points.push({ X: centerX - radius, Y: centerY });
-                this._points.push({ X: centerX - radius, Y: centerY + (c * radius) });
-                this._points.push({ X: centerX - (c * radius), Y: centerY + radius });
-                this._points.push({ X: centerX, Y: centerY + radius });
-
                 this._points.push({ X: centerX, Y: centerY + radius });
                 this._points.push({ X: centerX + (c * radius), Y: centerY + radius });
                 this._points.push({ X: centerX + radius, Y: centerY + (c * radius) });
@@ -756,6 +752,11 @@ export class GestureOverlay extends Touchable {
                 this._points.push({ X: centerX - radius, Y: centerY - (c * radius) });
                 this._points.push({ X: centerX - radius, Y: centerY });
 
+                this._points.push({ X: centerX - radius, Y: centerY });
+                this._points.push({ X: centerX - radius, Y: centerY + (c * radius) });
+                this._points.push({ X: centerX - (c * radius), Y: centerY + radius });
+                this._points.push({ X: centerX, Y: centerY + radius });
+
                 break;
 
             case "line":
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 7200d6da1..b619f858e 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -115,8 +115,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const inkLine = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), StrCast(this.layoutDoc.strokeStartMarker),
             StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", 1.0, false);
         // Thin blue line indicating that the current ink stroke is selected.
-        const selectedLine = InteractionUtils.CreatePolyline(data, left, top, Colors.MEDIUM_BLUE, strokeWidth, strokeWidth / 6, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
-            StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", 1.0, false);
+        // const selectedLine = InteractionUtils.CreatePolyline(data, left, top, Colors.MEDIUM_BLUE, strokeWidth, strokeWidth / 6, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"),
+        //     StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", 1.0, false);
         // Invisible polygonal line that enables the ink to be selected by the user.
         const clickableLine = InteractionUtils.CreatePolyline(data, left, top, "transparent", strokeWidth, strokeWidth + 15, StrCast(this.layoutDoc.strokeBezier),
             StrCast(this.layoutDoc.fillColor, "none"), "none", "none", undefined, scaleX, scaleY, "", this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted", 0.0, true);
@@ -145,7 +145,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
 
                 {clickableLine}
                 {inkLine}
-                {this.props.isSelected() ? selectedLine : ""}
+                {/* {this.props.isSelected() ? selectedLine : ""} */}
                 {this.props.isSelected() && this._properties?._controlButton ?
                     <>
                         <InkControls
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b0b8d7f41..1ce363b0a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -65,6 +65,7 @@ import { SearchBox } from './search/SearchBox';
 import { DefaultStyleProvider, DashboardStyleProvider, StyleProp } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
 import { Colors } from './global/globalEnums';
+import { DocumentType } from '../documents/DocumentTypes';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -592,7 +593,7 @@ export class MainView extends React.Component {
             <CaptureManager />
             <GroupManager />
             <GoogleAuthenticationManager />
-            <DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
+            <DocumentDecorations PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
             {this.search}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
-- 
cgit v1.2.3-70-g09d2


From 15bc374105eb7c55493d9ca6c2b12f9bf22735c9 Mon Sep 17 00:00:00 2001
From: vkalev <vjk1883@gmail.com>
Date: Fri, 3 Sep 2021 13:18:46 -0400
Subject: adding ComponentDecorations

---
 src/client/views/ComponentDecorations.scss |   0
 src/client/views/ComponentDecorations.tsx  |  14 +
 src/client/views/DocumentDecorations.scss  | 631 ++++++++++++++---------------
 src/client/views/DocumentDecorations.tsx   |  19 -
 src/client/views/InkStroke.scss            |  11 +
 src/client/views/InkingStroke.tsx          |  18 +-
 src/client/views/MainView.tsx              |   2 +
 src/client/views/nodes/DocumentView.tsx    |   2 +-
 8 files changed, 354 insertions(+), 343 deletions(-)
 create mode 100644 src/client/views/ComponentDecorations.scss
 create mode 100644 src/client/views/ComponentDecorations.tsx

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

diff --git a/src/client/views/ComponentDecorations.scss b/src/client/views/ComponentDecorations.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/client/views/ComponentDecorations.tsx b/src/client/views/ComponentDecorations.tsx
new file mode 100644
index 000000000..66f81fe4f
--- /dev/null
+++ b/src/client/views/ComponentDecorations.tsx
@@ -0,0 +1,14 @@
+import { observer } from "mobx-react";
+import { SelectionManager } from "../util/SelectionManager";
+import './ComponentDecorations.scss';
+import React = require("react");
+
+@observer
+export class ComponentDecorations extends React.Component {
+    static Instance: ComponentDecorations;
+
+    render() {
+        const seldoc = SelectionManager.Views().lastElement();
+        return seldoc?.ComponentView?.componentUI?.() ?? (null);
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 68e0d12d4..2e7142a8e 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -1,419 +1,408 @@
 @import "global/globalCssVariables";
 
-$linkGap : 3px;
+$linkGap: 3px;
 
 .documentDecorations {
-    position: absolute;
-    z-index: 2000;
-}
-.documentDecorations-inkstroke {
-    position: absolute;
-    overflow: visible;
-    pointer-events: none;
-
-  svg:not(:root) {
-    overflow: visible !important;
-  }
+  position: absolute;
+  z-index: 2000;
 }
 .documentDecorations-container {
-    z-index: $docDecorations-zindex;
+  z-index: $docDecorations-zindex;
+  position: absolute;
+  top: 0;
+  left: 0;
+  display: grid;
+  grid-template-rows: 20px 8px 1fr 8px;
+  grid-template-columns: 8px 16px 1fr 8px 8px;
+  pointer-events: none;
+
+  .documentDecorations-centerCont {
+    grid-column: 3;
+    background: none;
+  }
+
+  .documentDecorations-selectorButton {
+    pointer-events: auto;
+    height: 15px;
+    width: 15px;
+    left: -20px;
+    top: 20px;
+    display: inline-block;
     position: absolute;
-    top: 0;
-    left: 0;
-    display: grid;
-    grid-template-rows: 20px 8px 1fr 8px;
-    grid-template-columns: 8px 16px 1fr 8px 8px;
-    pointer-events: none;
-
-    .documentDecorations-centerCont {
-        grid-column: 3;
-        background: none;
-    }
+    opacity: 0.5;
+  }
 
-    .documentDecorations-selectorButton {
-        pointer-events: auto;
-        height: 15px;
-        width: 15px;
-        left: -20px;
-        top: 20px;
-        display: inline-block;
-        position: absolute;
-        opacity: 0.5;
-    }
+  .documentDecorations-radius {
+    pointer-events: auto;
+    opacity: 1;
+    transform: translate(10px, 10px);
+    grid-row: 4;
+  }
 
-    .documentDecorations-radius {
-        pointer-events: auto;
-        opacity: 1;
-        transform: translate(10px, 10px);
-        grid-row: 4;
+  .documentDecorations-topLeftResizer,
+  .documentDecorations-topRightResizer,
+  .documentDecorations-bottomLeftResizer,
+  .documentDecorations-bottomRightResizer,
+  .documentDecorations-leftResizer,
+  .documentDecorations-topResizer,
+  .documentDecorations-bottomResizer,
+  .documentDecorations-rightResizer {
+    pointer-events: auto;
+    background: $medium-gray;
+    opacity: 0.1;
+    &:hover {
+      opacity: 1;
     }
+  }
 
-    .documentDecorations-topLeftResizer,
-    .documentDecorations-topRightResizer,
-    .documentDecorations-bottomLeftResizer,
-    .documentDecorations-bottomRightResizer,
-    .documentDecorations-leftResizer,
-    .documentDecorations-topResizer,
-    .documentDecorations-bottomResizer,
-    .documentDecorations-rightResizer {
-        pointer-events: auto;
-        background: $medium-gray;
-        opacity: 0.1;
-        &:hover {
-            opacity: 1;
-        }
-    }
+  .documentDecorations-topLeftResizer,
+  .documentDecorations-leftResizer,
+  .documentDecorations-bottomLeftResizer {
+    grid-column: 1;
+  }
 
-    .documentDecorations-topLeftResizer,
-    .documentDecorations-leftResizer,
-    .documentDecorations-bottomLeftResizer {
-        grid-column: 1
-    }
+  .documentDecorations-topResizer,
+  .documentDecorations-bottomResizer {
+    grid-column-start: 2;
+    grid-column-end: 5;
+  }
 
-    .documentDecorations-topResizer,
-    .documentDecorations-bottomResizer {
-        grid-column-start: 2;
-        grid-column-end: 5;
-    }
+  .documentDecorations-bottomRightResizer,
+  .documentDecorations-topRightResizer,
+  .documentDecorations-rightResizer {
+    grid-column-start: 5;
+    grid-column-end: 7;
+  }
 
-    .documentDecorations-bottomRightResizer,
-    .documentDecorations-topRightResizer,
-    .documentDecorations-rightResizer {
-        grid-column-start: 5;
-        grid-column-end: 7;
-    }
+  .documentDecorations-rotation,
+  .documentDecorations-borderRadius {
+    grid-column: 5;
+    grid-row: 4;
+    border-radius: 100%;
+    background: black;
+    height: 8;
+    right: -12;
+    top: 12;
+    position: relative;
+    pointer-events: all;
+    cursor: nwse-resize;
 
-    .documentDecorations-rotation,
-    .documentDecorations-borderRadius {
-        grid-column: 5;
-        grid-row: 4;
-        border-radius: 100%;
-        background: black;
-        height: 8;
-        right: -12;
-        top: 12;
-        position: relative;
-        pointer-events: all;
-        cursor: nwse-resize;
-
-        .borderRadiusTooltip {
-            width: 10px;
-            height: 10px;
-            position: absolute;
-        }
+    .borderRadiusTooltip {
+      width: 10px;
+      height: 10px;
+      position: absolute;
     }
-    .documentDecorations-rotation {
-        background: transparent;
-        right: -15;
-    }
-
+  }
+  .documentDecorations-rotation {
+    background: transparent;
+    right: -15;
+  }
 
-    .documentDecorations-topLeftResizer,
-    .documentDecorations-bottomRightResizer {
-        cursor: nwse-resize;
-        background: unset;
-        opacity: 1;
-    }
+  .documentDecorations-topLeftResizer,
+  .documentDecorations-bottomRightResizer {
+    cursor: nwse-resize;
+    background: unset;
+    opacity: 1;
+  }
 
-    .documentDecorations-topLeftResizer {
-        border-left: 2px solid;
-        border-top: solid 2px;
-    }
+  .documentDecorations-topLeftResizer {
+    border-left: 2px solid;
+    border-top: solid 2px;
+  }
 
-    .documentDecorations-bottomRightResizer {
-        border-right: 2px solid;
-        border-bottom: solid 2px;
-    }
+  .documentDecorations-bottomRightResizer {
+    border-right: 2px solid;
+    border-bottom: solid 2px;
+  }
 
-    .documentDecorations-topLeftResizer:hover,
-    .documentDecorations-bottomRightResizer:hover {
-        opacity: 1;
-    }
+  .documentDecorations-topLeftResizer:hover,
+  .documentDecorations-bottomRightResizer:hover {
+    opacity: 1;
+  }
 
-    .documentDecorations-bottomRightResizer {
-        grid-row: 4;
-    }
+  .documentDecorations-bottomRightResizer {
+    grid-row: 4;
+  }
 
-    .documentDecorations-topRightResizer,
-    .documentDecorations-bottomLeftResizer {
-        cursor: nesw-resize;
-        background: unset;
-        opacity: 1;
-    }
+  .documentDecorations-topRightResizer,
+  .documentDecorations-bottomLeftResizer {
+    cursor: nesw-resize;
+    background: unset;
+    opacity: 1;
+  }
 
-    .documentDecorations-topRightResizer {
-        border-right: 2px solid;
-        border-top: 2px solid;
-    }
+  .documentDecorations-topRightResizer {
+    border-right: 2px solid;
+    border-top: 2px solid;
+  }
 
-    .documentDecorations-bottomLeftResizer {
-        border-left: 2px solid;
-        border-bottom: 2px solid;
-    }
+  .documentDecorations-bottomLeftResizer {
+    border-left: 2px solid;
+    border-bottom: 2px solid;
+  }
 
-    .documentDecorations-topRightResizer:hover,
-    .documentDecorations-bottomLeftResizer:hover {
-        cursor: nesw-resize;
-        background: black;
-        opacity: 1;
-    }
+  .documentDecorations-topRightResizer:hover,
+  .documentDecorations-bottomLeftResizer:hover {
+    cursor: nesw-resize;
+    background: black;
+    opacity: 1;
+  }
 
-    .documentDecorations-topResizer,
-    .documentDecorations-bottomResizer {
-        cursor: ns-resize;
-    }
+  .documentDecorations-topResizer,
+  .documentDecorations-bottomResizer {
+    cursor: ns-resize;
+  }
 
-    .documentDecorations-leftResizer,
-    .documentDecorations-rightResizer {
-        cursor: ew-resize;
-    }
+  .documentDecorations-leftResizer,
+  .documentDecorations-rightResizer {
+    cursor: ew-resize;
+  }
 
-    .documentDecorations-contextMenu {
-        width: 25px;
-        height: calc(100% + 8px); // 8px for the height of the top resizer bar
-        grid-column-start: 2;
-        grid-column-end: 2;
-        pointer-events: all;
-        padding-left: 5px;
-        cursor: pointer;
-    }
+  .documentDecorations-contextMenu {
+    width: 25px;
+    height: calc(100% + 8px); // 8px for the height of the top resizer bar
+    grid-column-start: 2;
+    grid-column-end: 2;
+    pointer-events: all;
+    padding-left: 5px;
+    cursor: pointer;
+  }
 
-    .documentDecorations-titleBackground {
-        background: #ffffffcf;
-        border-radius: 8px;
-        width: 100%;
-        height: 100%;
-        position: absolute;
-    }
+  .documentDecorations-titleBackground {
+    background: #ffffffcf;
+    border-radius: 8px;
+    width: 100%;
+    height: 100%;
+    position: absolute;
+  }
 
-    .documentDecorations-title {
-        opacity: 1;
-        grid-column-start: 2;
-        grid-column-end: 4;
-        pointer-events: auto;
-        overflow: hidden;
-        text-align: center;
-        display: flex;
-        margin-left: 5px;
-        height: 22px;
-        position: absolute;
-        .documentDecorations-titleSpan {
-            width: 100%;
-            border-radius: 8px;
-            background: #ffffffcf;
-            position: absolute;
-            display: inline-block;
-            cursor: move;
-        }
+  .documentDecorations-title {
+    opacity: 1;
+    grid-column-start: 2;
+    grid-column-end: 4;
+    pointer-events: auto;
+    overflow: hidden;
+    text-align: center;
+    display: flex;
+    margin-left: 5px;
+    height: 22px;
+    position: absolute;
+    .documentDecorations-titleSpan {
+      width: 100%;
+      border-radius: 8px;
+      background: #ffffffcf;
+      position: absolute;
+      display: inline-block;
+      cursor: move;
     }
+  }
 
-    .focus-visible {
-        margin-left: 0px;
-    }
+  .focus-visible {
+    margin-left: 0px;
+  }
 }
 
-
 .documentDecorations-iconifyButton {
-    opacity: 1;
-    grid-column-start: 4;
-    grid-column-end: 4;
-    pointer-events: all;
-    right: 0;
-    cursor: pointer;
-    position: absolute;
-    width: 20px;
+  opacity: 1;
+  grid-column-start: 4;
+  grid-column-end: 4;
+  pointer-events: all;
+  right: 0;
+  cursor: pointer;
+  position: absolute;
+  width: 20px;
 }
 
 .documentDecorations-openButton {
-    display: flex;
-    align-items: center;
-    opacity: 1;
-    grid-column-start: 5;
-    grid-column-end: 5;
-    pointer-events: all;
-    cursor: pointer;
+  display: flex;
+  align-items: center;
+  opacity: 1;
+  grid-column-start: 5;
+  grid-column-end: 5;
+  pointer-events: all;
+  cursor: pointer;
 }
 
 .documentDecorations-closeButton {
-    display: flex;
-    align-items: center;
-    opacity: 1;
-    grid-column-start: 1;
-    grid-column-end: 3;
-    pointer-events: all;
-    cursor: pointer;
-
-    >svg {
-        margin: 0;
-    }
+  display: flex;
+  align-items: center;
+  opacity: 1;
+  grid-column-start: 1;
+  grid-column-end: 3;
+  pointer-events: all;
+  cursor: pointer;
+
+  > svg {
+    margin: 0;
+  }
 }
 
 .documentDecorations-background {
-    background: lightblue;
-    position: absolute;
-    opacity: 0.1;
+  background: lightblue;
+  position: absolute;
+  opacity: 0.1;
 }
 
 .linkFlyout {
-    grid-column: 2/4;
+  grid-column: 2/4;
 }
 
 .linkButton-empty:hover {
-    background: $medium-gray;
-    transform: scale(1.05);
-    cursor: pointer;
+  background: $medium-gray;
+  transform: scale(1.05);
+  cursor: pointer;
 }
 
 .linkButton-nonempty:hover {
-    background: $medium-gray;
-    transform: scale(1.05);
-    cursor: pointer;
+  background: $medium-gray;
+  transform: scale(1.05);
+  cursor: pointer;
 }
 
 .link-button-container {
-    border-radius: 10px;
-    width: max-content;
-    height: auto;
-    display: flex;
-    flex-direction: row;
-    z-index: 998;
-    position: absolute;
-    justify-content: center;
-    align-items: center;
-    gap: 5px;
-    background: $medium-gray;
+  border-radius: 10px;
+  width: max-content;
+  height: auto;
+  display: flex;
+  flex-direction: row;
+  z-index: 998;
+  position: absolute;
+  justify-content: center;
+  align-items: center;
+  gap: 5px;
+  background: $medium-gray;
 }
 
 .linkButtonWrapper {
-    pointer-events: auto;
-    padding-right: 5px;
-    width: 25px;
+  pointer-events: auto;
+  padding-right: 5px;
+  width: 25px;
 }
 
 .linkButton-linker {
-    height: 20px;
-    width: 20px;
-    text-align: center;
-    border-radius: 50%;
-    pointer-events: auto;
-    color: $dark-gray;
-    border: $dark-gray 1px solid;
+  height: 20px;
+  width: 20px;
+  text-align: center;
+  border-radius: 50%;
+  pointer-events: auto;
+  color: $dark-gray;
+  border: $dark-gray 1px solid;
 }
 
 .linkButton-linker:hover {
-    cursor: pointer;
-    transform: scale(1.05);
+  cursor: pointer;
+  transform: scale(1.05);
 }
 
 .linkButton-empty,
 .linkButton-nonempty {
-    height: 20px;
-    width: 20px;
-    border-radius: 50%;
-    opacity: 0.9;
-    pointer-events: auto;
-    background-color: $dark-gray;
-    color: $white;
-    text-transform: uppercase;
-    letter-spacing: 2px;
-    font-size: 75%;
-    transition: transform 0.2s;
-    text-align: center;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-
-    &:hover {
-        background: $medium-gray;
-        transform: scale(1.05);
-        cursor: pointer;
-    }
+  height: 20px;
+  width: 20px;
+  border-radius: 50%;
+  opacity: 0.9;
+  pointer-events: auto;
+  background-color: $dark-gray;
+  color: $white;
+  text-transform: uppercase;
+  letter-spacing: 2px;
+  font-size: 75%;
+  transition: transform 0.2s;
+  text-align: center;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+
+  &:hover {
+    background: $medium-gray;
+    transform: scale(1.05);
+    cursor: pointer;
+  }
 }
 
 .templating-menu {
-    position: absolute;
-    pointer-events: auto;
-    text-transform: uppercase;
-    letter-spacing: 2px;
-    font-size: 75%;
-    transition: transform 0.2s;
-    text-align: center;
-    display: flex;
-    justify-content: center;
-    align-items: center;
+  position: absolute;
+  pointer-events: auto;
+  text-transform: uppercase;
+  letter-spacing: 2px;
+  font-size: 75%;
+  transition: transform 0.2s;
+  text-align: center;
+  display: flex;
+  justify-content: center;
+  align-items: center;
 }
 
 .documentdecorations-icon {
-    margin: 0px;
+  margin: 0px;
 }
 .templating-button,
 .docDecs-tagButton {
-    width: 20px;
-    height: 20px;
-    border-radius: 50%;
-    opacity: 0.9;
-    font-size: 14;
-    background-color: $dark-gray;
-    color: $white;
-    text-align: center;
-    cursor: pointer;
-
-    &:hover {
-        background: $medium-gray;
-        transform: scale(1.05);
-    }
+  width: 20px;
+  height: 20px;
+  border-radius: 50%;
+  opacity: 0.9;
+  font-size: 14;
+  background-color: $dark-gray;
+  color: $white;
+  text-align: center;
+  cursor: pointer;
+
+  &:hover {
+    background: $medium-gray;
+    transform: scale(1.05);
+  }
 }
 
 .documentDecorations-darkScheme {
-    background: dimgray;
+  background: dimgray;
 }
 
 #template-list {
-    position: absolute;
-    top: 25px;
-    left: 0px;
-    width: max-content;
-    font-family: $sans-serif;
-    font-size: 12px;
-    background-color: $light-gray;
-    padding: 2px 12px;
-    list-style: none;
-
-    .templateToggle,
-    .chromeToggle {
-        text-align: left;
-    }
+  position: absolute;
+  top: 25px;
+  left: 0px;
+  width: max-content;
+  font-family: $sans-serif;
+  font-size: 12px;
+  background-color: $light-gray;
+  padding: 2px 12px;
+  list-style: none;
+
+  .templateToggle,
+  .chromeToggle {
+    text-align: left;
+  }
 
-    input {
-        margin-right: 10px;
-    }
+  input {
+    margin-right: 10px;
+  }
 }
 
 @-moz-keyframes spin {
-    100% {
-        -moz-transform: rotate(360deg);
-    }
+  100% {
+    -moz-transform: rotate(360deg);
+  }
 }
 
 @-webkit-keyframes spin {
-    100% {
-        -webkit-transform: rotate(360deg);
-    }
+  100% {
+    -webkit-transform: rotate(360deg);
+  }
 }
 
 @keyframes spin {
-    100% {
-        -webkit-transform: rotate(360deg);
-        transform: rotate(360deg);
-    }
+  100% {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
 }
 
 @keyframes shadow-pulse {
-    0% {
-        box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
-    }
+  0% {
+    box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8);
+  }
 
-    100% {
-        box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
-    }
-}
\ No newline at end of file
+  100% {
+    box-shadow: 0 0 0 10px rgba(0, 255, 0, 0);
+  }
+}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3ffbb2904..2348c897b 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -457,24 +457,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
 
         const useRotation = seldoc.rootDoc.type === DocumentType.INK;
-        const screenData = seldoc.ComponentView?.screenStrokeData?.();
-        let selectedLine = (null);
-        if (screenData) {
-            const inkDoc = seldoc.props.Document;
-            const overlayWidth = 10;
-            selectedLine = <div className="documentDecorations-inkstroke" style={{
-                width: (bounds.r - bounds.x) + "px",
-                height: (bounds.b - bounds.y) + "px",
-                left: bounds.x,
-                top: bounds.y,
-            }} >
-                {InteractionUtils.CreatePolyline(screenData.screenPts, screenData.screenLeft, screenData.screenTop, Colors.MEDIUM_BLUE, overlayWidth, overlayWidth,
-                    StrCast(inkDoc.strokeBezier), StrCast(inkDoc.fillColor, "none"),
-                    StrCast(inkDoc.strokeStartMarker), StrCast(inkDoc.strokeEndMarker),
-                    StrCast(inkDoc.strokeDash), 1, 1, "", "none", 1.0, false)}
-
-            </div>
-        }
 
         return (<div className="documentDecorations" style={{ background: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : "" }} >
             <div className="documentDecorations-background" style={{
@@ -516,7 +498,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                         </>
                     }
                 </div >
-                {selectedLine}
                 {seldoc?.Document.type === DocumentType.FONTICON ? (null) : <div className="link-button-container" key="links" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
                     <DocumentButtonBar views={SelectionManager.Views} />
                 </div>}
diff --git a/src/client/views/InkStroke.scss b/src/client/views/InkStroke.scss
index 812a79bd5..8edefa07b 100644
--- a/src/client/views/InkStroke.scss
+++ b/src/client/views/InkStroke.scss
@@ -1,3 +1,14 @@
+.inkstroke-UI {
+  //   transform-origin: top left;
+  position: absolute;
+  overflow: visible;
+  pointer-events: none;
+
+  svg:not(:root) {
+    overflow: visible !important;
+  }
+}
+
 .inkStroke {
   mix-blend-mode: multiply;
   stroke-linejoin: round;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 297b3333c..41fbe0d55 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -114,7 +114,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         };
     }
 
-    screenStrokeData = () => {
+    componentUI = () => {
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth } = this.inkScaledData();
 
         const screenInkWidth = this.props.ScreenToLocalTransform().inverse().transformDirection(inkStrokeWidth, inkStrokeWidth);
@@ -122,7 +122,21 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const screenTop = Math.min(...screenPts.map(p => p.Y)) - screenInkWidth[0] / 2;
         const screenLeft = Math.min(...screenPts.map(p => p.X)) - screenInkWidth[0] / 2;
 
-        return { screenPts, screenLeft, screenTop };
+        const inkDoc = this.props.Document;
+        
+        const overlayWidth = 5;
+        return <div className="inkstroke-UI" style={{
+            // width: (bounds.r - bounds.x) + "px",
+            // height: (bounds.b - bounds.y) + "px",
+            left: screenLeft,
+            top: screenTop,
+        }} >
+            {InteractionUtils.CreatePolyline(screenPts, screenLeft, screenTop, Colors.MEDIUM_BLUE, overlayWidth, overlayWidth,
+                StrCast(inkDoc.strokeBezier), StrCast(inkDoc.fillColor, "none"),
+                StrCast(inkDoc.strokeStartMarker), StrCast(inkDoc.strokeEndMarker),
+                StrCast(inkDoc.strokeDash), 1, 1, "", "none", 1.0, false)}
+
+        </div>
     }
 
     render() {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1ce363b0a..b81459075 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -39,6 +39,7 @@ import { CollectionViewType } from './collections/CollectionView';
 import { ContextMenu } from './ContextMenu';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
+import { ComponentDecorations } from './ComponentDecorations';
 import { GestureOverlay } from './GestureOverlay';
 import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './global/globalCssVariables.scss';
 import { KeyManager } from './GlobalKeyHandler';
@@ -594,6 +595,7 @@ export class MainView extends React.Component {
             <GroupManager />
             <GoogleAuthenticationManager />
             <DocumentDecorations PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
+            <ComponentDecorations />
             {this.search}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3587c1d2a..4e6ed40d1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -88,7 +88,7 @@ export interface DocComponentView {
     setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     playFrom?: (time: number, endTime?: number) => void;
     setFocus?: () => void;
-    screenStrokeData?: () => { screenPts: { X: number, Y: number }[], screenTop: number, screenLeft: number };
+    componentUI?: () => JSX.Element;
 }
 export interface DocumentViewSharedProps {
     renderDepth: number;
-- 
cgit v1.2.3-70-g09d2


From 9a4bd3ca5dbe192eec20bf405a9904ef8b546de6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 4 Sep 2021 00:46:41 -0400
Subject: added top/left clip bounds to ink decorations

---
 src/client/views/ComponentDecorations.tsx | 4 ++--
 src/client/views/InkingStroke.tsx         | 3 ++-
 src/client/views/MainView.tsx             | 4 ++--
 src/client/views/nodes/DocumentView.tsx   | 2 +-
 4 files changed, 7 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/ComponentDecorations.tsx b/src/client/views/ComponentDecorations.tsx
index 66f81fe4f..66d1bd63d 100644
--- a/src/client/views/ComponentDecorations.tsx
+++ b/src/client/views/ComponentDecorations.tsx
@@ -4,11 +4,11 @@ import './ComponentDecorations.scss';
 import React = require("react");
 
 @observer
-export class ComponentDecorations extends React.Component {
+export class ComponentDecorations extends React.Component<{ boundsTop: number, boundsLeft: number }, { value: string }> {
     static Instance: ComponentDecorations;
 
     render() {
         const seldoc = SelectionManager.Views().lastElement();
-        return seldoc?.ComponentView?.componentUI?.() ?? (null);
+        return seldoc?.ComponentView?.componentUI?.(this.props.boundsLeft, this.props.boundsTop) ?? (null);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 90eb52dd2..e713cedee 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -114,7 +114,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         };
     }
 
-    componentUI = () => {
+    componentUI = (boundsLeft: number, boundsTop: number) => {
         const { inkData, inkScaleX, inkScaleY, inkStrokeWidth } = this.inkScaledData();
 
         const screenInkWidth = this.props.ScreenToLocalTransform().inverse().transformDirection(inkStrokeWidth, inkStrokeWidth);
@@ -129,6 +129,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         return <div className="inkstroke-UI" style={{
             left: screenOrigin[0],
             top: screenOrigin[1],
+            clip: `rect(${boundsTop - screenOrigin[1]}px, 10000px, 10000px, ${boundsLeft - screenOrigin[0]}px)`
         }} >
             {InteractionUtils.CreatePolyline(screenPts, screenLeft, screenTop, Colors.MEDIUM_BLUE, screenInkWidth[0], overlayWidth,
                 StrCast(inkDoc.strokeBezier), StrCast(inkDoc.fillColor, "none"),
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b81459075..f03124f0d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -83,7 +83,7 @@ export class MainView extends React.Component {
     @observable private _sidebarContent: any = this.userDoc?.sidebar;
     @observable private _flyoutWidth: number = 0;
 
-    @computed private get topOffset() { return Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } //TODO remove
+    @computed private get topOffset() { return 35 + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } //TODO remove
     @computed private get leftOffset() { return this.menuPanelWidth() - 2; }
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
@@ -595,7 +595,7 @@ export class MainView extends React.Component {
             <GroupManager />
             <GoogleAuthenticationManager />
             <DocumentDecorations PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
-            <ComponentDecorations />
+            <ComponentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset + 25} />
             {this.search}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4e6ed40d1..a9b303294 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -88,7 +88,7 @@ export interface DocComponentView {
     setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
     playFrom?: (time: number, endTime?: number) => void;
     setFocus?: () => void;
-    componentUI?: () => JSX.Element;
+    componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element;
 }
 export interface DocumentViewSharedProps {
     renderDepth: number;
-- 
cgit v1.2.3-70-g09d2


From 40f3b73fef74c204c927e7715c73d021b98d7428 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 5 Sep 2021 15:09:42 -0400
Subject: changed direcotry from CollectionLinearView to collectionLinear

---
 src/client/views/MainView.tsx                      |   2 +-
 src/client/views/collections/CollectionMenu.tsx    |   2 +-
 src/client/views/collections/CollectionView.tsx    |   2 +-
 .../collectionLinear/CollectionLinearView.scss     | 150 ++++++++++++++
 .../collectionLinear/CollectionLinearView.tsx      | 222 +++++++++++++++++++++
 .../views/collections/collectionLinear/index.ts    |   1 +
 .../collectionLinearView/CollectionLinearView.scss | 150 --------------
 .../collectionLinearView/CollectionLinearView.tsx  | 222 ---------------------
 .../collections/collectionLinearView/index.ts      |   1 -
 9 files changed, 376 insertions(+), 376 deletions(-)
 create mode 100644 src/client/views/collections/collectionLinear/CollectionLinearView.scss
 create mode 100644 src/client/views/collections/collectionLinear/CollectionLinearView.tsx
 create mode 100644 src/client/views/collections/collectionLinear/index.ts
 delete mode 100644 src/client/views/collections/collectionLinearView/CollectionLinearView.scss
 delete mode 100644 src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
 delete mode 100644 src/client/views/collections/collectionLinearView/index.ts

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dadf5b390..5c1a51052 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -32,7 +32,7 @@ import { Transform } from '../util/Transform';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
-import { CollectionLinearView } from './collections/CollectionLinearView';
+import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
 import { CollectionViewType } from './collections/CollectionView';
 import "./collections/TreeView.scss";
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 3d37a9df8..86bf9ad41 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -36,7 +36,7 @@ import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
 import { PresBox } from "../nodes/trails/PresBox";
 import { DefaultStyleProvider } from "../StyleProvider";
 import { CollectionDockingView } from "./CollectionDockingView";
-import { CollectionLinearView } from "./CollectionLinearView";
+import { CollectionLinearView } from "./collectionLinear";
 import "./CollectionMenu.scss";
 import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { TabDocView } from "./TabDocView";
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 229e93b9e..81375e557 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -24,7 +24,7 @@ import { CollectionCarouselView } from './CollectionCarouselView';
 import { CollectionDockingView } from "./CollectionDockingView";
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionGridView } from './collectionGrid/CollectionGridView';
-import { CollectionLinearView } from './CollectionLinearView';
+import { CollectionLinearView } from './collectionLinear';
 import CollectionMapView from './CollectionMapView';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.scss b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
new file mode 100644
index 000000000..8fe804466
--- /dev/null
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
@@ -0,0 +1,150 @@
+@import "../../global/globalCssVariables";
+@import "../../_nodeModuleOverrides";
+
+.collectionLinearView-outer {
+    overflow: visible;
+    height: 100%;
+    pointer-events: none;
+
+    &.true {
+        padding-left: 5px;
+        padding-right: 5px;
+        border-left: $standard-border;
+        background-color: $medium-blue-alt;
+    }
+
+    >input:not(:checked)~&.true {
+        background-color: transparent;
+    }
+
+    .collectionLinearView {
+        display: flex;
+        height: 100%;
+        align-items: center;
+        gap: 5px;
+
+        .collectionView {
+            overflow: visible !important;
+        }
+
+        >span {
+            background: $dark-gray;
+            color: $white;
+            border-radius: 18px;
+            margin-right: 6px;
+            cursor: pointer;
+        }
+
+        .bottomPopup-background {
+            background: $medium-blue;
+            display: flex;
+            border-radius: 10px;
+            height: 35;
+            transform: translate3d(6px, 0px, 0px);
+            align-content: center;
+            justify-content: center;
+            align-items: center;
+        }
+
+        .bottomPopup-text {
+            color: $white;
+            display: inline;
+            white-space: nowrap;
+            padding-left: 8px;
+            padding-right: 20px;
+            vertical-align: middle;
+            font-size: 12.5px;
+        }
+
+        .bottomPopup-descriptions {
+            cursor: pointer;
+            display: inline;
+            white-space: nowrap;
+            padding-left: 8px;
+            padding-right: 8px;
+            vertical-align: middle;
+            background-color: $light-gray;
+            border-radius: 3px;
+            color: black;
+            margin-right: 5px;
+        }
+
+        .bottomPopup-exit {
+            cursor: pointer;
+            display: inline;
+            white-space: nowrap;
+            margin-right: 10px;
+            padding-left: 8px;
+            padding-right: 8px;
+            vertical-align: middle;
+            background-color: $close-red;
+            border-radius: 3px;
+            color: black;
+        }
+
+        >label {
+            pointer-events: all;
+            cursor: pointer;
+            background-color: $medium-blue;
+            padding: 5;
+            border-radius: 2px;
+            height: 25;
+            min-width: 25;
+            margin: 0;
+            color: $white;
+            display: flex;
+            font-weight: 100;
+            width: fit-content;
+            transition: transform 0.2s;
+            align-items: center;
+            justify-content: center;
+            transition: 0.2s;
+
+            &:hover{
+                filter: brightness(0.85);
+            }
+        }
+
+        >input {
+            display: none;
+        }
+
+        >input:not(:checked)~.collectionLinearView-content {
+            display: none;
+        }
+
+        >input:checked~label {
+            transform: rotate(45deg);
+            transition: transform 0.5s;
+            cursor: pointer;
+        }
+
+        .collectionLinearView-content {
+            display: flex;
+            opacity: 1;
+            position: relative;
+
+            .collectionLinearView-docBtn,
+            .collectionLinearView-docBtn-scalable {
+                position: relative;
+                margin: auto;
+                transform-origin: center 80%;
+            }
+
+            .collectionLinearView-docBtn-scalable:hover {
+                transform: scale(1.15);
+            }
+
+            .collectionLinearView-round-button {
+                width: 18px;
+                height: 18px;
+                border-radius: 18px;
+                font-size: 15px;
+            }
+
+            .collectionLinearView-round-button:hover {
+                transform: scale(1.15);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
new file mode 100644
index 000000000..e78ca2a32
--- /dev/null
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -0,0 +1,222 @@
+import { Tooltip } from '@material-ui/core';
+import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
+import { documentSchema } from '../../../../fields/documentSchemas';
+import { Id } from '../../../../fields/FieldSymbols';
+import { makeInterface } from '../../../../fields/Schema';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../../Utils';
+import { DragManager } from '../../../util/DragManager';
+import { Transform } from '../../../util/Transform';
+import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
+import { DocumentView } from '../../nodes/DocumentView';
+import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
+import { StyleProp } from '../../StyleProvider';
+import "./CollectionLinearView.scss";
+import { CollectionSubView } from '../CollectionSubView';
+import { CollectionViewType } from '../CollectionView';
+import { Colors, Shadows } from '../../global/globalEnums';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+
+
+type LinearDocument = makeInterface<[typeof documentSchema,]>;
+const LinearDocument = makeInterface(documentSchema);
+
+@observer
+export class CollectionLinearView extends CollectionSubView(LinearDocument) {
+    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
+    @observable private _selectedIndex = -1;
+    private _dropDisposer?: DragManager.DragDropDisposer;
+    private _widthDisposer?: IReactionDisposer;
+    private _selectedDisposer?: IReactionDisposer;
+
+    componentWillUnmount() {
+        this._dropDisposer?.();
+        this._widthDisposer?.();
+        this._selectedDisposer?.();
+        this.childLayoutPairs.map((pair, ind) => ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
+    }
+
+    componentDidMount() {
+        this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10),
+            width => this.childDocs.length && (this.layoutDoc._width = width),
+            { fireImmediately: true }
+        );
+
+        this._selectedDisposer = reaction(
+            () => NumCast(this.layoutDoc.selectedIndex),
+            (i) => runInAction(() => {
+                this._selectedIndex = i;
+                let selected: any = undefined;
+                this.childLayoutPairs.map(async (pair, ind) => {
+                    const isSelected = this._selectedIndex === ind;
+                    if (isSelected) {
+                        selected = pair;
+                    }
+                    else {
+                        ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+                    }
+                });
+                if (selected && selected.layout) {
+                    ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
+                }
+            }),
+            { fireImmediately: true }
+        );
+    }
+    protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+        this._dropDisposer && this._dropDisposer();
+        if (ele) {
+            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
+        }
+    }
+
+    dimension = () => NumCast(this.rootDoc._height); // 2 * the padding
+    getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
+        if (!ele.current) return Transform.Identity();
+        const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
+        return new Transform(-translateX, -translateY, 1);
+    }
+
+    @action
+    exitLongLinks = () => {
+        if (DocumentLinksButton.StartLink) {
+            if (DocumentLinksButton.StartLink.Document) {
+                action((e: React.PointerEvent<HTMLDivElement>) => {
+                    Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
+                });
+            }
+        }
+        DocumentLinksButton.StartLink = undefined;
+        DocumentLinksButton.StartLinkView = undefined;
+    }
+
+    @action
+    changeDescriptionSetting = () => {
+        if (LinkDescriptionPopup.showDescriptions) {
+            if (LinkDescriptionPopup.showDescriptions === "ON") {
+                LinkDescriptionPopup.showDescriptions = "OFF";
+                LinkDescriptionPopup.descriptionPopup = false;
+            } else {
+                LinkDescriptionPopup.showDescriptions = "ON";
+            }
+        } else {
+            LinkDescriptionPopup.showDescriptions = "OFF";
+            LinkDescriptionPopup.descriptionPopup = false;
+        }
+    }
+
+    myContextMenu = (e: React.MouseEvent) => {
+        console.log("STOPPING");
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    render() {
+        const guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label
+        const flexDir: any = StrCast(this.Document.flexDirection); // Specify direction of linear view content
+        const flexGap: number = NumCast(this.Document.flexGap); // Specify the gap between linear view content
+        const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable); // Specify whether it is expandable or not
+        const floating: boolean = BoolCast(this.props.Document.linearViewFloating); // Specify whether it is expandable or not
+
+        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+        const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle
+        const menuOpener = <label htmlFor={`${guid}`}
+            style={{
+                boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
+                color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
+                backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY
+            }}
+            onPointerDown={e => e.stopPropagation()} >
+            <div className="collectionLinearView-menuOpener">
+                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"} /> : icon ? icon : <FontAwesomeIcon icon={"plus"} />}
+            </div>
+        </label>;
+
+        return <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : "transparent" }}>
+            <div className="collectionLinearView" ref={this.createDashEventsTarget}
+                onContextMenu={this.myContextMenu} >
+                {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close" : "Open"}</div></>} placement="top">
+                    {menuOpener}
+                </Tooltip>}
+                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
+                    onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
+
+                <div className="collectionLinearView-content"
+                    style={{
+                        height: this.dimension(),
+                        flexDirection: flexDir,
+                        gap: flexGap
+                    }}>
+                    {this.childLayoutPairs.map((pair, ind) => {
+                        const nested = pair.layout._viewType === CollectionViewType.Linear;
+                        const dref = React.createRef<HTMLDivElement>();
+                        const hidden = pair.layout.hidden === true;
+                        // const scalable = pair.layout.onClick || pair.layout.onDragStart;
+                        return hidden ? (null) : <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
+                            style={{
+                                pointerEvents: "all",
+                                width: nested ? undefined : NumCast(pair.layout._width),
+                                height: nested ? undefined : NumCast(pair.layout._height),
+                                marginLeft: !nested ? 2.5 : 0,
+                                marginRight: !nested ? 2.5 : 0,
+                                // width: NumCast(pair.layout._width),
+                                // height: NumCast(pair.layout._height),
+                            }}  >
+                            <DocumentView
+                                Document={pair.layout}
+                                DataDoc={pair.data}
+                                isContentActive={this.props.isContentActive}
+                                isDocumentActive={returnTrue}
+                                addDocument={this.props.addDocument}
+                                moveDocument={this.props.moveDocument}
+                                addDocTab={this.props.addDocTab}
+                                pinToPres={emptyFunction}
+                                rootSelected={this.props.isSelected}
+                                removeDocument={this.props.removeDocument}
+                                ScreenToLocalTransform={this.getTransform(dref)}
+                                PanelWidth={nested ? pair.layout[WidthSym] : this.dimension}
+                                PanelHeight={nested ? pair.layout[HeightSym] : this.dimension}
+                                renderDepth={this.props.renderDepth + 1}
+                                focus={emptyFunction}
+                                styleProvider={this.props.styleProvider}
+                                layerProvider={this.props.layerProvider}
+                                docViewPath={returnEmptyDoclist}
+                                whenChildContentsActiveChanged={emptyFunction}
+                                bringToFront={emptyFunction}
+                                docFilters={this.props.docFilters}
+                                docRangeFilters={this.props.docRangeFilters}
+                                searchFilterDocs={this.props.searchFilterDocs}
+                                ContainingCollectionView={undefined}
+                                ContainingCollectionDoc={undefined} />
+                        </div>;
+                    })}
+                </div>
+                {DocumentLinksButton.StartLink && StrCast(this.layoutDoc.title) === "docked buttons" ? <span className="bottomPopup-background" style={{
+                    pointerEvents: "all"
+                }}
+                    onPointerDown={e => e.stopPropagation()} >
+                    <span className="bottomPopup-text" >
+                        Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
+                    </span>
+
+                    <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
+                        <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
+                            Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
+                        </span>
+                    </Tooltip>
+
+                    <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
+                        <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
+                            Stop
+                        </span>
+                    </Tooltip>
+
+                </span> : null}
+            </div>
+        </div>;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionLinear/index.ts b/src/client/views/collections/collectionLinear/index.ts
new file mode 100644
index 000000000..ff73e14ae
--- /dev/null
+++ b/src/client/views/collections/collectionLinear/index.ts
@@ -0,0 +1 @@
+export * from "./CollectionLinearView";
\ No newline at end of file
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
deleted file mode 100644
index 8fe804466..000000000
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss
+++ /dev/null
@@ -1,150 +0,0 @@
-@import "../../global/globalCssVariables";
-@import "../../_nodeModuleOverrides";
-
-.collectionLinearView-outer {
-    overflow: visible;
-    height: 100%;
-    pointer-events: none;
-
-    &.true {
-        padding-left: 5px;
-        padding-right: 5px;
-        border-left: $standard-border;
-        background-color: $medium-blue-alt;
-    }
-
-    >input:not(:checked)~&.true {
-        background-color: transparent;
-    }
-
-    .collectionLinearView {
-        display: flex;
-        height: 100%;
-        align-items: center;
-        gap: 5px;
-
-        .collectionView {
-            overflow: visible !important;
-        }
-
-        >span {
-            background: $dark-gray;
-            color: $white;
-            border-radius: 18px;
-            margin-right: 6px;
-            cursor: pointer;
-        }
-
-        .bottomPopup-background {
-            background: $medium-blue;
-            display: flex;
-            border-radius: 10px;
-            height: 35;
-            transform: translate3d(6px, 0px, 0px);
-            align-content: center;
-            justify-content: center;
-            align-items: center;
-        }
-
-        .bottomPopup-text {
-            color: $white;
-            display: inline;
-            white-space: nowrap;
-            padding-left: 8px;
-            padding-right: 20px;
-            vertical-align: middle;
-            font-size: 12.5px;
-        }
-
-        .bottomPopup-descriptions {
-            cursor: pointer;
-            display: inline;
-            white-space: nowrap;
-            padding-left: 8px;
-            padding-right: 8px;
-            vertical-align: middle;
-            background-color: $light-gray;
-            border-radius: 3px;
-            color: black;
-            margin-right: 5px;
-        }
-
-        .bottomPopup-exit {
-            cursor: pointer;
-            display: inline;
-            white-space: nowrap;
-            margin-right: 10px;
-            padding-left: 8px;
-            padding-right: 8px;
-            vertical-align: middle;
-            background-color: $close-red;
-            border-radius: 3px;
-            color: black;
-        }
-
-        >label {
-            pointer-events: all;
-            cursor: pointer;
-            background-color: $medium-blue;
-            padding: 5;
-            border-radius: 2px;
-            height: 25;
-            min-width: 25;
-            margin: 0;
-            color: $white;
-            display: flex;
-            font-weight: 100;
-            width: fit-content;
-            transition: transform 0.2s;
-            align-items: center;
-            justify-content: center;
-            transition: 0.2s;
-
-            &:hover{
-                filter: brightness(0.85);
-            }
-        }
-
-        >input {
-            display: none;
-        }
-
-        >input:not(:checked)~.collectionLinearView-content {
-            display: none;
-        }
-
-        >input:checked~label {
-            transform: rotate(45deg);
-            transition: transform 0.5s;
-            cursor: pointer;
-        }
-
-        .collectionLinearView-content {
-            display: flex;
-            opacity: 1;
-            position: relative;
-
-            .collectionLinearView-docBtn,
-            .collectionLinearView-docBtn-scalable {
-                position: relative;
-                margin: auto;
-                transform-origin: center 80%;
-            }
-
-            .collectionLinearView-docBtn-scalable:hover {
-                transform: scale(1.15);
-            }
-
-            .collectionLinearView-round-button {
-                width: 18px;
-                height: 18px;
-                border-radius: 18px;
-                font-size: 15px;
-            }
-
-            .collectionLinearView-round-button:hover {
-                transform: scale(1.15);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
deleted file mode 100644
index 8d7809cdb..000000000
--- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx
+++ /dev/null
@@ -1,222 +0,0 @@
-import { Tooltip } from '@material-ui/core';
-import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
-import { Id } from '../../../../fields/FieldSymbols';
-import { makeInterface } from '../../../../fields/Schema';
-import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../../Utils';
-import { DragManager } from '../../../util/DragManager';
-import { Transform } from '../../../util/Transform';
-import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
-import { DocumentView } from '../../nodes/DocumentView';
-import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
-import { StyleProp } from '../../StyleProvider';
-import "./CollectionLinearView.scss";
-import { CollectionSubView } from '.././CollectionSubView';
-import { CollectionViewType } from '.././CollectionView';
-import { Colors, Shadows } from '../../global/globalEnums';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-
-
-type LinearDocument = makeInterface<[typeof documentSchema,]>;
-const LinearDocument = makeInterface(documentSchema);
-
-@observer
-export class CollectionLinearView extends CollectionSubView(LinearDocument) {
-    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
-    @observable private _selectedIndex = -1;
-    private _dropDisposer?: DragManager.DragDropDisposer;
-    private _widthDisposer?: IReactionDisposer;
-    private _selectedDisposer?: IReactionDisposer;
-
-    componentWillUnmount() {
-        this._dropDisposer?.();
-        this._widthDisposer?.();
-        this._selectedDisposer?.();
-        this.childLayoutPairs.map((pair, ind) => ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
-    }
-
-    componentDidMount() {
-        this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10),
-            width => this.childDocs.length && (this.layoutDoc._width = width),
-            { fireImmediately: true }
-        );
-
-        this._selectedDisposer = reaction(
-            () => NumCast(this.layoutDoc.selectedIndex),
-            (i) => runInAction(() => {
-                this._selectedIndex = i;
-                let selected: any = undefined;
-                this.childLayoutPairs.map(async (pair, ind) => {
-                    const isSelected = this._selectedIndex === ind;
-                    if (isSelected) {
-                        selected = pair;
-                    }
-                    else {
-                        ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
-                    }
-                });
-                if (selected && selected.layout) {
-                    ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
-                }
-            }),
-            { fireImmediately: true }
-        );
-    }
-    protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
-        this._dropDisposer && this._dropDisposer();
-        if (ele) {
-            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
-        }
-    }
-
-    dimension = () => NumCast(this.rootDoc._height); // 2 * the padding
-    getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
-        if (!ele.current) return Transform.Identity();
-        const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
-        return new Transform(-translateX, -translateY, 1);
-    }
-
-    @action
-    exitLongLinks = () => {
-        if (DocumentLinksButton.StartLink) {
-            if (DocumentLinksButton.StartLink.Document) {
-                action((e: React.PointerEvent<HTMLDivElement>) => {
-                    Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
-                });
-            }
-        }
-        DocumentLinksButton.StartLink = undefined;
-        DocumentLinksButton.StartLinkView = undefined;
-    }
-
-    @action
-    changeDescriptionSetting = () => {
-        if (LinkDescriptionPopup.showDescriptions) {
-            if (LinkDescriptionPopup.showDescriptions === "ON") {
-                LinkDescriptionPopup.showDescriptions = "OFF";
-                LinkDescriptionPopup.descriptionPopup = false;
-            } else {
-                LinkDescriptionPopup.showDescriptions = "ON";
-            }
-        } else {
-            LinkDescriptionPopup.showDescriptions = "OFF";
-            LinkDescriptionPopup.descriptionPopup = false;
-        }
-    }
-
-    myContextMenu = (e: React.MouseEvent) => {
-        console.log("STOPPING");
-        e.stopPropagation();
-        e.preventDefault();
-    }
-
-    render() {
-        const guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label
-        const flexDir: any = StrCast(this.Document.flexDirection); // Specify direction of linear view content
-        const flexGap: number = NumCast(this.Document.flexGap); // Specify the gap between linear view content
-        const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable); // Specify whether it is expandable or not
-        const floating: boolean = BoolCast(this.props.Document.linearViewFloating); // Specify whether it is expandable or not
-
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle
-        const menuOpener = <label htmlFor={`${guid}`}
-            style={{
-                boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
-                color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
-                backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY
-            }}
-            onPointerDown={e => e.stopPropagation()} >
-            <div className="collectionLinearView-menuOpener">
-                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"} /> : icon ? icon : <FontAwesomeIcon icon={"plus"} />}
-            </div>
-        </label>;
-
-        return <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : "transparent" }}>
-            <div className="collectionLinearView" ref={this.createDashEventsTarget}
-                onContextMenu={this.myContextMenu} >
-                {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close" : "Open"}</div></>} placement="top">
-                    {menuOpener}
-                </Tooltip>}
-                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
-                    onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
-
-                <div className="collectionLinearView-content"
-                    style={{
-                        height: this.dimension(),
-                        flexDirection: flexDir,
-                        gap: flexGap
-                    }}>
-                    {this.childLayoutPairs.map((pair, ind) => {
-                        const nested = pair.layout._viewType === CollectionViewType.Linear;
-                        const dref = React.createRef<HTMLDivElement>();
-                        const hidden = pair.layout.hidden === true;
-                        // const scalable = pair.layout.onClick || pair.layout.onDragStart;
-                        return hidden ? (null) : <div className={`collectionLinearView-docBtn`} key={pair.layout[Id]} ref={dref}
-                            style={{
-                                pointerEvents: "all",
-                                width: nested ? undefined : NumCast(pair.layout._width),
-                                height: nested ? undefined : NumCast(pair.layout._height),
-                                marginLeft: !nested ? 2.5 : 0,
-                                marginRight: !nested ? 2.5 : 0,
-                                // width: NumCast(pair.layout._width),
-                                // height: NumCast(pair.layout._height),
-                            }}  >
-                            <DocumentView
-                                Document={pair.layout}
-                                DataDoc={pair.data}
-                                isContentActive={this.props.isContentActive}
-                                isDocumentActive={returnTrue}
-                                addDocument={this.props.addDocument}
-                                moveDocument={this.props.moveDocument}
-                                addDocTab={this.props.addDocTab}
-                                pinToPres={emptyFunction}
-                                rootSelected={this.props.isSelected}
-                                removeDocument={this.props.removeDocument}
-                                ScreenToLocalTransform={this.getTransform(dref)}
-                                PanelWidth={nested ? pair.layout[WidthSym] : this.dimension}
-                                PanelHeight={nested ? pair.layout[HeightSym] : this.dimension}
-                                renderDepth={this.props.renderDepth + 1}
-                                focus={emptyFunction}
-                                styleProvider={this.props.styleProvider}
-                                layerProvider={this.props.layerProvider}
-                                docViewPath={returnEmptyDoclist}
-                                whenChildContentsActiveChanged={emptyFunction}
-                                bringToFront={emptyFunction}
-                                docFilters={this.props.docFilters}
-                                docRangeFilters={this.props.docRangeFilters}
-                                searchFilterDocs={this.props.searchFilterDocs}
-                                ContainingCollectionView={undefined}
-                                ContainingCollectionDoc={undefined} />
-                        </div>;
-                    })}
-                </div>
-                {DocumentLinksButton.StartLink && StrCast(this.layoutDoc.title) === "docked buttons" ? <span className="bottomPopup-background" style={{
-                    pointerEvents: "all"
-                }}
-                    onPointerDown={e => e.stopPropagation()} >
-                    <span className="bottomPopup-text" >
-                        Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
-                    </span>
-
-                    <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
-                        <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
-                            Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
-                        </span>
-                    </Tooltip>
-
-                    <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
-                        <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
-                            Stop
-                        </span>
-                    </Tooltip>
-
-                </span> : null}
-            </div>
-        </div>;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/collectionLinearView/index.ts b/src/client/views/collections/collectionLinearView/index.ts
deleted file mode 100644
index ff73e14ae..000000000
--- a/src/client/views/collections/collectionLinearView/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./CollectionLinearView";
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From f20dfa857e1651ced1eb2303ea8a8462483fbc4c Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Sun, 12 Sep 2021 10:00:30 -0400
Subject: updates

---
 src/client/util/CurrentUserUtils.ts       | 51 +++++++++++++++++--------------
 src/client/views/MainView.tsx             | 20 ++++++++++++
 src/client/views/collections/TreeView.tsx |  8 ++---
 src/client/views/nodes/DocumentView.tsx   |  5 +--
 4 files changed, 55 insertions(+), 29 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 56807c63b..760949b57 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -575,11 +575,11 @@ export class CurrentUserUtils {
         return [
             { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
             { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
-            { title: "File Manager", target: Cast(doc.myFilesystem, Doc, null), icon: "folder-open", click: 'selectMainMenu(self)' },
+            { title: "Files", target: Cast(doc.myFilesystem, Doc, null), icon: "folder-open", click: 'selectMainMenu(self)' },
             { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
-            { title: "Uploads", target: Cast(doc.myUploadDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+            { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
-            { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
+            { title: "Shared  with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
             { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
         ];
@@ -807,13 +807,15 @@ export class CurrentUserUtils {
         // setup dashboards library item
         await doc.myDashboards;
         if (doc.myDashboards === undefined) {
+            const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
+            const newDashboardButton:Doc = Docs.Create.FontIconDocument({ onClick: newDashboard, _forceActive: true, toolTip: "Create new dashboard", _stayInCollection: true, _hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
             doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true
+                treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newDashboardButton,
+                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true,
+                explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
             }));
-            const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
             // const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
             // const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
             // const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
@@ -821,7 +823,7 @@ export class CurrentUserUtils {
             const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
             (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]);
             (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]);
-            (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "share", "times"]);
+            (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "user-friends", "times"]);
             // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
             // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
         }
@@ -832,7 +834,7 @@ export class CurrentUserUtils {
         await doc.myTrails;
         if (doc.myTrails === undefined) {
             const newTrail = ScriptField.MakeScript(`createNewPresentation()`);
-            const newTrailButton:Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "New trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
+            const newTrailButton:Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
             doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
                 title: "My Trails", _showTitle: "title", _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
@@ -842,6 +844,7 @@ export class CurrentUserUtils {
             }));
             (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newTrail!]);
             (doc.myTrails as any as Doc).contextMenuLabels = new List<string>(["Create New Trail"]);
+            (doc.myTrails as any as Doc).childContextMenuIcons = new List<string>(["plus"]);
         }
         return doc.myTrails as any as Doc;
     }
@@ -851,8 +854,8 @@ export class CurrentUserUtils {
         if (doc.myFilesystem === undefined) {
             doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
             // doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true });
-            const newFolder = ScriptField.MakeFunction(`doc.makeFolder()`, { doc: doc.myFilesystem })!;
-            const newFolderButton:Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
+            const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
+            const newFolderButton:Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "Create new folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
             doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], {
                 title: "My Documents", _showTitle: "title",  buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
@@ -861,8 +864,9 @@ export class CurrentUserUtils {
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
                 explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
             }));
-            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder!]);
-            (doc.myTrails as any as Doc).contextMenuLabels = new List<string>(["Create new folder"]);
+            (doc.myFilesystem as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder!]);
+            (doc.myFilesystem as any as Doc).contextMenuLabels = new List<string>(["Create new folder"]);
+            (doc.myFilesystem as any as Doc).childContextMenuIcons = new List<string>(["plus"]);
         }
         return doc.myFilesystem as any as Doc;
     }
@@ -927,7 +931,7 @@ export class CurrentUserUtils {
     static async setupSidebarButtons(doc: Doc) {
         CurrentUserUtils.setupSidebarContainer(doc);
         await CurrentUserUtils.setupToolsBtnPanel(doc);
-        CurrentUserUtils.setupUploadSidebar(doc);
+        CurrentUserUtils.setupImportSidebar(doc);
         CurrentUserUtils.setupDashboards(doc);
         CurrentUserUtils.setupPresentations(doc);
         CurrentUserUtils.setupFilesystem(doc);
@@ -1194,7 +1198,7 @@ export class CurrentUserUtils {
                     title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
                     _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
                     _chromeHidden: true, boxShadow: "0 0",
-                    explainer: "This is where documents or dashboards that other users have shared with you will appear."
+                    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'"
                 }, sharingDocumentId + "outer", sharingDocumentId);
                 (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment;
             }
@@ -1213,13 +1217,13 @@ export class CurrentUserUtils {
     }
 
     // Import sidebar is where shared documents are contained
-    static setupUploadSidebar(doc: Doc) {
-        if (doc.myUploadDocs === undefined) {
-            const newUploadButton:Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Upload from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Upload", btnType: ButtonType.ClickButton, buttonText: "Upload", icon: "upload", system: true });
-            doc.myUploadDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
-                title: "My Uploads", _forceActive: true, buttonMenu: true, buttonMenuDoc: newUploadButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
+    static setupImportSidebar(doc: Doc) {
+        if (doc.myImportDocs === undefined) {
+            const newImportButton:Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", system: true });
+            doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
+                title: "My Imports", _forceActive: true, buttonMenu: true, buttonMenuDoc: newImportButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
                 childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
-                explainer: "This is where documents that are uploaded into Dash will go."
+                explainer: "This is where documents that are Imported into Dash will go."
             }));
         }
     }
@@ -1322,7 +1326,7 @@ export class CurrentUserUtils {
         doc.filterDocCount = 0;
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
-        this.setupUploadSidebar(doc); // sets up the import sidebar
+        this.setupImportSidebar(doc); // sets up the import sidebar
         this.setupSearchSidebar(doc); // sets up the search sidebar
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // documents in overlay layer
@@ -1449,7 +1453,7 @@ export class CurrentUserUtils {
                     }
                 }
             } else if (input.files && input.files.length !== 0) {
-                const importDocs = Cast(Doc.UserDoc().myUploadDocs, Doc, null);
+                const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null);
                 const disposer = OverlayView.ShowSpinner();
                 DocListCastAsync(importDocs.data).then(async list => {
                     const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {});
@@ -1473,7 +1477,6 @@ export class CurrentUserUtils {
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
-        const myTrails = await userDoc.myTrails as Doc;
         const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
         const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
@@ -1558,6 +1561,8 @@ Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.crea
     "creates a new dashboard when called");
 Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
     "creates a new presentation when called");
+Scripting.addGlobal(function createNewFolder() { return MainView.Instance.createNewFolder(); },
+    "creates a new folder in myFiles when called");
 Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
     "returns all the links to the document or its annotations", "(doc: any)");
 Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); },
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dadf5b390..809c63a45 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -63,6 +63,7 @@ import { PropertiesView } from './PropertiesView';
 import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
 import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
+import { ScriptField } from '../../fields/ScriptField';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -239,6 +240,25 @@ export class MainView extends React.Component {
         Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
     }
 
+    @action
+    createNewFolder = async () => {
+        if (!await this.userDoc.myFilesystem) {
+            this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
+            const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
+            const newFolderButton:Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
+            this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], {
+                title: "My Documents", _showTitle: "title",  buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
+                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+                treeViewTruncateTitleWidth: 150, ignoreClick: true,
+                isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
+                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
+                explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
+            }));
+        }
+        const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
+        Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
+    }
+
     getPWidth = () => this._panelWidth - this.propertiesWidth();
     getPHeight = () => this._panelHeight - (CollectionMenu.Instance?.Pinned ? 35 : 0);
     getContentsHeight = () => this._panelHeight;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 8cf34ddcc..2727a0af5 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -240,7 +240,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     makeFolder = () => {
-        const folder = Docs.Create.TreeDocument([], { title: "-folder-", _stayInCollection: true, isFolder: true });
+        const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
         TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
         return this.props.addDocument(folder);
     }
@@ -512,9 +512,9 @@ export class TreeView extends React.Component<TreeViewProps> {
         DocumentViewInternal.SelectAfterContextMenu = true;
     }
     contextMenuItems = () => {
-        const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" };
-        const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" };
-        const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" };
+        const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, icon: "folder-plus", label: "New Folder" };
+        const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: "copy", label: "Open Alias" };
+        const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: "eye", label: "Focus or Open" };
         return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? [makeFolder] :
             Doc.IsSystem(this.doc) ? [] :
                 this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b2629200d..ebd0211e2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -49,6 +49,7 @@ import { RadialMenu } from './RadialMenu';
 import { ScriptingBox } from "./ScriptingBox";
 import { PresBox } from './trails/PresBox';
 import React = require("react");
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
 const { Howl } = require('howler');
 
 interface Window {
@@ -152,7 +153,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     NativeWidth?: () => number;
     NativeHeight?: () => number;
     LayoutTemplate?: () => Opt<Doc>;
-    contextMenuItems?: () => { script: ScriptField, label: string }[];
+    contextMenuItems?: () => { script: ScriptField, label: string, icon: string }[];
     onClick?: () => ScriptField;
     onDoubleClick?: () => ScriptField;
     onPointerDown?: () => ScriptField;
@@ -672,7 +673,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
             cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
         this.props.contextMenuItems?.().forEach(item =>
-            item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
+            item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp }));
 
         if (!this.props.Document.isFolder) {
             const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
-- 
cgit v1.2.3-70-g09d2


From e911344b7b93381c2dda4434480a6168291c60c0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 13 Sep 2021 14:28:37 -0400
Subject: fixed errors and warnings.

---
 src/client/util/CurrentUserUtils.ts                 |  2 +-
 src/client/views/MainView.tsx                       |  5 +++--
 src/client/views/collections/TreeView.tsx           |  2 +-
 src/client/views/nodes/DocumentView.tsx             |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx       | 21 +++++----------------
 .../nodes/button/colorDropdown/ColorDropdown.tsx    | 10 ++++------
 .../views/nodes/formattedText/FormattedTextBox.tsx  |  2 +-
 7 files changed, 16 insertions(+), 28 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 9a5ccffa0..6a7523d5b 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -863,7 +863,7 @@ export class CurrentUserUtils {
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
                 explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
             }));
-            (doc.myFilesystem as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder!]);
+            (doc.myFilesystem as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder]);
             (doc.myFilesystem as any as Doc).contextMenuLabels = new List<string>(["Create new folder"]);
             (doc.myFilesystem as any as Doc).childContextMenuIcons = new List<string>(["plus"]);
         }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 68ec97523..9b1cc3499 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -64,6 +64,7 @@ import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
 import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 import { ScriptField } from '../../fields/ScriptField';
+import { ButtonType } from './nodes/button/FontIconBox';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -245,9 +246,9 @@ export class MainView extends React.Component {
         if (!await this.userDoc.myFilesystem) {
             this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
             const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
-            const newFolderButton:Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
+            const newFolderButton: Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
             this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], {
-                title: "My Documents", _showTitle: "title",  buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
+                title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
                 isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 2727a0af5..aa7b164b0 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -54,7 +54,7 @@ export interface TreeViewProps {
     indentDocument?: (editTitle: boolean) => void;
     outdentDocument?: (editTitle: boolean) => void;
     ScreenToLocalTransform: () => Transform;
-    contextMenuItems: { script: ScriptField, filter: ScriptField, label: string }[];
+    contextMenuItems: { script: ScriptField, filter: ScriptField, icon: string, label: string }[];
     dontRegisterView?: boolean;
     styleProvider?: StyleProviderFunc | undefined;
     treeViewHideHeaderFields: () => boolean;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5de4c2c4e..007dca0c4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -154,7 +154,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     NativeWidth?: () => number;
     NativeHeight?: () => number;
     LayoutTemplate?: () => Opt<Doc>;
-    contextMenuItems?: () => { script: ScriptField, label: string, icon: string }[];
+    contextMenuItems?: () => { script: ScriptField, filter?: ScriptField, label: string, icon: string }[];
     onClick?: () => ScriptField;
     onDoubleClick?: () => ScriptField;
     onPointerDown?: () => ScriptField;
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index fb96969c5..5ae7c3628 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -246,7 +246,6 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         let text: string | undefined;
         let dropdown = true;
         let icon: IconProp = "caret-down";
-        let noneSelected: boolean = false;
 
         if (script === 'setView') {
             const selected = SelectionManager.Docs().lastElement();
@@ -301,7 +300,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             <div className={`menuButton ${this.type} ${active}`}
                 style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
                 onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
-                {dropdown || noneSelected ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
+                {dropdown ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
                 <div className="menuButton-dropdown-header">
                     {text && text[0].toUpperCase() + text.slice(1)}
                 </div>
@@ -334,22 +333,14 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         const scriptCheck: string = script + "(undefined, true)";
         const boolResult = ScriptField.MakeScript(scriptCheck)?.script.run().result;
 
-        let stroke: boolean = false;
-        let strokeIcon: any;
-        // if (script === "setStrokeColor") {
-        //     stroke = true;
-        //     const checkWidth = ScriptField.MakeScript("setStrokeWidth(0, true)")?.script.run().result;
-        //     const width = 20 + (checkWidth / 100) * 70;
-        //     const height = 20 + (checkWidth / 100) * 70;
-        //     strokeIcon = (<div style={{ borderRadius: "100%", width: width + '%', height: height + '%', backgroundColor: boolResult ? boolResult : "#FFFFFF" }} />);
-        // }
+        const showAlpha: boolean = script !== "setStrokeColor"; // bcz: ugh this should be changed to a field property on the button
 
         const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
             '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
             '#FFFFFF', '#f1efeb', "transparent"];
 
         const colorBox = (func: (color: ColorState) => void) => <SketchPicker
-            disableAlpha={!stroke}
+            disableAlpha={!showAlpha}
             onChange={func} color={boolResult ? boolResult : "#FFFFFF"}
             presetColors={colorOptions} />;
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
@@ -375,10 +366,8 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}
                 onPointerDown={e => e.stopPropagation()}>
-                {stroke ? strokeIcon : <><FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                    <div className="colorButton-color"
-                        style={{ backgroundColor: boolResult ? boolResult : "#FFFFFF" }}
-                    ></div></>}
+                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                <div className="colorButton-color" style={{ backgroundColor: boolResult ? boolResult : "#FFFFFF" }} />
                 {label}
                 {/* {dropdownCaret} */}
                 {this.rootDoc.dropDownOpen ?
diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
index 1809f4e2e..235495250 100644
--- a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
+++ b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
@@ -14,8 +14,7 @@ export class ColorDropdown extends Component<IButtonProps> {
         const scriptCheck: string = script + "(undefined, true)";
         const boolResult = ScriptField.MakeScript(scriptCheck)?.script.run().result;
 
-        let stroke: boolean = false;
-        let strokeIcon: any;
+        const stroke: boolean = false;
         // if (script === "setStrokeColor") {
         //     stroke = true;
         //     const checkWidth = ScriptField.MakeScript("setStrokeWidth(0, true)")?.script.run().result;
@@ -55,10 +54,9 @@ export class ColorDropdown extends Component<IButtonProps> {
                 style={{ color: this.props.color, borderBottomLeftRadius: active ? 0 : undefined }}
                 onClick={() => this.props.rootDoc.dropDownOpen = !this.props.rootDoc.dropDownOpen}
                 onPointerDown={e => e.stopPropagation()}>
-                {stroke ? strokeIcon : <><FontAwesomeIcon className={`fontIconBox-icon-${this.props.type}`} icon={this.props.icon} color={this.props.color} />
-                    <div className="colorButton-color"
-                        style={{ backgroundColor: boolResult ? boolResult : "#FFFFFF" }}
-                    ></div></>}
+                <FontAwesomeIcon className={`fontIconBox-icon-${this.props.type}`} icon={this.props.icon} color={this.props.color} />
+                <div className="colorButton-color"
+                    style={{ backgroundColor: boolResult ? boolResult : "#FFFFFF" }} />
                 {label}
                 {/* {dropdownCaret} */}
                 {this.props.rootDoc.dropDownOpen ?
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 4b540312e..fce95547e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1160,7 +1160,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         selectOnLoad && this._editorView!.focus();
         // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
         if (this._editorView && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) {
-            this._editorView.state.storedMarks = [...(this._editorView!.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
+            this._editorView.state.storedMarks = [...(this._editorView.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
         }
     }
 
-- 
cgit v1.2.3-70-g09d2


From f4b3781cf2250477482d8260e7137d9cbdfcb8e4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 14 Sep 2021 16:44:35 -0400
Subject: fixed height of maincontent.  fixed warnings.

---
 src/client/views/MainView.tsx                         | 19 +++++++++++--------
 .../collectionSchema/CollectionSchemaCells.tsx        |  4 ++--
 2 files changed, 13 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 39a14285a..b102c9a1f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -11,7 +11,6 @@ import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
-import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, PromiseValue, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
@@ -37,7 +36,6 @@ import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
 import { CollectionViewType } from './collections/CollectionView';
 import "./collections/TreeView.scss";
-import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
@@ -50,11 +48,9 @@ import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
 import "./MainView.scss";
 import { AudioBox } from './nodes/AudioBox';
-import { ButtonType } from './nodes/button/FontIconBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import { LinkDocPreview } from './nodes/LinkDocPreview';
 import { RadialMenu } from './nodes/RadialMenu';
@@ -66,6 +62,10 @@ import { PreviewCursor } from './PreviewCursor';
 import { PropertiesView } from './PropertiesView';
 import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
+import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
+import { ScriptField } from '../../fields/ScriptField';
+import { ButtonType } from './nodes/button/FontIconBox';
+import { ComponentDecorations } from './ComponentDecorations';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -247,7 +247,10 @@ export class MainView extends React.Component {
         if (!await this.userDoc.myFilesystem) {
             this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
             const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
-            const newFolderButton: Doc = Docs.Create.FontIconDocument({ onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true });
+            const newFolderButton: Doc = Docs.Create.FontIconDocument({
+                onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder",
+                btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true
+            });
             this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], {
                 title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
@@ -450,7 +453,7 @@ export class MainView extends React.Component {
                 r && new _global.ResizeObserver(action(() => { this._panelWidth = r.getBoundingClientRect().width; this._panelHeight = r.getBoundingClientRect().height; })).observe(r);
             }} style={{
                 color: this.darkScheme ? "rgb(205,205,205)" : "black",
-                height: `calc(100% - ${this.topOffset}px)`,
+                height: `calc(100% - ${this.topOffset - 35}px)`,
                 width: "100%",
             }} >
                 {this.mainInnerContent}
@@ -495,8 +498,8 @@ export class MainView extends React.Component {
                     rootSelected={returnTrue}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
-                    isContentActive={returnFalse}
                     isAnyChildContentActive={returnFalse}
+                    isContentActive={returnFalse}
                     isSelected={returnFalse}
                     docViewPath={returnEmptyDoclist}
                     moveDocument={this.moveButtonDoc}
@@ -604,7 +607,7 @@ export class MainView extends React.Component {
             <GroupManager />
             <GoogleAuthenticationManager />
             <DocumentDecorations PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
-            <ComponentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset + 25} />
+            <ComponentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset + 27} /> {/*  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js */}
             {this.topbar}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index fd99abce5..ed196349e 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -293,12 +293,12 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
                                                 }
                                             }
                                             const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
-                                            const changeMade = value.length !== value.length || value.length - 2 !== value.length;
+                                            const changeMade = value.length - 2 !== value.length;
                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
                                             // handle booleans
                                         } else if (inputIsBool) {
                                             const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
-                                            const changeMade = value.length !== value.length || value.length - 2 !== value.length;
+                                            const changeMade = value.length - 2 !== value.length;
                                             script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
                                         }
                                     }
-- 
cgit v1.2.3-70-g09d2


From e0240412990f102523b86e324bc973cd2ed523a1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 14 Sep 2021 22:27:17 -0400
Subject: refactored a bunch of names in MainView.  Fixed scrolling in left
 menu flyout treeview panels.

---
 src/client/views/MainView.scss                     |  62 +---------
 src/client/views/MainView.tsx                      | 126 +++++++++++----------
 src/client/views/collections/CollectionMenu.tsx    |  34 +++---
 .../views/collections/CollectionTreeView.tsx       |  68 +++++------
 src/client/views/collections/TreeView.tsx          |   2 +-
 src/client/views/global/globalCssVariables.scss    |   8 +-
 .../views/global/globalCssVariables.scss.d.ts      |   4 +-
 7 files changed, 130 insertions(+), 174 deletions(-)

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

diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 817e45699..4f871f5ec 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -15,8 +15,8 @@
     margin-top: 10px;
 }
 
-.mainContent-div-flyout,
-.mainContent-div {
+.mainView-dockingContent-flyout,
+.mainView-dockingContent {
     position: relative;
     width: 100%;
     height: 100%;
@@ -99,7 +99,7 @@
     }
 }
 
-.mainView-mainContent {
+.mainView-dashboardArea {
     width: 100%;
     height: 100%;
     position: absolute;
@@ -208,7 +208,7 @@
     }
 }
 
-.mainView-menuPanel {
+.mainView-leftMenuPanel {
     min-width: var(--menuPanelWidth);
     background-color: $dark-gray;
     border-right: $standard-border;
@@ -220,60 +220,6 @@
     ::-webkit-scrollbar {
         width: 0;
     }
-
-    .mainView-menuPanel-button {
-        padding: 7px;
-        padding-left: 7px;
-        width: 100%;
-        background: $dark-gray;
-
-        .mainView-menuPanel-button-wrap {
-            width: 45px;
-            /* padding: 5px; */
-            touch-action: none;
-            background: $dark-gray;
-            transform-origin: top left;
-            /* margin-bottom: 5px; */
-            margin-top: 5px;
-            margin-right: 25px;
-            border-radius: 8px;
-
-            &:hover {
-                background: $black;
-                cursor: pointer;
-            }
-        }
-    }
-
-    .mainView-menuPanel-button-label {
-        color: white;
-        margin-left: px;
-        margin-right: 4px;
-        border-radius: 8px;
-        width: 42px;
-        position: relative;
-        text-align: center;
-        font-size: 8px;
-        margin-top: 1px;
-        letter-spacing: normal;
-        padding: 3px;
-        background-color: inherit;
-    }
-
-    .mainView-menuPanel-button-icon {
-        width: auto;
-        height: 35px;
-        padding: 5px;
-    }
-}
-
-.mainView-searchPanel {
-    width: 100%;
-    height: $searchpanel-height;
-    background-color: black;
-    color: white;
-    text-align: center;
-    vertical-align: middle;
 }
 
 .mainView-mainDiv {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b102c9a1f..5bda9f6bf 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -11,9 +11,10 @@ import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
+import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, PromiseValue, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocUtils } from '../documents/Documents';
@@ -36,11 +37,12 @@ import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
 import { CollectionViewType } from './collections/CollectionView';
 import "./collections/TreeView.scss";
+import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
 import { GestureOverlay } from './GestureOverlay';
-import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './global/globalCssVariables.scss';
+import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
 import { Colors } from './global/globalEnums';
 import { KeyManager } from './GlobalKeyHandler';
 import { InkStrokeProperties } from './InkStrokeProperties';
@@ -48,9 +50,11 @@ import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
 import "./MainView.scss";
 import { AudioBox } from './nodes/AudioBox';
+import { ButtonType } from './nodes/button/FontIconBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import { LinkDocPreview } from './nodes/LinkDocPreview';
 import { RadialMenu } from './nodes/RadialMenu';
@@ -62,10 +66,6 @@ import { PreviewCursor } from './PreviewCursor';
 import { PropertiesView } from './PropertiesView';
 import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
-import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
-import { ScriptField } from '../../fields/ScriptField';
-import { ButtonType } from './nodes/button/FontIconBox';
-import { ComponentDecorations } from './ComponentDecorations';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -76,21 +76,32 @@ export class MainView extends React.Component {
     @observable public LastButton: Opt<Doc>;
     @observable private _windowWidth: number = 0;
     @observable private _windowHeight: number = 0;
-    @observable private _panelWidth: number = 0;
-    @observable private _panelHeight: number = 0;
+    @observable private _dashUIWidth: number = 0;   // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
+    @observable private _dashUIHeight: number = 0;  // height of entire main dashboard region including top menu buttons
     @observable private _panelContent: string = "none";
     @observable private _sidebarContent: any = this.userDoc?.sidebar;
-    @observable private _flyoutWidth: number = 0;
+    @observable private _leftMenuFlyoutWidth: number = 0;
 
-    @computed private get topOffset() { return 35 + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } //TODO remove
-    @computed private get leftOffset() { return this.menuPanelWidth() - 2; }
+    @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
+    @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
+    @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
+    @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
     @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
-    @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
-
-    menuPanelWidth = () => Number(MENU_PANEL_WIDTH.replace("px", ""));
-    propertiesWidth = () => Math.max(0, Math.min(this._panelWidth - 50, CurrentUserUtils.propertiesWidth || 0));
+    @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
+
+    topMenuHeight = () => 35;
+    topMenuWidth = returnZero; // value is ignored ...
+    leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", ""));
+    leftMenuHeight = () => this._dashUIHeight;
+    leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
+    leftMenuFlyoutHeight = () => this._dashUIHeight;
+    propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
+    propertiesHeight = () => this._dashUIHeight;
+    mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
+    mainDocViewHeight = () => this._dashUIHeight - this.topMenuHeight();
 
     componentDidMount() {
         document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
@@ -264,11 +275,6 @@ export class MainView extends React.Component {
         Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
     }
 
-    getPWidth = () => this._panelWidth - this.propertiesWidth();
-    getPHeight = () => this._panelHeight - (CollectionMenu.Instance?.Pinned ? 35 : 0);
-    getContentsHeight = () => this._panelHeight;
-    getMenuPanelHeight = () => this._panelHeight + (CollectionMenu.Instance?.Pinned ? 35 : 0);
-
     @computed get mainDocView() {
         return <DocumentView key="main"
             Document={this.mainContainer!}
@@ -283,8 +289,8 @@ export class MainView extends React.Component {
             isContentActive={returnTrue}
             removeDocument={undefined}
             ScreenToLocalTransform={Transform.Identity}
-            PanelWidth={this.getPWidth}
-            PanelHeight={this.getPHeight}
+            PanelWidth={this.mainDocViewWidth}
+            PanelHeight={this.mainDocViewHeight}
             focus={DocUtils.DefaultFocus}
             whenChildContentsActiveChanged={emptyFunction}
             bringToFront={emptyFunction}
@@ -298,11 +304,9 @@ export class MainView extends React.Component {
     }
 
     @computed get dockingContent() {
-        return <div key="docking" className={`mainContent-div${this._flyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
-            // style={{ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`, width: `calc(100% - ${this._flyoutWidth + this.propertiesWidth()}px)` }}>
-            // FIXME update with property panel width
+        return <div key="docking" className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
             style={{
-                minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`,
+                minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
                 transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
             }}>
             {!this.mainContainer ? (null) : this.mainDocView}
@@ -312,22 +316,21 @@ export class MainView extends React.Component {
     @action
     onPropertiesPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e,
-            action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._panelWidth - e.clientX)) ? false : false),
+            action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false),
             action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
-            action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._panelWidth - 50, 250) : 0), false);
+            action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0), false);
     }
 
     @action
     onFlyoutPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e,
-            action(e => (this._flyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
-            () => this._flyoutWidth < 5 && this.closeFlyout(),
+            action(e => (this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
+            () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(),
             this.closeFlyout);
     }
 
-    flyoutWidthFunc = () => this._flyoutWidth;
-    sidebarScreenToLocal = () => new Transform(0, -this.topOffset, 1);
-    mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftOffset, 0);
+    sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1);
+    mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
     addDocTabFunc = (doc: Doc, where: string): boolean => {
         return where === "close" ? CollectionDockingView.CloseSplit(doc) :
             doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : CollectionDockingView.AddSplit(doc, "right");
@@ -335,10 +338,10 @@ export class MainView extends React.Component {
 
 
     @computed get flyout() {
-        return !this._flyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}>
+        return !this._leftMenuFlyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}>
             {this.docButtons}
         </div> :
-            <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._flyoutWidth, width: this._flyoutWidth }} >
+            <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }} >
                 <div className="mainView-contentArea" >
                     <DocumentView
                         Document={this._sidebarContent.proto || this._sidebarContent}
@@ -352,8 +355,8 @@ export class MainView extends React.Component {
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
-                        PanelWidth={this.flyoutWidthFunc}
-                        PanelHeight={this.getContentsHeight}
+                        PanelWidth={this.leftMenuFlyoutWidth}
+                        PanelHeight={this.leftMenuFlyoutHeight}
                         renderDepth={0}
                         isContentActive={returnTrue}
                         scriptContext={CollectionDockingView.Instance?.props.Document}
@@ -371,8 +374,8 @@ export class MainView extends React.Component {
             </div>;
     }
 
-    @computed get menuPanel() {
-        return <div key="menu" className="mainView-menuPanel">
+    @computed get leftMenuPanel() {
+        return <div key="menu" className="mainView-leftMenuPanel">
             <DocumentView
                 Document={Doc.UserDoc().menuStack as Doc}
                 DataDoc={undefined}
@@ -382,8 +385,8 @@ export class MainView extends React.Component {
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
                 ScreenToLocalTransform={this.sidebarScreenToLocal}
-                PanelWidth={this.menuPanelWidth}
-                PanelHeight={this.getMenuPanelHeight}
+                PanelWidth={this.leftMenuWidth}
+                PanelHeight={this.leftMenuHeight}
                 renderDepth={0}
                 docViewPath={returnEmptyDoclist}
                 focus={DocUtils.DefaultFocus}
@@ -405,7 +408,7 @@ export class MainView extends React.Component {
     @action
     selectMenu = (button: Doc) => {
         const title = StrCast(Doc.GetProto(button).title);
-        const willOpen = !this._flyoutWidth || this._panelContent !== title;
+        const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title;
         this.closeFlyout();
         if (willOpen) {
             switch (this._panelContent = title) {
@@ -422,38 +425,41 @@ export class MainView extends React.Component {
     }
 
     @computed get mainInnerContent() {
-        const width = this.propertiesWidth() + this._flyoutWidth + this.menuPanelWidth();
-        const transform = this._flyoutWidth ? 'translate(-28px, 0px)' : undefined;
+        const width = this.propertiesWidth() + this._leftMenuFlyoutWidth + this.leftMenuWidth();
+        const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined;
         return <>
-            {this.menuPanel}
+            {this.leftMenuPanel}
             <div key="inner" className={`mainView-innerContent${this.darkScheme ? "-dark" : ""}`}>
                 {this.flyout}
-                <div className="mainView-libraryHandle" style={{ display: !this._flyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
+                <div className="mainView-libraryHandle" style={{ display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
                     <FontAwesomeIcon icon="chevron-left" color={this.darkScheme ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
                 </div>
                 <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)`, transform: transform }}>
-                    <CollectionMenu />
+                    <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
 
                     {this.dockingContent}
 
-                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._flyoutWidth ? 0 : this.propertiesWidth() - 1 }}>
+                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._leftMenuFlyoutWidth ? 0 : this.propertiesWidth() - 1 }}>
                         <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? Colors.WHITE : Colors.BLACK} size="sm" />
                     </div>
                     <div className="properties-container">
-                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />}
+                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
                     </div>
                 </div>
             </div>
         </>;
     }
 
-    @computed get mainContent() {
+    @computed get mainDashboardArea() {
         return !this.userDoc ? (null) :
-            <div className="mainView-mainContent" ref={r => {
-                r && new _global.ResizeObserver(action(() => { this._panelWidth = r.getBoundingClientRect().width; this._panelHeight = r.getBoundingClientRect().height; })).observe(r);
+            <div className="mainView-dashboardArea" ref={r => {
+                r && new _global.ResizeObserver(action(() => {
+                    this._dashUIWidth = r.getBoundingClientRect().width;
+                    this._dashUIHeight = r.getBoundingClientRect().height;
+                })).observe(r);
             }} style={{
                 color: this.darkScheme ? "rgb(205,205,205)" : "black",
-                height: `calc(100% - ${this.topOffset - 35}px)`,
+                height: `calc(100% - ${this.topOfDashUI}px)`,
                 width: "100%",
             }} >
                 {this.mainInnerContent}
@@ -461,7 +467,7 @@ export class MainView extends React.Component {
     }
 
     expandFlyout = action((button: Doc) => {
-        this._flyoutWidth = (this._flyoutWidth || 250);
+        this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250);
         this._sidebarContent.proto = button.target as any;
         this.LastButton = button;
         console.log(button.title);
@@ -471,7 +477,7 @@ export class MainView extends React.Component {
         this.LastButton = undefined;
         this._panelContent = "none";
         this._sidebarContent.proto = undefined;
-        this._flyoutWidth = 0;
+        this._leftMenuFlyoutWidth = 0;
     });
 
     remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
@@ -509,8 +515,8 @@ export class MainView extends React.Component {
                     pinToPres={emptyFunction}
                     removeDocument={this.remButtonDoc}
                     ScreenToLocalTransform={this.buttonBarXf}
-                    PanelWidth={this.flyoutWidthFunc}
-                    PanelHeight={this.getContentsHeight}
+                    PanelWidth={this.leftMenuFlyoutWidth}
+                    PanelHeight={this.leftMenuFlyoutHeight}
                     renderDepth={0}
                     focus={DocUtils.DefaultFocus}
                     whenChildContentsActiveChanged={emptyFunction}
@@ -606,14 +612,14 @@ export class MainView extends React.Component {
             <CaptureManager />
             <GroupManager />
             <GoogleAuthenticationManager />
-            <DocumentDecorations PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
-            <ComponentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset + 27} /> {/*  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js */}
+            <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
+            <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
             {this.topbar}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
             <GestureOverlay >
-                {this.mainContent}
+                {this.mainDashboardArea}
             </GestureOverlay>
             <PreviewCursor />
             <TaskCompletionBox />
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 6ffa6c279..8e03b2db2 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -42,8 +42,13 @@ import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
 import { TabDocView } from "./TabDocView";
 import { Colors } from "../global/globalEnums";
 
+interface CollectionMenuProps {
+    panelHeight: () => number;
+    panelWidth: () => number;
+}
+
 @observer
-export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
+export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
     @observable static Instance: CollectionMenu;
 
     @observable SelectedCollection: DocumentView | undefined;
@@ -93,13 +98,10 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
         return new Transform(-translateX, -translateY, 1 / scale);
     }
 
-    panelWidth100 = () => 100;
-    panelHeight35 = () => 35;
-
     @computed get contMenuButtons() {
         trace();
         const selDoc = Doc.UserDoc().contextMenuBtns;
-        return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: "35px" }} >
+        return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
             <CollectionLinearView
                 Document={selDoc}
                 DataDoc={undefined}
@@ -122,8 +124,8 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
                 pinToPres={emptyFunction}
                 removeDocument={returnFalse}
                 ScreenToLocalTransform={this.buttonBarXf}
-                PanelWidth={this.panelWidth100}
-                PanelHeight={this.panelHeight35}
+                PanelWidth={this.props.panelWidth}
+                PanelHeight={this.props.panelHeight}
                 renderDepth={0}
                 focus={emptyFunction}
                 whenChildContentsActiveChanged={emptyFunction}
@@ -176,7 +178,7 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
     }
 }
 
-interface CollectionMenuProps {
+interface CollectionViewMenuProps {
     type: CollectionViewType;
     fieldKey: string;
     docView: DocumentView;
@@ -185,7 +187,7 @@ interface CollectionMenuProps {
 const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
 
 @observer
-export class CollectionViewBaseChrome extends React.Component<CollectionMenuProps> {
+export class CollectionViewBaseChrome extends React.Component<CollectionViewMenuProps> {
     //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
 
     get document() { return this.props.docView?.props.Document; }
@@ -628,14 +630,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
 }
 
 @observer
-export class CollectionDockingChrome extends React.Component<CollectionMenuProps> {
+export class CollectionDockingChrome extends React.Component<CollectionViewMenuProps> {
     render() {
         return (null);
     }
 }
 
 @observer
-export class CollectionFreeFormViewChrome extends React.Component<CollectionMenuProps & { isOverlay: boolean, isDoc?: boolean }> {
+export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean, isDoc?: boolean }> {
     public static Instance: CollectionFreeFormViewChrome;
     constructor(props: any) {
         super(props);
@@ -866,7 +868,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
     }
 }
 @observer
-export class CollectionStackingViewChrome extends React.Component<CollectionMenuProps> {
+export class CollectionStackingViewChrome extends React.Component<CollectionViewMenuProps> {
     @observable private _currentKey: string = "";
     @observable private suggestions: string[] = [];
 
@@ -987,7 +989,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
 
 
 @observer
-export class CollectionSchemaViewChrome extends React.Component<CollectionMenuProps> {
+export class CollectionSchemaViewChrome extends React.Component<CollectionViewMenuProps> {
     // private _textwrapAllRows: boolean = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
     get document() { return this.props.docView.props.Document; }
 
@@ -1035,7 +1037,7 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionMenuPr
 }
 
 @observer
-export class CollectionTreeViewChrome extends React.Component<CollectionMenuProps> {
+export class CollectionTreeViewChrome extends React.Component<CollectionViewMenuProps> {
 
     get document() { return this.props.docView.props.Document; }
     get sortAscending() {
@@ -1072,7 +1074,7 @@ export class CollectionTreeViewChrome extends React.Component<CollectionMenuProp
 
 // Enter scroll speed for 3D Carousel 
 @observer
-export class Collection3DCarouselViewChrome extends React.Component<CollectionMenuProps> {
+export class Collection3DCarouselViewChrome extends React.Component<CollectionViewMenuProps> {
     get document() { return this.props.docView.props.Document; }
     @computed get scrollSpeed() {
         return this.document._autoScrollSpeed;
@@ -1113,7 +1115,7 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionMe
  * Chrome for grid view.
  */
 @observer
-export class CollectionGridViewChrome extends React.Component<CollectionMenuProps> {
+export class CollectionGridViewChrome extends React.Component<CollectionViewMenuProps> {
 
     private clicked: boolean = false;
     private entered: boolean = false;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 4d62a1af4..624a0bf4a 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -267,16 +267,17 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         return hideTitle ? (null) : (this.outlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren);
     }
 
+    return35 = () => 35;
     @computed get buttonMenu() {
-        const menuDoc:Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+        const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
         // To create a multibutton menu add a CollectionLinearView
-        if (menuDoc){
-            
+        if (menuDoc) {
+
             const width: number = NumCast(menuDoc._width, 30);
             const height: number = NumCast(menuDoc._height, 30);
             console.log(menuDoc.title, width, height);
             return (<div className="buttonMenu-docBtn"
-                style = {{width: width, height: height}}>
+                style={{ width: width, height: height }}>
                 <DocumentView
                     Document={menuDoc}
                     DataDoc={menuDoc}
@@ -289,8 +290,8 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                     rootSelected={this.props.isSelected}
                     removeDocument={this.props.removeDocument}
                     ScreenToLocalTransform={Transform.Identity}
-                    PanelWidth={() => 35}
-                    PanelHeight={() => 35}
+                    PanelWidth={this.return35}
+                    PanelHeight={this.return35}
                     renderDepth={this.props.renderDepth + 1}
                     focus={emptyFunction}
                     styleProvider={this.props.styleProvider}
@@ -302,13 +303,14 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
                     docRangeFilters={this.props.docRangeFilters}
                     searchFilterDocs={this.props.searchFilterDocs}
                     ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined} 
+                    ContainingCollectionDoc={undefined}
                 />
             </div>);
         }
     }
 
-    
+    @observable _explainerHeight: number = 0;
+
     @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
     @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
 
@@ -336,30 +338,30 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
         const noviceExplainer = this.rootDoc.explainer;
 
         return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
-               <>
-                    {this.titleBar}
-                    <div className="collectionTreeView-container"
-                        style={this.outlineMode ? { transform: `scale(${this.contentScaling})`, width: `calc(${100 / this.contentScaling}%)` } : {}}
-                        onContextMenu={this.onContextMenu}>
-                        {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
-                            {buttonMenu ? this.buttonMenu : null}
-                            {Doc.UserDoc().noviceMode && noviceExplainer ? 
-                                <div className="documentExplanation">
-                                    {noviceExplainer}
-                                </div>
-                                : null
-                            }
-                        </div> : null}
-                        <div className="collectionTreeView-dropTarget"
-                            style={{ background: background(), paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingBottom: `${this.paddingBot()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }}
-                            onWheel={e => e.stopPropagation()}
-                            onDrop={this.onTreeDrop}
-                            ref={this.createTreeDropTarget}>
-                            <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
-                                {this.treeViewElements}
-                            </ul>
-                        </div >
-                    </div>
-               </>;
+            <>
+                {this.titleBar}
+                <div className="collectionTreeView-container"
+                    style={this.outlineMode ? { transform: `scale(${this.contentScaling})`, width: `calc(${100 / this.contentScaling}%)` } : {}}
+                    onContextMenu={this.onContextMenu}>
+                    {buttonMenu || noviceExplainer ? <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
+                        {buttonMenu ? this.buttonMenu : null}
+                        {Doc.UserDoc().noviceMode && noviceExplainer ?
+                            <div className="documentExplanation">
+                                {noviceExplainer}
+                            </div>
+                            : null
+                        }
+                    </div> : null}
+                    <div className="collectionTreeView-dropTarget"
+                        style={{ background: background(), height: `calc(100% - ${this._explainerHeight}px)`, paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingBottom: `${this.paddingBot()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }}
+                        onWheel={e => e.stopPropagation()}
+                        onDrop={this.onTreeDrop}
+                        ref={this.createTreeDropTarget}>
+                        <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
+                            {this.treeViewElements}
+                        </ul>
+                    </div >
+                </div>
+            </>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index aa7b164b0..4fc25752f 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -869,7 +869,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const childLayout = Doc.Layout(pair.layout);
             const rowHeight = () => {
                 const aspect = Doc.NativeAspect(childLayout);
-                return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
+                return (aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]());
             };
             return <TreeView key={child[Id]} ref={r => treeViewRefs.set(child, r ? r : undefined)}
                 document={pair.layout}
diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index caa9f4fe5..95bd44c1f 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -54,7 +54,7 @@ $standard-border-radius: 3px;
 $standard-box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
 
 
-$searchpanel-height: 32px;
+$dashboardselector-height: 32px;
 $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc
 $docDecorations-zindex: 998; // then doc decorations appear over everything else
 $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right?
@@ -63,7 +63,7 @@ $SCHEMA_DIVIDER_WIDTH: 4;
 $MINIMIZED_ICON_SIZE: 24;
 $MAX_ROW_HEIGHT: 44px;
 $DFLT_IMAGE_NATIVE_DIM: 900px;
-$MENU_PANEL_WIDTH: 60px;
+$LEFT_MENU_WIDTH: 60px;
 $TREE_BULLET_WIDTH: 20px;
 
 :export {
@@ -74,8 +74,8 @@ $TREE_BULLET_WIDTH: 20px;
     MAX_ROW_HEIGHT: $MAX_ROW_HEIGHT;
     SEARCH_THUMBNAIL_SIZE: $search-thumnail-size;
     ANTIMODEMENU_HEIGHT: $antimodemenu-height;
-    SEARCH_PANEL_HEIGHT: $searchpanel-height;
+    DASHBOARD_SELECTOR_HEIGHT: $dashboardselector-height;
     DFLT_IMAGE_NATIVE_DIM: $DFLT_IMAGE_NATIVE_DIM;
-    MENU_PANEL_WIDTH: $MENU_PANEL_WIDTH;
+    LEFT_MENU_WIDTH: $LEFT_MENU_WIDTH;
     TREE_BULLET_WIDTH: $TREE_BULLET_WIDTH;
 }
\ No newline at end of file
diff --git a/src/client/views/global/globalCssVariables.scss.d.ts b/src/client/views/global/globalCssVariables.scss.d.ts
index 11e62e1eb..59c2b3585 100644
--- a/src/client/views/global/globalCssVariables.scss.d.ts
+++ b/src/client/views/global/globalCssVariables.scss.d.ts
@@ -7,9 +7,9 @@ interface IGlobalScss {
     MAX_ROW_HEIGHT: string;
     SEARCH_THUMBNAIL_SIZE: string;
     ANTIMODEMENU_HEIGHT: string;
-    SEARCH_PANEL_HEIGHT: string;
+    DASHBOARD_SELECTOR_HEIGHT: string;
     DFLT_IMAGE_NATIVE_DIM: string;
-    MENU_PANEL_WIDTH: string;
+    LEFT_MENU_WIDTH: string;
     TREE_BULLET_WIDTH: string;
 }
 declare const globalCssVariables: IGlobalScss;
-- 
cgit v1.2.3-70-g09d2


From 1395ce3fbf73bf5df5ed4add744c333cfc8008c9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 15 Sep 2021 15:43:24 -0400
Subject: another windows fix for contextmenus on web pages

---
 src/client/views/MainView.tsx     |  1 +
 src/client/views/nodes/WebBox.tsx | 30 ++++++++++++++++++------------
 2 files changed, 19 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5bda9f6bf..d854f118f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -214,6 +214,7 @@ export class MainView extends React.Component {
                 }
             }
         }, false);
+        document.oncontextmenu = () => false;
     }
 
     initAuthenticationRouters = async () => {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index fe5070fa4..cce71329d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -224,15 +224,20 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
 
         // bcz: hack - iframe grabs all events which messes up how we handle contextMenus.  So this super naively simulates the event stack to get the specific menu items and the doc view menu items.
-        if (e.button === 2 && ContextMenu.Instance.getItems().length) {
-            ContextMenu.Instance.displayMenu(e.pageX, e.pageY);
-        }
-        else {
-            ContextMenu.Instance.closeMenu();
-            if (e.button === 2) {
+        if (e.button === 2) {
+            e.preventDefault();
+            e.stopPropagation();
+            const where = this.props.ScreenToLocalTransform().inverse().transformPoint(e.pageX, e.pageY);
+            if (ContextMenu.Instance.getItems().length) {
+                ContextMenu.Instance.displayMenu(where[0], where[1]);
+            }
+            else {
+                ContextMenu.Instance.closeMenu();
                 this.specificContextMenu(e);
-                this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, e.pageX, e.pageY);
+                this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, where[0], where[1]);
             }
+        } else {
+            ContextMenu.Instance.closeMenu();
         }
     }
 
@@ -511,11 +516,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @computed get annotationLayer() {
         TraceMobx();
-        return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
-            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation {...this.props} fieldKey={this.annotationKey} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)
-            }
-        </div>;
+        return !this.inlineTextAnnotations.length ? (null) :
+            <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+                {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
+                    <Annotation {...this.props} fieldKey={this.annotationKey} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)
+                }
+            </div>;
 
     }
     @observable _showSidebar = false;
-- 
cgit v1.2.3-70-g09d2


From eb50e46332c2f0b48c7cb165d55245aab317ef08 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 21 Sep 2021 19:43:32 -0400
Subject: fixed up some darkScheme css, enabled comic mode for developers,
 enabled opening up fields on dashboards in myDocuments

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/CurrentUserUtils.ts                | 30 ++++++++++------
 src/client/util/SelectionManager.ts                | 14 ++------
 src/client/util/SettingsManager.tsx                | 19 +++++-----
 src/client/views/ContextMenu.scss                  |  1 -
 src/client/views/DocumentDecorations.scss          | 28 +++++++++++----
 src/client/views/DocumentDecorations.tsx           | 42 +++++++++++++---------
 src/client/views/MainView.scss                     | 12 +++----
 src/client/views/MainView.tsx                      | 14 ++++----
 src/client/views/StyleProvider.tsx                 |  5 +--
 .../views/collections/CollectionSchemaView.tsx     |  2 +-
 src/client/views/collections/TreeView.tsx          |  9 +++--
 .../CollectionFreeFormLayoutEngines.tsx            |  3 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 ++-
 .../collectionSchema/CollectionSchemaView.tsx      |  7 ++--
 src/client/views/nodes/DocumentView.tsx            |  6 ++--
 src/client/views/nodes/button/FontIconBox.tsx      |  2 --
 .../views/nodes/formattedText/DashDocView.tsx      |  3 +-
 src/mobile/MobileInterface.tsx                     |  4 +--
 19 files changed, 118 insertions(+), 89 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fafbc4a7d..206f65bfd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -862,7 +862,7 @@ export namespace Docs {
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
             const tabs = TreeDocument(documents, { title: "On-Screen Tabs", childDontRegisterViews: true, freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", _fitWidth: true, system: true, isFolder: true });
             const all = TreeDocument([], { title: "Off-Screen Tabs", childDontRegisterViews: true, freezeChildren: "add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", system: true, isFolder: true });
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
 
         export function DirectoryImportDocument(options: DocumentOptions = {}) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d5dc9e2be..568a9ddbd 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -20,6 +20,7 @@ import { Networking } from "../Network";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
 import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { TreeView } from "../views/collections/TreeView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
@@ -38,7 +39,6 @@ import { ColorScheme } from "./SettingsManager";
 import { SharingManager } from "./SharingManager";
 import { SnappingManager } from "./SnappingManager";
 import { UndoManager } from "./UndoManager";
-import { TreeView } from "../views/collections/TreeView";
 
 interface Button {
     title?: string;
@@ -816,16 +816,19 @@ export class CurrentUserUtils {
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true,
                 explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
             }));
-            // const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`);
-            // const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-            // const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
+            const toggleDarkTheme = ScriptField.MakeScript(`this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`);
+            const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
+            const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
             const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`);
             const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
-            (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]);
-            (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]);
-            (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "user-friends", "times"]);
-            // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
-            // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
+            const developerFilter = ScriptField.MakeFunction('!IsNoviceMode()');
+            // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]);
+            // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]);
+            // (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "user-friends", "times"]);
+            (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleDarkTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
+            (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
+            (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "chalkboard", "tv", "camera", "users", "times"]);
+            (doc.myDashboards as any as Doc).childContextMenuFilters = new List<ScriptField>([undefined as any, developerFilter, developerFilter, developerFilter, undefined as any, undefined as any]);
         }
         return doc.myDashboards as any as Doc;
     }
@@ -1302,7 +1305,6 @@ export class CurrentUserUtils {
             }, { fireImmediately: true });
         // Document properties on load
         doc.system = true;
-        doc.darkScheme = ColorScheme.Dark;
         doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
         doc.title = Doc.CurrentUserEmail;
         doc._raiseWhenDragged = true;
@@ -1321,7 +1323,6 @@ export class CurrentUserUtils {
         doc.fontColor = StrCast(doc.fontColor, "black");
         doc.fontHighlight = StrCast(doc.fontHighlight, "");
         doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false);
-        doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white");
         doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
         doc.noviceMode = BoolCast(doc.noviceMode, true);
         doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); //
@@ -1358,6 +1359,10 @@ export class CurrentUserUtils {
         // });
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
+        if (doc.activeDashboard instanceof Doc) {
+            // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+            doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme;
+        }
         return doc;
     }
 
@@ -1637,4 +1642,7 @@ Scripting.addGlobal(function makeTopLevelFolder() {
     const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
     TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined };
     return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder);
+});
+Scripting.addGlobal(function toggleComicMode() {
+    Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic";
 });
\ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index bac13373c..e507ec3bf 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -2,7 +2,6 @@ import { action, observable, ObservableMap } from "mobx";
 import { computedFn } from "mobx-utils";
 import { Doc, Opt } from "../../fields/Doc";
 import { DocumentType } from "../documents/DocumentTypes";
-import { CollectionSchemaView } from "../views/collections/collectionSchema/CollectionSchemaView";
 import { CollectionViewType } from "../views/collections/CollectionView";
 import { DocumentView } from "../views/nodes/DocumentView";
 
@@ -13,12 +12,10 @@ export namespace SelectionManager {
         @observable IsDragging: boolean = false;
         SelectedViews: ObservableMap<DocumentView, Doc> = new ObservableMap();
         @observable SelectedSchemaDocument: Doc | undefined;
-        @observable SelectedSchemaCollection: CollectionSchemaView | undefined;
 
         @action
-        SelectSchemaView(collectionView: Opt<CollectionSchemaView>, doc: Opt<Doc>) {
+        SelectSchemaViewDoc(doc: Opt<Doc>) {
             manager.SelectedSchemaDocument = doc;
-            manager.SelectedSchemaCollection = collectionView;
         }
         @action
         SelectView(docView: DocumentView, ctrlPressed: boolean): void {
@@ -33,7 +30,6 @@ export namespace SelectionManager {
             } else if (!ctrlPressed && Array.from(manager.SelectedViews.entries()).length > 1) {
                 Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false));
                 manager.SelectedSchemaDocument = undefined;
-                manager.SelectedSchemaCollection = undefined;
                 manager.SelectedViews.clear();
                 manager.SelectedViews.set(docView, docView.rootDoc);
             }
@@ -47,7 +43,6 @@ export namespace SelectionManager {
         }
         @action
         DeselectAll(): void {
-            manager.SelectedSchemaCollection = undefined;
             manager.SelectedSchemaDocument = undefined;
             Array.from(manager.SelectedViews.keys()).map(dv => dv.props.whenChildContentsActiveChanged(false));
             manager.SelectedViews.clear();
@@ -62,8 +57,8 @@ export namespace SelectionManager {
     export function SelectView(docView: DocumentView, ctrlPressed: boolean): void {
         manager.SelectView(docView, ctrlPressed);
     }
-    export function SelectSchemaView(colSchema: Opt<CollectionSchemaView>, document: Opt<Doc>): void {
-        manager.SelectSchemaView(colSchema, document);
+    export function SelectSchemaViewDoc(document: Opt<Doc>): void {
+        manager.SelectSchemaViewDoc(document);
     }
 
     const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) {  // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
@@ -96,9 +91,6 @@ export namespace SelectionManager {
     export function SelectedSchemaDoc(): Doc | undefined {
         return manager.SelectedSchemaDocument;
     }
-    export function SelectedSchemaCollection(): CollectionSchemaView | undefined {
-        return manager.SelectedSchemaCollection;
-    }
     export function Docs(): Doc[] {
         return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
     }
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index bd91db779..6a26dfdc7 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -19,9 +19,9 @@ export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
 export enum ColorScheme {
-    Dark = "Dark",
-    Light = "Light",
-    System = "Match System"
+    Dark = "-Dark",
+    Light = "-Light",
+    System = "-MatchSystem"
 }
 
 @observer
@@ -38,7 +38,7 @@ export class SettingsManager extends React.Component<{}> {
     @observable activeTab = "Accounts";
 
     @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
-    @computed get colorScheme() { return Doc.UserDoc().colorScheme; }
+    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard.colorScheme; }
 
     constructor(props: {}) {
         super(props);
@@ -81,16 +81,16 @@ export class SettingsManager extends React.Component<{}> {
         const scheme: ColorScheme = (e.currentTarget as any).value;
         switch (scheme) {
             case ColorScheme.Light:
-                Doc.UserDoc().colorScheme = ColorScheme.Light;
+                CurrentUserUtils.ActiveDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" });
                 break;
             case ColorScheme.Dark:
-                Doc.UserDoc().colorScheme = ColorScheme.Dark;
+                CurrentUserUtils.ActiveDashboard.colorScheme = ColorScheme.Dark;
                 addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" });
                 break;
             case ColorScheme.System: default:
                 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
-                    Doc.UserDoc().colorScheme = e.matches ? ColorScheme.Dark : ColorScheme.Light;
+                    CurrentUserUtils.ActiveDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 });
                 break;
         }
@@ -119,6 +119,7 @@ export class SettingsManager extends React.Component<{}> {
         </div>;
 
         const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System];
+        const schemeMap = ["Light", "Dark", "Match system"];
 
         return <div className="colors-content">
             <div className="preferences-color">
@@ -132,8 +133,8 @@ export class SettingsManager extends React.Component<{}> {
             <div className="preferences-colorScheme">
                 <div className="preferences-color-text">Color Scheme</div>
                 <div className="preferences-color-controls">
-                    <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(Doc.UserDoc().colorScheme)}>
-                        {colorSchemes.map(scheme => <option key={scheme} value={scheme}> {scheme} </option>)}
+                    <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme)}>
+                        {colorSchemes.map((scheme, i) => <option key={scheme} value={scheme}> {schemeMap[i]} </option>)}
                     </select>
                 </div>
             </div>
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index 47ae0424b..ea24dbf6d 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -8,7 +8,6 @@
     flex-direction: column;
     background: whitesmoke;
     border-radius: 3px;
-    border: solid $light-gray 1px;
 }
 
 // .contextMenu-item:first-child {
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index a9f50f81b..d8ad47ecb 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -2,10 +2,14 @@
 
 $linkGap: 3px;
 
+.documentDecorations-Dark,
 .documentDecorations {
   position: absolute;
   z-index: 2000;
 }
+.documentDecorations-Dark {
+    background: dimgray;
+}
 .documentDecorations-container {
     z-index: $docDecorations-zindex;
     position: absolute;
@@ -50,12 +54,17 @@ $linkGap: 3px;
         pointer-events: auto;
         background: $medium-gray;
         opacity: 0.1;
-
         &:hover {
             opacity: 1;
         }
     }
 
+    .documentDecorations-resizer-Dark
+    {
+        background: $light-gray;
+        opacity: 0.2;
+    }
+
     .documentDecorations-topLeftResizer,
     .documentDecorations-leftResizer,
     .documentDecorations-bottomLeftResizer {
@@ -221,6 +230,7 @@ $linkGap: 3px;
     cursor: ns-resize;
   }
 
+    .documentDecorations-title-Dark,
     .documentDecorations-title {
         opacity: 1;
         grid-column-start: 2;
@@ -233,14 +243,22 @@ $linkGap: 3px;
         height: 22px;
         position: absolute;
 
-        .documentDecorations-titleSpan {
+        .documentDecorations-titleSpan, 
+        .documentDecorations-titleSpan-Dark  {
             width: 100%;
             border-radius: 8px;
-            background: #ffffffcf;
+            background: #ffffffa0;
             position: absolute;
             display: inline-block;
             cursor: move;
         }
+        .documentDecorations-titleSpan-Dark {
+            background: hsla(0, 0%, 0%, 0.412);
+        }
+    }
+    .documentDecorations-title-Dark {
+        color: white;
+        background: black;
     }
 
   .documentDecorations-contextMenu {
@@ -439,10 +457,6 @@ $linkGap: 3px;
   }
 }
 
-.documentDecorations-darkScheme {
-  background: dimgray;
-}
-
 #template-list {
   position: absolute;
   top: 25px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d785d5419..e9a54d6a5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -8,7 +8,7 @@ import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSy
 import { Document } from '../../fields/documentSchemas';
 import { InkField } from "../../fields/InkField";
 import { ScriptField } from '../../fields/ScriptField';
-import { Cast, NumCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { GetEffectiveAcl } from '../../fields/util';
 import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils";
 import { Docs } from "../documents/Documents";
@@ -27,6 +27,8 @@ import { LightboxView } from './LightboxView';
 import { DocumentView } from "./nodes/DocumentView";
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import React = require("react");
+import { dark } from '@material-ui/core/styles/createPalette';
+import { color } from 'd3-color';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> {
@@ -241,7 +243,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         InkStrokeProperties.Instance?._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK)
             .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc));
 
-        if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
+        const resizeHdl = this._resizeHdlId.split(" ")[0];
+        if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
             const project = (p: number[], a: number[], b: number[]) => {
                 const atob = [b[0] - a[0], b[1] - a[1]];
                 const atop = [p[0] - a[0], p[1] - a[1]];
@@ -264,7 +267,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         this._snapY = thisPt.y;
         let dragBottom = false, dragRight = false, dragBotRight = false;
         let dX = 0, dY = 0, dW = 0, dH = 0;
-        switch (this._resizeHdlId) {
+        switch (this._resizeHdlId.split(" ")[0]) {
             case "": break;
             case "documentDecorations-topLeftResizer":
                 dX = -1;
@@ -437,11 +440,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                 </div>
             </Tooltip>);
 
+        const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
         const titleArea = this._edtingTitle ?
-            <input ref={this._keyinput} className="documentDecorations-title" style={{ width: `calc(100% - ${seldoc?.props.hideResizeHandles ? 0 : 20}px` }} type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
-                onBlur={e => this.titleBlur()} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> :
+            <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
+                style={{ width: `calc(100% - ${seldoc?.props.hideResizeHandles ? 0 : 20}px` }}
+                type="text" name="dynbox" autoComplete="on"
+                value={this._accumulatedTitle}
+                onBlur={e => this.titleBlur()}
+                onChange={action(e => this._accumulatedTitle = e.target.value)}
+                onKeyPress={this.titleEntered} /> :
             <div className="documentDecorations-title" style={{ width: `calc(100% - ${seldoc?.props.hideResizeHandles ? 0 : 20}px` }} key="title" onPointerDown={this.onTitleDown} >
-                <span className="documentDecorations-titleSpan">{`${this.selectionTitle}`}</span>
+                <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
             </div>;
 
         let inMainMenuPanel = false;
@@ -457,8 +466,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
 
         const useRotation = seldoc.rootDoc.type === DocumentType.INK;
+        const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : "";
 
-        return (<div className="documentDecorations" style={{ background: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : "" }} >
+        return (<div className={`documentDecorations${colorScheme}`}>
             <div className="documentDecorations-background" style={{
                 width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
                 height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
@@ -481,15 +491,15 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                         <>
                             {SelectionManager.Views().length !== 1 || hideTitle ? (null) :
                                 topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, undefined, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)}
-                            <div key="tl" className="documentDecorations-topLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="t" className="documentDecorations-topResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="tr" className="documentDecorations-topRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="l" className="documentDecorations-leftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="c" className="documentDecorations-centerCont"></div>
-                            <div key="r" className="documentDecorations-rightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="bl" className="documentDecorations-bottomLeftResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="b" className="documentDecorations-bottomResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="br" className="documentDecorations-bottomRightResizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="l" className={`documentDecorations-leftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="c" className={`documentDecorations-centerCont ${resizerScheme}`}></div>
+                            <div key="r" className={`documentDecorations-rightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="bl" className={`documentDecorations-bottomLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
 
                             {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
                                 topBtn("selector", "arrow-alt-circle-up", undefined, this.onSelectorClick, "tap to select containing document")}
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 4f871f5ec..7fa841002 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -41,7 +41,7 @@
 }
 
 .mainView-container,
-.mainView-container-dark {
+.mainView-container-Dark {
     width: 100%;
     height: 100%;
     position: absolute;
@@ -65,7 +65,7 @@
     }
 }
 
-.mainView-container-dark {
+.mainView-container-Dark {
     color: $light-gray;
 
     .lm_goldenlayout {
@@ -91,7 +91,7 @@
 
     .contextMenu-cont,
     .contextMenu-item {
-        background: $medium-gray;
+        background: $dark-gray;
     }
 
     .contextMenu-item:hover {
@@ -144,7 +144,7 @@
     }
 }
 
-.mainView-innerContent, .mainView-innerContent-dark {
+.mainView-innerContent, .mainView-innerContent-Dark {
     display: contents;
     flex-direction: row;
     position: relative;
@@ -175,7 +175,7 @@
 .mainView-libraryHandle {
     background-color: $light-gray;
 }
-.mainView-innerContent-dark 
+.mainView-innerContent-Dark 
 {
     .propertiesView {
         background-color: #252525;
@@ -198,7 +198,7 @@
         background: #353535;
     }
 }
-.mainView-container-dark {
+.mainView-container-Dark {
     .contextMenu-cont {
         background: $medium-gray;
         color: $white;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index d854f118f..c99ba447c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -26,7 +26,7 @@ import { HistoryUtil } from '../util/History';
 import { Hypothesis } from '../util/HypothesisUtils';
 import { Scripting } from '../util/Scripting';
 import { SelectionManager } from '../util/SelectionManager';
-import { SettingsManager } from '../util/SettingsManager';
+import { SettingsManager, ColorScheme } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { Transform } from '../util/Transform';
@@ -88,7 +88,7 @@ export class MainView extends React.Component {
     @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
     @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
     @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
+    @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
     @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
 
@@ -430,10 +430,10 @@ export class MainView extends React.Component {
         const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined;
         return <>
             {this.leftMenuPanel}
-            <div key="inner" className={`mainView-innerContent${this.darkScheme ? "-dark" : ""}`}>
+            <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
                 {this.flyout}
                 <div className="mainView-libraryHandle" style={{ display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
-                    <FontAwesomeIcon icon="chevron-left" color={this.darkScheme ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
+                    <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
                 </div>
                 <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)`, transform: transform }}>
                     <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
@@ -441,7 +441,7 @@ export class MainView extends React.Component {
                     {this.dockingContent}
 
                     <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._leftMenuFlyoutWidth ? 0 : this.propertiesWidth() - 1 }}>
-                        <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? Colors.WHITE : Colors.BLACK} size="sm" />
+                        <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
                     </div>
                     <div className="properties-container">
                         {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
@@ -459,7 +459,7 @@ export class MainView extends React.Component {
                     this._dashUIHeight = r.getBoundingClientRect().height;
                 })).observe(r);
             }} style={{
-                color: this.darkScheme ? "rgb(205,205,205)" : "black",
+                color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black",
                 height: `calc(100% - ${this.topOfDashUI}px)`,
                 width: "100%",
             }} >
@@ -601,7 +601,7 @@ export class MainView extends React.Component {
     }
 
     render() {
-        return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")}
+        return (<div className={`mainView-container${this.colorScheme}`}
             onScroll={() => ((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)}
             ref={r => {
                 r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r);
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 3c88a4830..1eb7a222e 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -22,6 +22,7 @@ import "./StyleProvider.scss";
 import React = require("react");
 import Color = require('color');
 import { lightOrDark } from '../../Utils';
+import { ColorScheme } from '../util/SettingsManager';
 
 export enum StyleLayers {
     Background = "background"
@@ -49,7 +50,7 @@ export enum StyleProp {
     FontSize = "fontSize",                // size of text font
 }
 
-function darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
+function darkScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark; }
 
 function toggleBackground(doc: Doc) {
     UndoManager.RunInBatch(() => runInAction(() => {
@@ -143,7 +144,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                                     doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
                                         StrCast((props?.renderDepth || 0) > 0 ?
                                             Doc.UserDoc().activeCollectionNestedBackground :
-                                            Doc.UserDoc().activeCollectionBackground)));
+                                            Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE))));
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 6a22acae8..3ea190a98 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -353,7 +353,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     @action setFocused = (doc: Doc) => this._focusedTable = doc;
 
     @action setPreviewDoc = (doc: Opt<Doc>) => {
-        SelectionManager.SelectSchemaView(this, doc);
+        SelectionManager.SelectSchemaViewDoc(doc);
         this._previewDoc = doc;
     }
 
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 97de097e0..a3da0e0e4 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -481,13 +481,16 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     @computed get validExpandViewTypes() {
-        if (this.doc.viewType === CollectionViewType.Docking) return [this.fieldKey];
+        if (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) {
+            return [this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : "layout"];
+        }
         const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : "";
         const links = () => DocListCast(this.doc.links).length ? "links" : "";
-        const data = () => this.childDocs && !this.props.treeView.dashboardMode ? this.fieldKey : "";
+        const data = () => this.childDocs ? this.fieldKey : "";
         const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases";
         const fields = () => Doc.UserDoc().noviceMode ? "" : "fields";
-        return [data(), "layout", ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
+        const layout = this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+        return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
     expandNextviewType = () => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 37444a9dc..b3c57d33a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -7,6 +7,7 @@ import { Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { aggregateBounds } from "../../../../Utils";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import React = require("react");
+import { ColorScheme } from "../../../util/SettingsManager";
 
 export interface ViewDefBounds {
     type: string;
@@ -361,7 +362,7 @@ export function computeTimelineLayout(
         groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
-    const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+    const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
     return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 94cf1c5a6..0b12f6c21 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -51,6 +51,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import Color = require("color");
+import { ColorScheme } from "../../../util/SettingsManager";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -1419,6 +1420,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const renderGridSpace = gridSpace * this.zoomScaling();
         const w = this.props.PanelWidth() + 2 * renderGridSpace;
         const h = this.props.PanelHeight() + 2 * renderGridSpace;
+        const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)";
         return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
             ref={(el) => {
                 const ctx = el?.getContext('2d');
@@ -1429,7 +1431,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
                     ctx.clearRect(0, 0, w, h);
                     if (ctx) {
-                        ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
+                        ctx.strokeStyle = strokeStyle;
                         ctx.beginPath();
                         for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
                             ctx.moveTo(x, Cy - h);
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index dfe99ffc8..12493ecc1 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -337,8 +337,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             {this.renderTypes(this._col)}
             {this.renderColors(this._col)}
             <div className="collectionSchema-headerMenu-group">
-                <button onClick={() => { this.deleteColumn(this._col.heading); }}
-                >Hide Column</button>
+                <button onClick={() => { this.deleteColumn(this._col.heading); }}>
+                    Hide Column
+                </button>
             </div>
         </div>;
     }
@@ -353,7 +354,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     @action setFocused = (doc: Doc) => this._focusedTable = doc;
 
     @action setPreviewDoc = (doc: Opt<Doc>) => {
-        SelectionManager.SelectSchemaView(this, doc);
+        SelectionManager.SelectSchemaViewDoc(doc);
         this._previewDoc = doc;
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3e15ed661..5d0b91b91 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -25,7 +25,6 @@ import { InteractionUtils } from '../../util/InteractionUtils';
 import { LinkManager } from '../../util/LinkManager';
 import { Scripting } from '../../util/Scripting';
 import { SelectionManager } from "../../util/SelectionManager";
-import { ColorScheme } from "../../util/SettingsManager";
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from "../../util/Transform";
@@ -50,6 +49,7 @@ import { ScriptingBox } from "./ScriptingBox";
 import { PresBox } from './trails/PresBox';
 import React = require("react");
 import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { ColorScheme } from "../../util/SettingsManager";
 const { Howl } = require('howler');
 
 interface Window {
@@ -1061,9 +1061,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     render() {
         TraceMobx();
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        const highlightColor = (Doc.UserDoc().colorScheme === ColorScheme.Dark ?
-            ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
-            ["transparent", "#4476F7", "#4476F7", "yellow", "magenta", "cyan", "orange"])[highlightIndex];
+        const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "yellow", "magenta", "cyan", "orange"][highlightIndex];
         const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex];
         const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
         let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index adcac0aef..520034c3c 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -14,7 +14,6 @@ import { WebField } from '../../../../fields/URLField';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { Scripting } from "../../../util/Scripting";
 import { SelectionManager } from '../../../util/SelectionManager';
-import { ColorScheme } from '../../../util/SettingsManager';
 import { UndoManager, undoBatch } from '../../../util/UndoManager';
 import { CollectionViewType } from '../../collections/CollectionView';
 import { ContextMenu } from '../../ContextMenu';
@@ -463,7 +462,6 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
     render() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const dark: boolean = Doc.UserDoc().colorScheme === ColorScheme.Dark;
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
             <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index e519de1c5..149836e93 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -12,6 +12,7 @@ import { FormattedTextBox } from "./FormattedTextBox";
 import React = require("react");
 import * as ReactDOM from 'react-dom';
 import { observer } from "mobx-react";
+import { ColorScheme } from "../../../util/SettingsManager";
 
 export class DashDocView {
     _fieldWrapper: HTMLSpanElement; // container for label and value
@@ -20,7 +21,7 @@ export class DashDocView {
         this._fieldWrapper = document.createElement("span");
         this._fieldWrapper.style.position = "relative";
         this._fieldWrapper.style.textIndent = "0";
-        this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
+        this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimGray" : "lightGray"));
         this._fieldWrapper.style.width = node.attrs.width;
         this._fieldWrapper.style.height = node.attrs.height;
         this._fieldWrapper.style.display = node.attrs.hidden ? "none" : "inline-block";
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 404e828ea..652804126 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -17,7 +17,7 @@ import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
 import { DocumentType } from "../client/documents/DocumentTypes";
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { Scripting } from '../client/util/Scripting';
-import { SettingsManager } from '../client/util/SettingsManager';
+import { SettingsManager, ColorScheme } from '../client/util/SettingsManager';
 import { Transform } from '../client/util/Transform';
 import { UndoManager } from "../client/util/UndoManager";
 import { TabDocView } from '../client/views/collections/TabDocView';
@@ -403,7 +403,7 @@ export class MobileInterface extends React.Component {
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
 
-        const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
+        const toggleTheme = ScriptField.MakeScript(`self.colorScheme = self.colorScheme ? undefined: ${ColorScheme.Dark}}`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
         const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`);
         dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneDashboard!]);
-- 
cgit v1.2.3-70-g09d2


From 12ae56962397a786397158af9e442a955883d16f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 22 Sep 2021 03:30:58 -0400
Subject: fixed runtime bug inside toggleBold().  removed print statements.

---
 src/client/documents/Documents.ts                  |   3 +-
 src/client/util/CurrentUserUtils.ts                |   3 +-
 src/client/views/MainView.tsx                      |   1 -
 .../collectionLinear/CollectionLinearView.tsx      |   1 -
 src/client/views/nodes/button/FontIconBox.tsx      | 126 +++++---------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  11 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 186 +++++++++++----------
 src/client/views/nodes/formattedText/marks_rts.ts  |   6 +-
 8 files changed, 153 insertions(+), 184 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 206f65bfd..8ac647b99 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -141,8 +141,8 @@ export class DocumentOptions {
     _columnWidth?: number;
     _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden
     _fontSize?: string;
-    _fontWeight?: number;
     _fontFamily?: string;
+    _fontWeight?: string;
     _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
     _curPage?: number; // current page of a PDF or other? paginated document
     _currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video)  value is in seconds
@@ -1188,7 +1188,6 @@ export namespace DocUtils {
                 description: ":" + StrCast(note.title),
                 event: undoBatch((args: { x: number, y: number }) => {
                     const textDoc = Docs.Create.TextDocument("", {
-                        _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize),
                         _width: 200, x, y, _autoHeight: note._autoHeight !== false,
                         title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1)
                     });
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 5683a8c21..297d4b241 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1535,8 +1535,7 @@ export class CurrentUserUtils {
     public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
         const tbox = Docs.Create.TextDocument("", {
             _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, backgroundColor: backgroundColor,
-            _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
-            _fontFamily: StrCast(Doc.UserDoc().fontFamily), title
+            _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, title
         });
         const template = Doc.UserDoc().defaultTextLayout;
         if (template instanceof Doc) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c99ba447c..fbd3fece2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -471,7 +471,6 @@ export class MainView extends React.Component {
         this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250);
         this._sidebarContent.proto = button.target as any;
         this.LastButton = button;
-        console.log(button.title);
     });
 
     closeFlyout = action(() => {
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 7fe95fef0..18a715edf 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -109,7 +109,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
     }
 
     myContextMenu = (e: React.MouseEvent) => {
-        console.log("STOPPING");
         e.stopPropagation();
         e.preventDefault();
     }
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 520034c3c..6a782b105 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -113,7 +113,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         // Script for checking the outcome of the toggle
         const checkScript: string = StrCast(this.rootDoc.script) + "(0, true)";
-        const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result;
+        const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result || 0;
 
 
         if (numBtnType === NumButtonType.Slider) {
@@ -158,7 +158,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 <div
                     className={`menuButton ${this.type} ${numBtnType}`}
                 >
-                    <div className={`button`} onClick={action((e) => setValue(checkResult - 1))}>
+                    <div className={`button`} onClick={action((e) => setValue(Number(checkResult) - 1))}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"minus"} />
                     </div>
                     <div
@@ -177,7 +177,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                             onChange={action((e) => setValue(Number(e.target.value)))}
                         />
                     </div>
-                    <div className={`button`} onClick={action((e) => setValue(checkResult + 1))}>
+                    <div className={`button`} onClick={action((e) => setValue(Number(checkResult) + 1))}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"plus"} />
                     </div>
 
@@ -261,8 +261,8 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             }
             noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
         } else if (script === 'setFont') {
-            const selected = SelectionManager.Docs().lastElement();
-            text = StrCast((selected?.type === DocumentType.RTF ? selected : Doc.UserDoc())._fontFamily);
+            const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+            text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
             noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
                 "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
         }
@@ -337,7 +337,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         const colorBox = (func: (color: ColorState) => void) => <SketchPicker
             disableAlpha={false}
             onChange={func}
-            color={boolResult ? boolResult : "#FFFFFF"}
+            color={boolResult ?? "transparent"}
             presetColors={colorOptions} />;
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
             <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
@@ -360,7 +360,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}
                 onPointerDown={e => e.stopPropagation()}>
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                <div className="colorButton-color" style={{ backgroundColor: boolResult ? boolResult : "#FFFFFF" }} />
+                <div className="colorButton-color" style={{ backgroundColor: boolResult ?? "transparent" }} />
                 {label}
                 {/* {dropdownCaret} */}
                 {this.rootDoc.dropDownOpen ?
@@ -387,7 +387,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
         // Button label
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
@@ -406,7 +406,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         } else {
             return (
                 <div className={`menuButton ${this.type}`}
-                    style={{ opacity: 1, backgroundColor: backgroundColor, color: color }}>
+                    style={{ opacity: 1, backgroundColor, color }}>
                     <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                     {label}
                 </div>
@@ -571,11 +571,7 @@ Scripting.addGlobal(function setView(view: string) {
 Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     if (checkResult) {
-        if (selected) {
-            return selected._backgroundColor;
-        } else {
-            return "#FFFFFF";
-        }
+        return selected?._backgroundColor ?? "transparent";
     }
     if (selected) selected._backgroundColor = color;
     Doc.UserDoc()._fontColor = color;
@@ -596,7 +592,7 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     if (checkResult && selected) {
         if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
-        else return "transparent";
+        return "transparent";
     }
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
 });
@@ -617,16 +613,18 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
 Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
     SelectionManager.Docs().map(doc => doc._fontFamily = font);
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    editorView?.state && RichTextMenu.Instance.setFontFamily(font, editorView);
-    Doc.UserDoc()._fontFamily = font;
-    return Doc.UserDoc()._fontFamily;
+    if (checkResult) {
+        return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+    }
+    if (editorView) RichTextMenu.Instance.setFontFamily(font);
+    else Doc.UserDoc().fontFamily = font;
 });
 
 Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
     const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
     switch (info) {
-        case "family": return style?.activeColors[0];
+        case "family": return style?.activeFamilies[0];
         case "size": return style?.activeSizes[0];
         case "color": return style?.activeColors[0];
         case "highlight": return style?.activeHighlights[0];
@@ -643,7 +641,7 @@ Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", ch
             active = StrCast(Doc.UserDoc().textAlign);
         }
         if (active === align) return Colors.MEDIUM_BLUE;
-        else return "transparent";
+        return "transparent";
     }
     SelectionManager.Docs().map(doc => doc.textAlign = align);
     switch (align) {
@@ -667,38 +665,25 @@ Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", check
     if (checkResult) {
         const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
         if (active === mapStyle) return Colors.MEDIUM_BLUE;
-        else return "transparent";
+        return "transparent";
     }
     if (editorView) {
         const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
-        if (active === mapStyle) {
-            editorView?.state && RichTextMenu.Instance.changeListType(editorView.state.schema.nodes.ordered_list.create({ mapStyle: "" }));
-        } else {
-            editorView?.state && RichTextMenu.Instance.changeListType(editorView.state.schema.nodes.ordered_list.create({ mapStyle: mapStyle }));
-        }
+        editorView?.state && RichTextMenu.Instance.changeListType(
+            editorView.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle }));
     }
 });
 
 // toggle: Set overlay status of selected document
 Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
 
     if (checkResult) {
-        if (selected) {
-            return selected._fontColor;
-        } else {
-            return Doc.UserDoc()._fontColor;
-        }
+        return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
     }
 
-    if (selected) {
-        selected._fontColor = color;
-        if (color) {
-            editorView?.state && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
-        }
-    }
-    Doc.UserDoc()._fontColor = color;
+    if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
+    else Doc.UserDoc()._fontColor = color;
 });
 
 // toggle: Set overlay status of selected document
@@ -707,11 +692,7 @@ Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: bool
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
 
     if (checkResult) {
-        if (selected) {
-            return selected._fontHighlight;
-        } else {
-            return Doc.UserDoc()._fontHighlight;
-        }
+        return (selected ?? Doc.UserDoc())._fontHighlight;
     }
     if (selected) {
         selected._fontColor = color;
@@ -722,62 +703,43 @@ Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: bool
     Doc.UserDoc()._fontHighlight = color;
 });
 
-
-
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFontSize(size: string, checkResult?: boolean) {
+Scripting.addGlobal(function setFontSize(size: string | number, checkResult?: boolean) {
+    if (typeof size === "number") size = size.toString();
+    if (size && Number(size).toString() === size) size += "px";
+    const editorView = RichTextMenu.Instance.TextView?.EditorView;
     if (checkResult) {
-        const size: number = parseInt(StrCast(Doc.UserDoc()._fontSize), 10);
-        return size;
+        return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", "");
     }
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    editorView?.state && RichTextMenu.Instance.setFontSize(Number(size), editorView);
-    Doc.UserDoc()._fontSize = size + "px";
+    if (editorView) RichTextMenu.Instance.setFontSize(size);
+    else Doc.UserDoc()._fontSize = size;
 });
 
 Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        const editorView = RichTextMenu.Instance.TextView?.EditorView;
-        if (editorView) {
-            const bold: boolean = editorView?.state && RichTextMenu.Instance.getBoldStatus();
-            if (bold) return Colors.MEDIUM_BLUE;
-            else return "transparent";
-        }
-        else return "transparent";
+        return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent";
     }
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    if (editorView) {
-        editorView?.state && RichTextMenu.Instance.toggleBold(editorView, true);
-    }
-    SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.bold = !doc.bold);
-    Doc.UserDoc().bold = !Doc.UserDoc().bold;
-    return Doc.UserDoc().bold;
+    if (editorView) RichTextMenu.Instance?.toggleBold();
+    else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold";
 });
 
 Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        return "transparent";
-    }
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    if (editorView) {
-        editorView?.state && RichTextMenu.Instance.toggleUnderline(editorView, true);
+        return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent";
     }
-    SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.underline = !doc.underline);
-    Doc.UserDoc().underline = !Doc.UserDoc().underline;
-    return Doc.UserDoc().underline;
+    if (editorView) RichTextMenu.Instance?.toggleUnderline();
+    else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline";
 });
 
 Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        return "transparent";
-    }
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    if (editorView) {
-        editorView?.state && RichTextMenu.Instance.toggleItalic(editorView, true);
+        return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent";
     }
-    SelectionManager.Docs().filter(doc => StrCast(doc.type) === DocumentType.RTF).map(doc => doc.italic = !doc.italic);
-    Doc.UserDoc().italic = !Doc.UserDoc().italic;
-    return Doc.UserDoc().italic;
+    if (editorView) RichTextMenu.Instance?.toggleItalics();
+    else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics";
 });
 
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 617fa0bee..16aa4de6c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1168,7 +1168,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         selectOnLoad && this._editorView!.focus();
         // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
         if (this._editorView && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) {
-            this._editorView.state.storedMarks = [...(this._editorView.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })];
+            this._editorView.state.storedMarks = [...(this._editorView.state.storedMarks ?? []),
+            schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }),
+            ...(Doc.UserDoc().fontColor !== "transparent" && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
+            ...(Doc.UserDoc().fontStyle === "italics" ? [schema.mark(schema.marks.em)] : []),
+            ...(Doc.UserDoc().textDecoration === "underline" ? [schema.mark(schema.marks.underline)] : []),
+            ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: StrCast(Doc.UserDoc().fontFamily) })] : []),
+            ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: StrCast(Doc.UserDoc().fontSize, "") })] : []),
+            ...(Doc.UserDoc().fontWeight === "bold" ? [schema.mark(schema.marks.strong)] : [])];
         }
     }
 
@@ -1596,7 +1603,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         background: this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
                         color: this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
                         fontSize: this.props.fontSize ? this.props.fontSize : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
-                        fontWeight: Cast(this.layoutDoc._fontWeight, "number", null),
+                        fontWeight: Cast(this.layoutDoc._fontWeight, "string", null) as any,
                         fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
                         pointerEvents: interactive ? undefined : "none",
                     }}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9904a7939..23a3d80a4 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,8 +1,7 @@
 import React = require("react");
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
 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, runInAction, computed } from "mobx";
 import { observer } from "mobx-react";
 import { lift, wrapIn } from "prosemirror-commands";
 import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model";
@@ -10,10 +9,7 @@ import { wrapInList } from "prosemirror-schema-list";
 import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { Doc } from "../../../../fields/Doc";
-import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
 import { Cast, StrCast } from "../../../../fields/Types";
-import { TraceMobx } from "../../../../fields/util";
-import { unimplementedFunction, Utils } from "../../../../Utils";
 import { DocServer } from "../../../DocServer";
 import { LinkManager } from "../../../util/LinkManager";
 import { SelectionManager } from "../../../util/SelectionManager";
@@ -29,7 +25,7 @@ const { toggleMark } = require("prosemirror-commands");
 
 @observer
 export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
-    static Instance: RichTextMenu;
+    @observable static Instance: RichTextMenu;
     public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
     private _linkToRef = React.createRef<HTMLInputElement>();
 
@@ -39,22 +35,22 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     public _brushMap: Map<string, Set<Mark>> = new Map();
 
     @observable private collapsed: boolean = false;
-    @observable private boldActive: boolean = false;
-    @observable private italicsActive: boolean = false;
-    @observable private underlineActive: boolean = false;
-    @observable private strikethroughActive: boolean = false;
-    @observable private subscriptActive: boolean = false;
-    @observable private superscriptActive: boolean = false;
-
-    @observable private activeFontSize: string = "";
-    @observable private activeFontFamily: string = "";
+    @observable private _boldActive: boolean = false;
+    @observable private _italicsActive: boolean = false;
+    @observable private _underlineActive: boolean = false;
+    @observable private _strikethroughActive: boolean = false;
+    @observable private _subscriptActive: boolean = false;
+    @observable private _superscriptActive: boolean = false;
+
+    @observable private _activeFontSize: string = "13px";
+    @observable private _activeFontFamily: string = "";
     @observable private activeListType: string = "";
     @observable private activeAlignment: string = "left";
 
     @observable private brushMarks: Set<Mark> = new Set();
     @observable private showBrushDropdown: boolean = false;
 
-    @observable private activeFontColor: string = "black";
+    @observable private _activeFontColor: string = "black";
     @observable private showColorDropdown: boolean = false;
 
     @observable private activeHighlightColor: string = "transparent";
@@ -67,10 +63,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     _delayHide = false;
     constructor(props: Readonly<{}>) {
         super(props);
-        RichTextMenu.Instance = this;
-        this._canFade = false;
-        //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
-        runInAction(() => this.Pinned = true);
+        runInAction(() => {
+            RichTextMenu.Instance = this;
+            this._canFade = false;
+            //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
+            this.Pinned = true;
+        });
     }
 
     componentDidMount() {
@@ -81,6 +79,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         this._reaction?.();
     }
 
+    @computed get bold() { return this._boldActive; }
+    @computed get underline() { return this._underlineActive; }
+    @computed get italics() { return this._italicsActive; }
+    @computed get strikeThrough() { return this._strikethroughActive; }
+    @computed get fontColor() { return this._activeFontColor; }
+    @computed get fontFamily() { return this._activeFontFamily; }
+    @computed get fontSize() { return this._activeFontSize; }
+
     public delayHide = () => this._delayHide = true;
 
     @action
@@ -110,10 +116,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
         this.activeListType = this.getActiveListStyle();
         this.activeAlignment = this.getActiveAlignment();
-        this.activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
-        this.activeFontSize = !activeSizes.length ? "13px" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
-        this.activeFontColor = !activeColors.length ? "black" : activeColors.length === 1 ? String(activeColors[0]) : "...";
-        this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "...";
+        this._activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
+        this._activeFontSize = !activeSizes.length ? "13px" : activeSizes[0];
+        this._activeFontColor = !activeColors.length ? "black" : activeColors.length > 0 ? String(activeColors[0]) : "...";
+        this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length > 0 ? String(activeHighlights[0]) : "...";
 
         // update link in current selection
         this.getTextLinkTargetTitle().then(targetTitle => this.setCurrentLink(targetTitle));
@@ -125,7 +131,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
             if (node?.type === schema.nodes.ordered_list) {
                 let attrs = node.attrs;
                 if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, fontFamily: mark.attrs.family };
-                if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: `${mark.attrs.fontSize}px` };
+                if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: mark.attrs.fontSize };
                 if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color };
                 const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
                 dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
@@ -142,17 +148,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         }
     }
 
-    getBoldStatus() {
-        if (this.view && this.TextView.props.isSelected(true)) {
-            const path = (this.view.state.selection.$from as any).path;
-            for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) {
-                if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) {
-                    return path[i].attrs.strong;
-                }
-            }
-        }
-    }
-
     // finds font sizes and families in selection
     getActiveAlignment() {
         if (this.view && this.TextView.props.isSelected(true)) {
@@ -193,25 +188,23 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         if (this.TextView.props.isSelected(true)) {
             const state = this.view.state;
             const pos = this.view.state.selection.$from;
-            const ref_node = this.reference_node(pos);
-            if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
-                const marks = Array.from(ref_node.marks);
-                marks.push(...(this.view.state.storedMarks as any));
-                marks.forEach(m => {
-                    m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
-                    m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
-                    m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "px");
-                    m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight));
+            const marks: Mark<any>[] = [];
+            if (state.selection.empty) {
+                const ref_node = this.reference_node(pos);
+                marks.push(...[...(ref_node !== this.view.state.doc && ref_node?.isText ?
+                    [...(state.storedMarks ?? []), ...Array.from(ref_node.marks)] : [])]);
+            } else {
+                state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
+                    node.marks?.filter(mark => !mark.isInSet(marks)).map(mark => marks.push(mark));
                 });
             }
-            !activeFamilies.length && (activeFamilies.push(StrCast(this.TextView.layoutDoc._fontFamily, StrCast(Doc.UserDoc().fontFamily))));
-            !activeSizes.length && (activeSizes.push(StrCast(this.TextView.layoutDoc._fontSize, StrCast(Doc.UserDoc().fontSize))));
-            !activeColors.length && (activeColors.push(StrCast(this.TextView.layoutDoc.color, StrCast(Doc.UserDoc().fontColor))));
+            marks.forEach(m => {
+                m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
+                m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
+                m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize);
+                m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight));
+            });
         }
-        !activeFamilies.length && (activeFamilies.push(StrCast(Doc.UserDoc().fontFamily)));
-        !activeSizes.length && (activeSizes.push(StrCast(Doc.UserDoc().fontSize)));
-        !activeColors.length && (activeColors.push(StrCast(Doc.UserDoc().fontColor, "black")));
-        !activeHighlights.length && (activeHighlights.push(StrCast(Doc.UserDoc().fontHighlight, "")));
         return { activeFamilies, activeSizes, activeColors, activeHighlights };
     }
 
@@ -251,11 +244,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
                     return [];
                 }
                 activeMarks = markGroup.filter(mark_type => {
-                    if (mark_type === state.schema.marks.pFontSize) {
-                        return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
-                    }
+                    // if (mark_type === state.schema.marks.pFontSize) {
+                    //     return mark.isINSet
+                    //     ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
+                    // }
                     const mark = state.schema.mark(mark_type);
-                    return ref_node.marks.includes(mark);
+                    return mark.isInSet(ref_node.marks);
                 });
             }
         }
@@ -270,56 +264,66 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     setActiveMarkButtons(activeMarks: MarkType[] | undefined) {
         if (!activeMarks) return;
 
-        this.boldActive = false;
-        this.italicsActive = false;
-        this.underlineActive = false;
-        this.strikethroughActive = false;
-        this.subscriptActive = false;
-        this.superscriptActive = false;
+        this._boldActive = false;
+        this._italicsActive = false;
+        this._underlineActive = false;
+        this._strikethroughActive = false;
+        this._subscriptActive = false;
+        this._superscriptActive = false;
 
         activeMarks.forEach(mark => {
             switch (mark.name) {
-                case "strong": this.boldActive = true; break;
-                case "em": this.italicsActive = true; break;
-                case "underline": this.underlineActive = true; break;
-                case "strikethrough": this.strikethroughActive = true; break;
-                case "subscript": this.subscriptActive = true; break;
-                case "superscript": this.superscriptActive = true; break;
+                case "strong": this._boldActive = true; break;
+                case "em": this._italicsActive = true; break;
+                case "underline": this._underlineActive = true; break;
+                case "strikethrough": this._strikethroughActive = true; break;
+                case "subscript": this._subscriptActive = true; break;
+                case "superscript": this._superscriptActive = true; break;
             }
         });
     }
 
-    toggleBold = (view: EditorView, forceBool?: boolean) => {
-        const mark = view.state.schema.mark(view.state.schema.marks.strong, { strong: forceBool });
-        this.setMark(mark, view.state, view.dispatch, false);
-        view.focus();
+    toggleBold = () => {
+        if (this.view) {
+            const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
+            this.setMark(mark, this.view.state, this.view.dispatch, false);
+            this.view.focus();
+        }
     }
 
-    toggleUnderline = (view: EditorView, forceBool?: boolean) => {
-        const mark = view.state.schema.mark(view.state.schema.marks.underline, { underline: forceBool });
-        this.setMark(mark, view.state, view.dispatch, false);
-        view.focus();
+    toggleUnderline = () => {
+        if (this.view) {
+            const mark = this.view.state.schema.mark(this.view.state.schema.marks.underline);
+            this.setMark(mark, this.view.state, this.view.dispatch, false);
+            this.view.focus();
+        }
     }
 
-    toggleItalic = (view: EditorView, forceBool?: boolean) => {
-        const mark = view.state.schema.mark(view.state.schema.marks.em, { em: forceBool });
-        this.setMark(mark, view.state, view.dispatch, false);
-        view.focus();
+    toggleItalics = () => {
+        if (this.view) {
+            const mark = this.view.state.schema.mark(this.view.state.schema.marks.em);
+            this.setMark(mark, this.view.state, this.view.dispatch, false);
+            this.view.focus();
+        }
     }
 
 
-    setFontSize = (size: number, view: EditorView) => {
-        const fmark = view.state.schema.marks.pFontSize.create({ fontSize: size });
-        this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
-        view.focus();
-        this.updateMenu(view, undefined, this.props);
+    setFontSize = (fontSize: string) => {
+        if (this.view) {
+            const fmark = this.view.state.schema.marks.pFontSize.create({ fontSize });
+            this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
+            this.view.focus();
+            this.updateMenu(this.view, undefined, this.props);
+        }
     }
 
-    setFontFamily = (family: string, view: EditorView) => {
-        const fmark = view.state.schema.marks.pFontFamily.create({ family: family });
-        this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
-        view.focus();
-        this.updateMenu(view, undefined, this.props);
+    setFontFamily = (family: string) => {
+        if (this.view) {
+            const fmark = this.view.state.schema.marks.pFontFamily.create({ family: family });
+            this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
+            this.view.focus();
+            this.updateMenu(this.view, undefined, this.props);
+        }
     }
 
     setHighlight(color: String, view: EditorView, dispatch: any) {
@@ -330,7 +334,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     }
 
     setColor(color: String, view: EditorView, dispatch: any) {
-        const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color });
+        const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color });
         if (view.state.selection.empty) {
             dispatch(view.state.tr.addStoredMark(colorMark));
             return false;
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 655ee7e44..6103a28d6 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -61,13 +61,13 @@ export const marks: { [index: string]: MarkSpec } = {
 
     /** FONT SIZES */
     pFontSize: {
-        attrs: { fontSize: { default: 10 } },
+        attrs: { fontSize: { default: "10px" } },
         parseDOM: [{
             tag: "span", getAttrs(dom: any) {
-                return { fontSize: dom.style.fontSize ? Number(dom.style.fontSize.replace("px", "")) : "" };
+                return { fontSize: dom.style.fontSize ? dom.style.fontSize.toString() : "" };
             }
         }],
-        toDOM: (node) => node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize}px;` }] : ['span', 0]
+        toDOM: (node) => node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize};` }] : ['span', 0]
     },
 
     /* FONTS */
-- 
cgit v1.2.3-70-g09d2


From 13ec8c89d7fb809a9ceefa3c25e332b0f4457593 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 22 Sep 2021 11:28:46 -0400
Subject: fixed dropdowns to be over documentdecorations by moving top menu bar
 out of maincontent area and giving it a higher z-index than DocDecorations. 
 fixed toggle behavior of shape drawing modes.  made color dropdown state be
 runtime only (doesn't write to data model) -- need to decide if that's what
 we want.

---
 src/client/views/MainView.tsx                 | 14 +++---
 src/client/views/nodes/button/FontIconBox.tsx | 68 +++++++++++++--------------
 2 files changed, 42 insertions(+), 40 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index fbd3fece2..35c5801e5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -12,7 +12,7 @@ import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, PromiseValue, StrCast } from '../../fields/Types';
+import { PromiseValue, StrCast } from '../../fields/Types';
 import { TraceMobx } from '../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
@@ -26,7 +26,7 @@ import { HistoryUtil } from '../util/History';
 import { Hypothesis } from '../util/HypothesisUtils';
 import { Scripting } from '../util/Scripting';
 import { SelectionManager } from '../util/SelectionManager';
-import { SettingsManager, ColorScheme } from '../util/SettingsManager';
+import { ColorScheme, SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { Transform } from '../util/Transform';
@@ -101,7 +101,7 @@ export class MainView extends React.Component {
     propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
     propertiesHeight = () => this._dashUIHeight;
     mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
-    mainDocViewHeight = () => this._dashUIHeight - this.topMenuHeight();
+    mainDocViewHeight = () => this._dashUIHeight;
 
     componentDidMount() {
         document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
@@ -436,7 +436,6 @@ export class MainView extends React.Component {
                     <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
                 </div>
                 <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)`, transform: transform }}>
-                    <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
 
                     {this.dockingContent}
 
@@ -460,7 +459,7 @@ export class MainView extends React.Component {
                 })).observe(r);
             }} style={{
                 color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black",
-                height: `calc(100% - ${this.topOfDashUI}px)`,
+                height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`,
                 width: "100%",
             }} >
                 {this.mainInnerContent}
@@ -613,11 +612,14 @@ export class MainView extends React.Component {
             <GroupManager />
             <GoogleAuthenticationManager />
             <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
-            <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
+            <ComponentDecorations boundsLeft={0} boundsTop={this.topOfMainDocContent} />
             {this.topbar}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
+            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} >
+                <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+            </div>
             <GestureOverlay >
                 {this.mainDashboardArea}
             </GestureOverlay>
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 368699dea..511df8786 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,7 +5,7 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
-import { Doc, StrListCast } from '../../../../fields/Doc';
+import { Doc, StrListCast, WidthSym, HeightSym } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
 import { createSchema, makeInterface } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
@@ -318,35 +318,35 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         );
     }
 
+    @observable colorPickerClosed: boolean = true;
     @computed get colorScript() {
         const script = StrCast(this.rootDoc.script);
         return ScriptField.MakeScript(script + '(colValue, checkResult)', { colValue: "string", checkResult: "boolean" });
     }
-    colorPicker = (boolResult: string) => {
-        const click = (value: ColorState) => {
+
+    colorPicker = (curColor: string) => {
+        const change = (value: ColorState) => {
             const s = this.colorScript;
             s && undoBatch(() => s.script.run({ colValue: Utils.colorString(value), checkResult: false }).result)();
         };
-        const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
+        const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
             '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
             '#FFFFFF', '#f1efeb', "transparent"];
         return <SketchPicker
-            disableAlpha={false}
-            onChange={click}
-            color={boolResult}
-            presetColors={colorOptions} />;
+            onChange={change}
+            color={curColor}
+            presetColors={presets} />
     }
     /**
      * Color button
      */
     @computed get colorButton() {
-        const active = 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 boolResult = this.colorScript?.script.run({ colValue: undefined, checkResult: true }).result;
+        const curColor = this.colorScript?.script.run({ colValue: undefined, checkResult: true }).result ?? "transparent";
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
@@ -355,26 +355,27 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
             <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
         </div>;
-
+        setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
         return (
-            <div className={`menuButton ${this.type} ${active}`}
+            <div className={`menuButton ${this.type} ${this.colorPickerClosed}`}
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
-                onClick={() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}
+                onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)}
                 onPointerDown={e => e.stopPropagation()}>
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                <div className="colorButton-color" style={{ backgroundColor: boolResult ?? "transparent" }} />
+                <div className="colorButton-color" style={{ backgroundColor: curColor }} />
                 {label}
                 {/* {dropdownCaret} */}
-                {this.rootDoc.dropDownOpen ?
+                {this.colorPickerClosed ? (null) :
                     <div>
                         <div className="menuButton-dropdownBox"
                             onPointerDown={e => e.stopPropagation()}
                             onClick={e => e.stopPropagation()}>
-                            {this.colorPicker(boolResult ?? "transparent")}
+                            {this.colorPicker(curColor)}
                         </div>
-                        <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
-                    </div>
-                    : null}
+                        <div className="dropbox-background" onClick={action((e) => {
+                            e.stopPropagation(); this.colorPickerClosed = true;
+                        })} />
+                    </div>}
             </div>
         );
     }
@@ -754,24 +755,23 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
 
 Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) {
     if (checkResult) {
-        if (Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool) return Colors.MEDIUM_BLUE;
-        else return "transparent";
+        return (Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance?.InkShape === "" || GestureOverlay.Instance?.InkShape === tool) ?
+            Colors.MEDIUM_BLUE : "transparent";
     }
-    if (tool === "circle") {
-        Doc.UserDoc().activeInkTool = "pen";
-        GestureOverlay.Instance.InkShape = tool;
-    } else if (tool === "square") {
-        Doc.UserDoc().activeInkTool = "pen";
-        GestureOverlay.Instance.InkShape = tool;
-    } else if (tool === "line") {
-        Doc.UserDoc().activeInkTool = "pen";
-        GestureOverlay.Instance.InkShape = tool;
-    } else if (tool) {
-        if (Doc.UserDoc().activeInkTool === tool && GestureOverlay.Instance.InkShape === "" || GestureOverlay.Instance.InkShape === tool) {
-            GestureOverlay.Instance.InkShape = "";
+    if (["circle", "square", "line"].includes(tool)) {
+        if (GestureOverlay.Instance.InkShape === tool) {
+            Doc.UserDoc().activeInkTool = InkTool.None;
+            GestureOverlay.Instance.InkShape = InkTool.None;
+        } else {
+            Doc.UserDoc().activeInkTool = InkTool.Pen;
+            GestureOverlay.Instance.InkShape = tool;
+        }
+    } else if (tool) { // pen
+        if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
             Doc.UserDoc().activeInkTool = InkTool.None;
         } else {
             Doc.UserDoc().activeInkTool = tool;
+            GestureOverlay.Instance.InkShape = "";
         }
     } else {
         Doc.UserDoc().activeInkTool = InkTool.None;
-- 
cgit v1.2.3-70-g09d2


From e7e67ff7e3170b6eb5bf05ffe284ca08f4802459 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 25 Sep 2021 12:30:35 -0400
Subject: added a hacky fix to what seems to be a Chrome bug when auto
 expanding the left flyout panel

---
 src/client/views/MainView.tsx                                   | 6 ++++++
 src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 3 +--
 2 files changed, 7 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 35c5801e5..7edcd6217 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -467,7 +467,13 @@ export class MainView extends React.Component {
     }
 
     expandFlyout = action((button: Doc) => {
+        // bcz: What's going on here!?  
+        // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab.  All of the div below the CollectionFreeFormView's main div
+        // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be.
+        // interactively adjusting the flyout fixes the problem.  So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);)
         this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250);
+        setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0);
+
         this._sidebarContent.proto = button.target as any;
         this.LastButton = button;
     });
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 81f6307d1..24a7d77e0 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -646,8 +646,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     render() {
         return <div className="marqueeView"
             style={{
-                overflow: //(!this.props.ContainingCollectionView && this.props.isAnnotationOverlay) ? "visible" :
-                    StrCast(this.props.Document._overflow),
+                overflow: StrCast(this.props.Document._overflow),
                 cursor: "hand"
             }}
             onDragOver={e => e.preventDefault()}
-- 
cgit v1.2.3-70-g09d2


From dab30019adba202ba2266acf046ad3c41f90c6e2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 27 Sep 2021 14:06:51 -0400
Subject: videoBox fitsWidth properly in lightbox.  documentdecorations less
 hacky for deciding when to allow nativewidth/height changes by adding props
 to document type templates.  fixed height of properties pane & updated CSS
 for leftflyout & properties pane.  marquees no longer oveflow image/video
 boxes.

---
 src/client/documents/Documents.ts        | 12 +++++++++---
 src/client/views/DocumentDecorations.tsx | 29 +++++++++++------------------
 src/client/views/GlobalKeyHandler.ts     |  2 +-
 src/client/views/MainView.scss           |  9 ++++-----
 src/client/views/MainView.tsx            | 12 ++++++------
 src/client/views/nodes/ImageBox.scss     |  1 +
 src/client/views/nodes/ImageBox.tsx      |  4 ++--
 src/client/views/nodes/VideoBox.scss     | 15 ++++++++++++++-
 src/client/views/nodes/VideoBox.tsx      | 22 +++++++++++++++-------
 src/client/views/nodes/WebBox.tsx        |  8 ++++----
 10 files changed, 67 insertions(+), 47 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8ac647b99..403620bdd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -352,7 +352,7 @@ export namespace Docs {
         const TemplateMap: TemplateMap = new Map([
             [DocumentType.RTF, {
                 layout: { view: FormattedTextBox, dataField: "text" },
-                options: { _height: 150, _xMargin: 10, _yMargin: 10, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
             }],
             [DocumentType.SEARCH, {
                 layout: { view: SearchBox, dataField: defaultDataKey },
@@ -372,7 +372,7 @@ export namespace Docs {
             }],
             [DocumentType.WEB, {
                 layout: { view: WebBox, dataField: defaultDataKey },
-                options: { _height: 300, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
             }],
             [DocumentType.COL, {
                 layout: { view: CollectionView, dataField: defaultDataKey },
@@ -392,7 +392,7 @@ export namespace Docs {
             }],
             [DocumentType.PDF, {
                 layout: { view: PDFBox, dataField: defaultDataKey },
-                options: { _curPage: 1, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
             }],
             [DocumentType.IMPORT, {
                 layout: { view: DirectoryImportBox, dataField: defaultDataKey },
@@ -506,6 +506,12 @@ export namespace Docs {
             const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
             // fetch the actual prototype documents from the server
             const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds);
+            Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeHeightUnfrozen = true; // to avoid having to recreate the DB
+            Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeHeightUnfrozen = true;
+            Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeHeightUnfrozen = true;
+            Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeDimModifiable = true; // to avoid having to recreate the DB
+            Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeDimModifiable = true;
+            Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeDimModifiable = true;
 
             // update this object to include any default values: DocumentOptions for all prototypes
             prototypeIds.map(id => {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e9a54d6a5..bd61c9f60 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -319,12 +319,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                 let height = (doc._height || (nheight / nwidth * width));
                 height = !height || isNaN(height) ? 20 : height;
                 const scale = docView.props.ScreenToLocalTransform().Scale;
-                const canModifyNativeDim = e.ctrlKey || doc.allowReflow;
+                const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable;
                 if (nwidth && nheight) {
                     if (nwidth / nheight !== width / height && !dragBottom) {
                         height = nheight / nwidth * width;
                     }
-                    if (canModifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
+                    if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
                         if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;
                         else dW = dH * nwidth / nheight;
                     }
@@ -334,34 +334,27 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                 doc.x = (doc.x || 0) + dX * (actualdW - width);
                 doc.y = (doc.y || 0) + dY * (actualdH - height);
                 const fixedAspect = (nwidth && nheight);
-                if (canModifyNativeDim && [DocumentType.IMG, DocumentType.SCREENSHOT, DocumentType.VID].includes(doc.type as DocumentType)) {
-                    dW !== 0 && runInAction(() => {
-                        const dataDoc = doc[DataSym];
-                        const nw = Doc.NativeWidth(dataDoc);
-                        const nh = Doc.NativeHeight(dataDoc);
-                        Doc.SetNativeWidth(dataDoc, nw + (dW > 0 ? 10 : -10));
-                        Doc.SetNativeHeight(dataDoc, nh + (dW > 0 ? 10 : -10) * nh / nw);
-                    });
-                }
-                else if (fixedAspect) {
-                    if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !canModifyNativeDim)) || dragRight) {
-                        if (dragRight && canModifyNativeDim) {
+                if (fixedAspect) {
+                    if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) {
+                        if (dragRight && modifyNativeDim) {
                             doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc);
                         } else {
                             if (!doc._fitWidth) doc._height = nheight / nwidth * actualdW;
-                            else if (!canModifyNativeDim || dragBotRight) doc._height = actualdH;
+                            else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
                         }
                         doc._width = actualdW;
                     }
                     else {
-                        if (dragBottom && (canModifyNativeDim || docView.layoutDoc._fitWidth)) { // frozen web pages and others that fitWidth can't grow horizontally to match a vertical resize so the only choice is to change the nativeheight even if the ctrl key isn't used
+                        if (dragBottom && (modifyNativeDim ||
+                            (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
                             doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
                             doc._autoHeight = false;
                         } else {
                             if (!doc._fitWidth) doc._width = nwidth / nheight * actualdH;
-                            else if (!canModifyNativeDim || dragBotRight) doc._width = actualdW;
+                            else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
                         }
-                        doc._height = actualdH;
+                        if (!modifyNativeDim) doc._height = Math.min(nheight / nwidth * NumCast(doc._width), actualdH);
+                        else doc._height = actualdH;
                     }
                 } else {
                     dH && (doc._height = actualdH);
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 574d28b8f..364bf05e2 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -141,7 +141,7 @@ export class KeyManager {
             case "delete":
             case "backspace":
                 if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") {
-                    const selected = SelectionManager.Views().filter(dv => !dv.topMost);;
+                    const selected = SelectionManager.Views().filter(dv => !dv.topMost);
                     UndoManager.RunInBatch(() => {
                         SelectionManager.DeselectAll();
                         selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document));
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 7fa841002..c3cdb7dde 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -109,9 +109,9 @@
 
 .properties-container {
     height: 100%;
-    position: relative;
-    left: 100%;
-    top: calc(-100% - 36px);
+    position: absolute;
+    right: 0;
+    top: 0;
     z-index: 3000;
 }
 
@@ -298,9 +298,8 @@
     width: var(--flyoutHandleWidth);
     height: 55px;
     top: 50%;
-    left: -10px;
     border-radius: 8px;
-    position: relative;
+    position: absolute;
     z-index: 41; // lm_maximised has a z-index of 40 and this needs to be above that
     touch-action: none;
     cursor: grab;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7edcd6217..3f7df705f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -426,23 +426,23 @@ export class MainView extends React.Component {
     }
 
     @computed get mainInnerContent() {
-        const width = this.propertiesWidth() + this._leftMenuFlyoutWidth + this.leftMenuWidth();
-        const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined;
+        const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth();
+        const width = this.propertiesWidth() + leftMenuFlyoutWidth;
         return <>
             {this.leftMenuPanel}
             <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
                 {this.flyout}
-                <div className="mainView-libraryHandle" style={{ display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
+                <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
                     <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
                 </div>
-                <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)`, transform: transform }}>
+                <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
 
                     {this.dockingContent}
 
-                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this._leftMenuFlyoutWidth ? 0 : this.propertiesWidth() - 1 }}>
+                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
                         <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
                     </div>
-                    <div className="properties-container">
+                    <div className="properties-container" style={{ width: this.propertiesWidth() }}>
                         {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
                     </div>
                 </div>
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 9142a68ed..4238f6d29 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -13,6 +13,7 @@
         width: 100%;
         pointer-events: none;
         mix-blend-mode: multiply; // bcz: makes text fuzzy!
+        overflow: hidden;
     }
 
     .imageBox-fader {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index b927e98db..89f70985c 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -354,15 +354,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             }} >
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                 renderDepth={this.props.renderDepth + 1}
-                isAnnotationOverlay={true}
                 fieldKey={this.annotationKey}
                 CollectionView={undefined}
+                isAnnotationOverlay={true}
                 annotationLayerHostsContent={true}
                 PanelWidth={this.props.PanelWidth}
                 PanelHeight={this.props.PanelHeight}
                 ScreenToLocalTransform={this.screenToLocalTransform}
-                scaling={returnOne}
                 select={emptyFunction}
+                scaling={returnOne}
                 whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                 removeDocument={this.removeDocument}
                 moveDocument={this.moveDocument}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index cdd36eb3b..4871599b8 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -14,6 +14,9 @@
     height: 100%;
     position: relative;
     .videoBox-viewer {
+        display:flex;
+        flex-direction: column;
+        height: 100%;
         border-radius: inherit;
         opacity: 0.99;  // hack!  overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger
     }
@@ -26,7 +29,17 @@
     .videoBox-stackPanel {
         z-index: -1;
         width: 100%;
-        position: absolute; 
+        position: relative; 
+    }
+
+    .videoBox-annotationLayer {
+        position: relative;
+        transform-origin: left top;
+        top: 0;
+        width: 100%;
+        pointer-events: none;
+        mix-blend-mode: multiply; // bcz: makes text fuzzy!
+        overflow: hidden;
     }
 }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e3ba638b3..440ccf638 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -550,7 +550,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     @computed get annotationLayer() {
-        return <div className="imageBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
+        return <div className="videoBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
     }
 
     marqueeDown = (e: React.PointerEvent) => {
@@ -568,10 +568,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.props.select(true);
     });
 
+    @computed get fitWidth() { return this.props.docViewPath?.().lastElement().fitWidth; }
     contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
     scaling = () => this.props.scaling?.() || 1;
-    panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100;
-    panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100;
+    panelWidth = (): number => this.fitWidth ? this.props.PanelWidth() : (Doc.NativeAspect(this.rootDoc) || 1) * this.panelHeight();
+    panelHeight = (): number => this.fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.heightPercent / 100 * this.props.PanelHeight();
     screenToLocalTransform = () => {
         const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
         return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
@@ -585,10 +586,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
             style={{
                 pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
-                borderRadius
+                borderRadius,
+                overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined
             }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
             <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
-                <div style={{ position: "absolute", transition: this.transition, width: this.panelWidth(), height: this.panelHeight(), top: 0, left: `${(100 - this.heightPercent) / 2}%` }}>
+                <div style={{
+                    position: "absolute", transition: this.transition,
+                    width: this.panelWidth(),
+                    height: this.panelHeight(),
+                    top: 0,
+                    left: (this.props.PanelWidth() - this.panelWidth()) / 2
+                }}>
                     <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
                         renderDepth={this.props.renderDepth + 1}
                         fieldKey={this.annotationKey}
@@ -608,9 +616,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         {this.contentFunc}
                     </CollectionFreeFormView>
                 </div>
-                {this.uIButtons}
                 {this.annotationLayer}
-                {this.renderTimeline}
                 {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
                     <MarqueeAnnotator
                         rootDoc={this.rootDoc}
@@ -625,6 +631,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         annotationLayer={this._annotationLayer.current}
                         mainCont={this._mainCont.current}
                     />}
+                {this.renderTimeline}
+                {this.uIButtons}
             </div>
         </div >);
     }
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 8e6586735..37d716993 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -430,9 +430,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!cm.findByDescription("Options...")) {
             !Doc.UserDoc().noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
             funcs.push({
-                description: (!this.layoutDoc.allowReflow ? "Allow" : "Prevent") + " Reflow", event: () => {
-                    const nw = !this.layoutDoc.allowReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
-                    this.layoutDoc.allowReflow = !nw;
+                description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => {
+                    const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
+                    this.layoutDoc.forceReflow = !nw;
                     if (nw) {
                         Doc.SetInPlace(this.layoutDoc, this.fieldKey + "-nativeWidth", nw, true);
                     }
@@ -536,7 +536,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get content() {
         const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
         return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
-            style={{ width: !this.layoutDoc.allowReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
+            style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
             {this.urlContent}
         </div>;
     }
-- 
cgit v1.2.3-70-g09d2


From 5f95911a504a47c867198fccc32a75bf22d26056 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 29 Sep 2021 15:15:21 -0400
Subject: added snapping to close curve or to self-snap a vertex to its curve. 
 fixed ink decorations from being clipped when zoomed.  fixed crash with
 zero-length tangent

---
 src/client/views/InkControlPtHandles.tsx | 18 ++++++----
 src/client/views/InkStrokeProperties.ts  | 61 ++++++++++++++++++++++++++++++--
 src/client/views/InkingStroke.tsx        | 26 ++++----------
 src/client/views/MainView.tsx            |  2 +-
 4 files changed, 78 insertions(+), 29 deletions(-)

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

diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index f80aca268..249a0fa64 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -42,10 +42,13 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
             setupMoveUpEvents(this, e,
                 action((e: PointerEvent, down: number[], delta: number[]) => {
                     if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("drag ink ctrl pt");
-                    InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex);
+                    InkStrokeProperties.Instance?.moveControl(delta[0] * screenScale, delta[1] * screenScale, controlIndex);
                     return false;
                 }),
                 action(() => {
+                    if (this.controlUndo) {
+                        InkStrokeProperties.Instance?.snapControl(this.props.inkDoc, controlIndex);
+                    }
                     this.controlUndo?.end();
                     this.controlUndo = undefined;
                     UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
@@ -117,20 +120,21 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
         }
 
         const screenSpaceLineWidth = this.props.screenSpaceLineWidth;
+        const closed = inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y;
         const nearestScreenPt = this.props.nearestScreenPt();
         const TagType = (broken?: boolean) => broken ? "rect" : "circle";
         const hdl = (control: { X: number, Y: number, I: number }, scale: number, color: string) => {
             const broken = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"))?.includes(control.I);
-            const Tag = TagType(broken) as keyof JSX.IntrinsicElements;
-            return <circle key={control.I.toString() + scale}
-                x={control.X - screenSpaceLineWidth * 3 * scale}
-                y={control.Y - screenSpaceLineWidth * 3 * scale}
+            const Tag = TagType((control.I === 0 || control.I === inkData.length - 1) && !closed) as keyof JSX.IntrinsicElements;
+            return <Tag key={control.I.toString() + scale}
+                x={control.X - screenSpaceLineWidth * 2 * scale}
+                y={control.Y - screenSpaceLineWidth * 2 * scale}
                 cx={control.X}
                 cy={control.Y}
                 r={screenSpaceLineWidth * 2 * scale}
                 opacity={this.controlUndo ? 0.15 : 1}
-                height={screenSpaceLineWidth * 6 * scale}
-                width={screenSpaceLineWidth * 6 * scale}
+                height={screenSpaceLineWidth * 4 * scale}
+                width={screenSpaceLineWidth * 4 * scale}
                 strokeWidth={screenSpaceLineWidth / 2}
                 stroke={Colors.MEDIUM_BLUE}
                 fill={broken ? Colors.MEDIUM_BLUE : color}
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 3770eb7c1..ac5cdfee2 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -189,7 +189,7 @@ export class InkStrokeProperties {
             const order = controlIndex % 4;
             const closed = ink.lastElement().X === ink[0].X && ink.lastElement().Y === ink[0].Y;
 
-            return ink.map((pt, i) => {
+            const newpts = ink.map((pt, i) => {
                 const leftHandlePoint = order === 0 && i === controlIndex + 1;
                 const rightHandlePoint = order === 0 && controlIndex !== 0 && i === controlIndex - 2;
                 if (controlIndex === i ||
@@ -201,12 +201,68 @@ export class InkStrokeProperties {
                     (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 1) ||
                     (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 2) ||
                     ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1))) {
-                    return ({ X: pt.X - deltaX / xScale, Y: pt.Y - deltaY / yScale });
+                    return ({ X: pt.X + deltaX / xScale, Y: pt.Y + deltaY / yScale });
                 }
                 return pt;
             });
+            return newpts;
         })
 
+
+    public static nearestPtToStroke(ctrlPoints: { X: number, Y: number }[], refPt: { X: number, Y: number }, excludeSegs?: number[]) {
+        var distance = Number.MAX_SAFE_INTEGER;
+        var nearestT = -1;
+        var nearestSeg = -1;
+        var nearestPt = { X: 0, Y: 0 };
+        for (var i = 0; i < ctrlPoints.length - 3; i += 4) {
+            if (excludeSegs?.includes(i)) continue;
+            const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]];
+            const point = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).project({ x: refPt.X, y: refPt.Y });
+            if (point.t !== undefined) {
+                const dist = Math.sqrt((point.x - refPt.X) * (point.x - refPt.X) + (point.y - refPt.Y) * (point.y - refPt.Y));
+                if (dist < distance) {
+                    distance = dist;
+                    nearestT = point.t;
+                    nearestSeg = i;
+                    nearestPt = { X: point.x, Y: point.y };
+                }
+            }
+        }
+        return { distance, nearestT, nearestSeg, nearestPt };
+    }
+
+    /**
+     * Handles the movement/scaling of a control point.
+     */
+    snapControl = (inkDoc: Doc, controlIndex: number) => {
+        const ink = Cast(inkDoc.data, InkField)?.inkData;
+        if (ink) {
+            const closed = ink.lastElement().X === ink[0].X && ink.lastElement().Y === ink[0].Y;
+
+            // figure out which segments we don't want to snap to - avoid the dragged control point's segment and the next and prev segments (when they exist -- ie not for endpoints of unclosed curve)
+            const thisseg = Math.floor(controlIndex / 4) * 4;
+            const which = controlIndex % 4;
+            const nextseg = which > 1 && (closed || controlIndex < ink.length - 1) ? (thisseg + 4) % ink.length : -1;
+            const prevseg = which < 2 && (closed || controlIndex > 0) ? (thisseg - 4 + ink.length) % ink.length : -1;
+            const refPt = ink[controlIndex];
+            const { nearestPt } = InkStrokeProperties.nearestPtToStroke(ink, refPt, [thisseg, prevseg, nextseg]);
+
+            // nearestPt is in inkDoc coordinates -- we need to compute the distance in screen coordinates.
+            // so we scale the X & Y distances by the internal ink scale factor and then transform the final distance by the ScreenToLocal.Scale of the inkDoc itself.
+            const oldXrange = (xs => ({ coord: NumCast(inkDoc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X));
+            const oldYrange = (ys => ({ coord: NumCast(inkDoc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y));
+            const ptsXscale = NumCast(inkDoc._width) / (oldXrange.max - oldXrange.min);
+            const ptsYscale = NumCast(inkDoc._height) / (oldYrange.max - oldYrange.min);
+            const near = Math.sqrt((nearestPt.X - refPt.X) * (nearestPt.X - refPt.X) * ptsXscale * ptsXscale +
+                (nearestPt.Y - refPt.Y) * (nearestPt.Y - refPt.Y) * ptsYscale * ptsYscale);
+
+            if (near / (this.selectedInk?.lastElement().props.ScreenToLocalTransform().Scale || 1) < 10) {
+                return this.moveControl((nearestPt.X - ink[controlIndex].X) * ptsXscale, (nearestPt.Y - ink[controlIndex].Y) * ptsYscale, controlIndex)
+            }
+        }
+        return false;
+    }
+
     /**
      * Snaps a control point with broken tangency back to synced rotation.
      * @param handleIndexA The handle point that retains its current position.
@@ -247,6 +303,7 @@ export class InkStrokeProperties {
     angleBetweenTwoVectors = (vectorA: PointData, vectorB: PointData) => {
         const magnitudeA = Math.sqrt(vectorA.X * vectorA.X + vectorA.Y * vectorA.Y);
         const magnitudeB = Math.sqrt(vectorB.X * vectorB.X + vectorB.Y * vectorB.Y);
+        if (magnitudeA === 0 || magnitudeB === 0) return 0;
         // Normalizing the vectors.
         vectorA = { X: vectorA.X / magnitudeA, Y: vectorA.Y / magnitudeA };
         vectorB = { X: vectorB.X / magnitudeB, Y: vectorB.Y / magnitudeB };
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 4cd7f7698..8b1b3ea32 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -130,25 +130,14 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const screenPts = inkData.map(point => this.props.ScreenToLocalTransform().inverse().transformPoint(
             (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
             (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
-        var nearest = Number.MAX_SAFE_INTEGER;
-        this._nearestT = -1;
-        this._nearestSeg = -1;
-        this._nearestScrPt = { X: 0, Y: 0 };
-        for (var i = 0; i < screenPts.length - 3; i += 4) {
-            const array = [screenPts[i], screenPts[i + 1], screenPts[i + 2], screenPts[i + 3]];
-            const point = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).project({ x: e.clientX, y: e.clientY });
-            if (point.t) {
-                const dist = (point.x - e.clientX) * (point.x - e.clientX) + (point.y - e.clientY) * (point.y - e.clientY);
-                if (dist < nearest) {
-                    nearest = dist;
-                    this._nearestT = point.t;
-                    this._nearestSeg = i;
-                    this._nearestScrPt = { X: point.x, Y: point.y };
-                }
-            }
-        }
+        const { distance, nearestT, nearestSeg, nearestPt } = InkStrokeProperties.nearestPtToStroke(screenPts, { X: e.clientX, Y: e.clientY });
+
+        this._nearestT = nearestT;
+        this._nearestSeg = nearestSeg;
+        this._nearestScrPt = nearestPt;
     }
 
+
     nearestScreenPt = () => this._nearestScrPt;
     componentUI = (boundsLeft: number, boundsTop: number) => {
         const inkDoc = this.props.Document;
@@ -159,11 +148,10 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const screenPts = inkData.map(point => this.props.ScreenToLocalTransform().inverse().transformPoint(
             (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2,
             (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] }));
-        const screenOrigin = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
         const screenHdlPts = screenPts;
 
         return <div className="inkstroke-UI" style={{
-            clip: `rect(${boundsTop - screenOrigin[1]}px, 10000px, 10000px, ${boundsLeft - screenOrigin[0]}px)`
+            clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)`
         }} >
             {InteractionUtils.CreatePolyline(screenPts, 0, 0, Colors.MEDIUM_BLUE, screenInkWidth[0], screenSpaceCenterlineStrokeWidth,
                 StrCast(inkDoc.strokeBezier), StrCast(inkDoc.fillColor, "none"),
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 3f7df705f..6d0d5eb39 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -618,7 +618,7 @@ export class MainView extends React.Component {
             <GroupManager />
             <GoogleAuthenticationManager />
             <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
-            <ComponentDecorations boundsLeft={0} boundsTop={this.topOfMainDocContent} />
+            <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
             {this.topbar}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
-- 
cgit v1.2.3-70-g09d2

