From 131408385d49fc8d5f360c2918737da4cecc13c1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 29 Jul 2022 16:55:01 -0400
Subject: fixed autoHeight for texdtviews embedded in text views.   fixed doc
 decorations for sidebar items in lightbox view.  added 'tree' as sidebar type
 for text.  fixed text sidebar to show same annotations whether in stacking or
 tree view.   fixed linkDescription pop to go away on click outside of it.

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

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 22ca76b2e..21326efaa 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -394,7 +394,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     // remove all node type and apply the passed-in one to the selected text
     changeListType = (mapStyle: string) => {
         const active = this.view?.state && RichTextMenu.Instance.getActiveListStyle();
-        const nodeType = this.view?.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle });
+        const nodeType = this.view?.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? '' : mapStyle });
         if (!this.view || nodeType?.attrs.mapStyle === '') return;
 
         const nextIsOL = this.view.state.selection.$from.nodeAfter?.type === schema.nodes.ordered_list;
-- 
cgit v1.2.3-70-g09d2


From dbf21b19ecc70047a580023104d05aed0d43c946 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 4 Aug 2022 11:01:37 -0400
Subject: fixed notetaking view to have an 'unset' category for unassigned
 notes.  fixed pivot view to not fail when docs have no width.

---
 .../views/collections/CollectionNoteTakingView.tsx | 30 +++++----
 .../collections/CollectionNoteTakingViewColumn.tsx |  2 +
 .../CollectionFreeFormLayoutEngines.tsx            |  6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 -
 src/client/views/nodes/FilterBox.tsx               |  2 +-
 src/client/views/nodes/FunctionPlotBox.tsx         | 76 ++++++++++++----------
 .../views/nodes/formattedText/RichTextMenu.tsx     | 13 ----
 7 files changed, 66 insertions(+), 64 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 989719c80..f24b98621 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -55,10 +55,18 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
     }
     @computed get columnHeaders() {
-        return Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
+        const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
+        let docs = this.childDocs;
+        const needsUnsetCategory = docs.some(d => {
+            return !d[this.notetakingCategoryField] && !columnHeaders.find(sh => sh.heading === 'unset');
+        });
+        if (needsUnsetCategory) {
+            columnHeaders.push(new SchemaHeaderField('unset'));
+        }
+        return columnHeaders;
     }
     @computed get notetakingCategoryField() {
-        return 'noteTakingCategory';
+        return 'NotetakingCategory';
     }
     @computed get filteredChildren() {
         return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
@@ -120,8 +128,8 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     // (2) documentView gets unmounted as you remove it from the list
     get Sections() {
         const columnHeaders = this.columnHeaders;
-        const sections = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
         let docs = this.childDocs;
+        const sections = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
         const rowCol = this.docsDraggedRowCol;
 
         // filter out the currently dragged docs from the child docs, since we will insert them later
@@ -133,10 +141,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
 
         // this will sort the docs into the correct columns (minus the ones you're currently dragging)
         docs.map(d => {
-            if (!d[this.notetakingCategoryField]) {
-                d[this.notetakingCategoryField] = columnHeaders.length > 0 ? columnHeaders[0].heading : `New Column`;
-            }
-            const sectionValue = d[this.notetakingCategoryField] as object;
+            const sectionValue = (d[this.notetakingCategoryField] as object) ?? `unset`;
 
             // look for if header exists already
             const existingHeader = columnHeaders.find(sh => sh.heading === sectionValue.toString());
@@ -310,7 +315,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     // how to get the width of a document. Currently returns the width of the column (minus margins)
     // if a note doc. Otherwise, returns the normal width (for graphs, images, etc...)
     getDocWidth(d: Doc) {
-        const heading = d[this.notetakingCategoryField] as object;
+        const heading = (d[this.notetakingCategoryField] as object) ?? 'unset';
         const castedSectionValue = heading.toString();
         const existingHeader = this.columnHeaders.find(sh => sh.heading === castedSectionValue);
         const colStartXCoords = this.columnStartXCoords;
@@ -364,7 +369,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
     // This function is used to preview where a document will drop in a column once a drag is complete.
     @action
     onPointerOver = (ex: number, ey: number) => {
-        console.log('Pover9ing = ');
         if (this.childDocList) {
             // get the current docs for the column based on the mouse's x coordinate
             // will use again later, which is why we're saving as local
@@ -386,7 +390,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
                 }
                 pos0 = pos1;
             });
-            console.log('Pover = ' + dropInd + ' ' + this.getColumnFromXCoord(xCoord));
             // we alter the pivot fields of the docs in case they are moved to a new column.
             const colIndex = this.getColumnFromXCoord(xCoord);
             const colHeader = StrCast(this.columnHeaders[colIndex].heading);
@@ -422,7 +425,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         if (docs) {
             docs.map(d => {
                 if (d instanceof Promise) return;
-                const sectionValue = d[this.notetakingCategoryField] as object;
+                const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset';
                 if (sectionValue.toString() == colHeader) {
                     docsMatchingHeader.push(d);
                 }
@@ -619,7 +622,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
             const col = this.sectionNoteTaking(sections[i][0], sections[i][1]);
             eles.push(col);
             if (i < sections.length - 1) {
-                eles.push(<CollectionNoteTakingViewDivider index={i + 1} setColumnStartXCoords={this.setColumnStartXCoords.bind(this)} xMargin={this.xMargin} />);
+                eles.push(<CollectionNoteTakingViewDivider key={`divider${i}`} index={i + 1} setColumnStartXCoords={this.setColumnStartXCoords.bind(this)} xMargin={this.xMargin} />);
             }
         }
         return eles;
@@ -686,7 +689,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
         return (
             <>
                 {buttonMenu || noviceExplainer ? (
-                    <div className="documentButtonMenu">
+                    <div className="documentButtonMenu" key="buttons">
                         {buttonMenu ? this.buttonMenu : null}
                         {Doc.UserDoc().noviceMode && noviceExplainer ? <div className="documentExplanation">{noviceExplainer}</div> : null}
                     </div>
@@ -694,6 +697,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
                 <div
                     className="collectionNoteTakingView"
                     ref={this.createRef}
+                    key="notes"
                     style={{
                         overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
                         background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 5ba262418..8452d895f 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -112,6 +112,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
 
     @action
     headingChanged = (value: string, shiftDown?: boolean) => {
+        console.log('HEADING CH');
         const castedValue = this.getValue(value);
         if (castedValue) {
             if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
@@ -255,6 +256,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
 
     @computed get innards() {
         TraceMobx();
+        console.log('INNARD START');
         const key = this.props.pivotField;
         const heading = this._heading;
         const columnYMargin = this.props.headingObject ? 0 : this.props.yMargin;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index a0ebe4cdc..3d85d32a0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -393,8 +393,10 @@ function normalizeResults(
         0
     );
     aggBounds.r = aggBounds.x + Math.max(minWidth, aggBounds.r - aggBounds.x);
-    const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
-    let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? panelDim[1] / (aggBounds.b - aggBounds.y) : wscale;
+    const width = aggBounds.r - aggBounds.x === 0 ? 1 : aggBounds.r - aggBounds.x;
+    const height = aggBounds.b - aggBounds.y === 0 ? 1 : aggBounds.b - aggBounds.y;
+    const wscale = panelDim[0] / width;
+    let scale = wscale * height > panelDim[1] ? panelDim[1] / height : wscale;
     if (Number.isNaN(scale)) scale = 1;
 
     Array.from(docMap.entries())
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3e938ec1c..82b377dfa 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -496,7 +496,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const currentCol = DocListCast(this.rootDoc.currentInkDoc);
             const rootDocList = DocListCast(this.rootDoc.data);
             currentCol.push(rootDocList[rootDocList.length - 1]);
-            console.log(currentCol);
 
             this._batch?.end();
         }
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index ff04a293c..dc3fc0396 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -116,7 +116,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable _loaded = false;
     componentDidMount() {
         reaction(
-            () => DocListCastAsync(this.layoutDoc.data),
+            () => DocListCastAsync(this.layoutDoc[this.fieldKey]),
             async activeTabsAsync => {
                 const activeTabs = await activeTabsAsync;
                 activeTabs && (await SearchBox.foreachRecursiveDocAsync(activeTabs, emptyFunction));
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 3ab0a3ff2..15d0f88f6 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -1,4 +1,4 @@
-import functionPlot from "function-plot";
+import functionPlot from 'function-plot';
 import { action, computed, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -12,7 +12,6 @@ import { Docs } from '../../documents/Documents';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldView, FieldViewProps } from './FieldView';
 
-
 const EquationSchema = createSchema({});
 
 type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>;
@@ -20,74 +19,83 @@ const EquationDocument = makeInterface(EquationSchema, documentSchema);
 
 @observer
 export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FunctionPlotBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(FunctionPlotBox, fieldKey);
+    }
     public static GraphCount = 0;
     _plot: any;
-    _plotId = "";
+    _plotId = '';
     _plotEle: any;
     constructor(props: any) {
         super(props);
-        this._plotId = "graph" + FunctionPlotBox.GraphCount++;
+        this._plotId = 'graph' + FunctionPlotBox.GraphCount++;
     }
     componentDidMount() {
         this.props.setContentView?.(this);
-        reaction(() => [DocListCast(this.dataDoc.data).lastElement()?.text, this.layoutDoc.width, this.layoutDoc.height, this.dataDoc.xRange, this.dataDoc.yRange],
-            () => this.createGraph());
+        reaction(
+            () => [DocListCast(this.dataDoc[this.fieldKey]).lastElement()?.text, this.layoutDoc.width, this.layoutDoc.height, this.dataDoc.xRange, this.dataDoc.yRange],
+            () => this.createGraph()
+        );
     }
     getAnchor = () => {
         const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc });
         anchor.xRange = new List<number>(Array.from(this._plot.options.xAxis.domain));
         anchor.yRange = new List<number>(Array.from(this._plot.options.yAxis.domain));
         return anchor;
-    }
+    };
     @action
     scrollFocus = (doc: Doc, smooth: boolean) => {
-        this.dataDoc.xRange = new List<number>(Array.from(Cast(doc.xRange, listSpec("number"), Cast(this.dataDoc.xRange, listSpec("number"), [-10, 10]))));
-        this.dataDoc.yRange = new List<number>(Array.from(Cast(doc.yRange, listSpec("number"), Cast(this.dataDoc.xRange, listSpec("number"), [-1, 9]))));
+        this.dataDoc.xRange = new List<number>(Array.from(Cast(doc.xRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10]))));
+        this.dataDoc.yRange = new List<number>(Array.from(Cast(doc.yRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9]))));
         return 0;
-    }
+    };
     createGraph = (ele?: HTMLDivElement) => {
         this._plotEle = ele || this._plotEle;
         const width = this.props.PanelWidth();
         const height = this.props.PanelHeight();
-        const fn = StrCast(DocListCast(this.dataDoc.data).lastElement()?.text, "x^2").replace(/\\frac\{(.*)\}\{(.*)\}/, "($1/$2)");
+        const fn = StrCast(DocListCast(this.dataDoc.data).lastElement()?.text, 'x^2').replace(/\\frac\{(.*)\}\{(.*)\}/, '($1/$2)');
         try {
             this._plot = functionPlot({
-                target: "#" + this._plotEle.id,
+                target: '#' + this._plotEle.id,
                 width,
                 height,
-                xAxis: { domain: Cast(this.dataDoc.xRange, listSpec("number"), [-10, 10]) },
-                yAxis: { domain: Cast(this.dataDoc.xRange, listSpec("number"), [-1, 9]) },
+                xAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10]) },
+                yAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9]) },
                 grid: true,
                 data: [
                     {
                         fn,
                         // derivative: { fn: "2 * x", updateOnMouseMove: true }
-                    }
-                ]
+                    },
+                ],
             });
         } catch (e) {
             console.log(e);
         }
-    }
+    };
     @computed get theGraph() {
-        return <div id={`${this._plotId}`} ref={r => r && this.createGraph(r)} style={{ position: "absolute", width: "100%", height: "100%" }}
-            onPointerDown={e => e.stopPropagation()} />;
+        return <div id={`${this._plotId}`} ref={r => r && this.createGraph(r)} style={{ position: 'absolute', width: '100%', height: '100%' }} onPointerDown={e => e.stopPropagation()} />;
     }
     render() {
         TraceMobx();
-        return (<div
-            style={{
-                pointerEvents: !this.isContentActive() ? "all" : undefined,
-                width: this.props.PanelWidth(),
-                height: this.props.PanelHeight()
-            }}
-        >
-            {this.theGraph}
-            <div style={{
-                display: this.props.isSelected() ? "none" : undefined, position: "absolute", width: "100%", height: "100%",
-                pointerEvents: "all"
-            }} />
-        </div>);
+        return (
+            <div
+                style={{
+                    pointerEvents: !this.isContentActive() ? 'all' : undefined,
+                    width: this.props.PanelWidth(),
+                    height: this.props.PanelHeight(),
+                }}>
+                {this.theGraph}
+                <div
+                    style={{
+                        display: this.props.isSelected() ? 'none' : undefined,
+                        position: 'absolute',
+                        width: '100%',
+                        height: '100%',
+                        pointerEvents: 'all',
+                    }}
+                />
+            </div>
+        );
     }
-}
\ 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 21326efaa..2a77210ae 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -60,7 +60,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable private showLinkDropdown: boolean = false;
 
     _reaction: IReactionDisposer | undefined;
-    _delayHide = false;
     constructor(props: Readonly<{}>) {
         super(props);
         runInAction(() => {
@@ -70,16 +69,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         });
     }
 
-    componentDidMount() {
-        this._reaction = reaction(
-            () => SelectionManager.Views(),
-            () => this._delayHide && !(this._delayHide = false) && this.fadeOut(true)
-        );
-    }
-    componentWillUnmount() {
-        this._reaction?.();
-    }
-
     @computed get noAutoLink() {
         return this._noLinkActive;
     }
@@ -108,8 +97,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         return this._activeAlignment;
     }
 
-    public delayHide = () => (this._delayHide = true);
-
     @action
     public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any) {
         if (this._linkToRef.current?.getBoundingClientRect().width) {
-- 
cgit v1.2.3-70-g09d2


From 542a057edbdd4661cfb5ef5d07058aa93b47a113 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 24 Aug 2022 11:16:46 -0400
Subject: cleaned up some of the pin with view code.  added a pin button to
 menu bar when tab is selected to avoid needing to use context menu

---
 src/client/util/CurrentUserUtils.ts                |   5 +-
 src/client/util/SelectionManager.ts                |   3 +
 src/client/views/collections/CollectionMenu.tsx    |  44 ---
 src/client/views/collections/TabDocView.tsx        |  39 +--
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   3 +
 src/client/views/nodes/button/FontIconBox.tsx      |  96 ++----
 .../views/nodes/formattedText/RichTextMenu.tsx     |   4 +-
 src/client/views/nodes/trails/PresBox.tsx          | 358 +++++----------------
 8 files changed, 124 insertions(+), 428 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 17d58595c..99a8c895f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -267,7 +267,7 @@ export class CurrentUserUtils {
             {key: "Script",     creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
             // {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument(opts),         opts: { _width: 300, _height: 300 }},
             {key: "Header",     creator: headerTemplate,                                    opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,}},
-            {key: "Presentation",creator: Docs.Create.PresDocument,                         opts: { _width: 400, _height: 500, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }},
+            {key: "Presentation",creator: Docs.Create.PresDocument,                         opts: { _width: 400, _height: 500, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, treeViewHideTitle: true,  _chromeHidden: true, boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _backgroundGridShow: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, 
                                                                                                 treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, 
@@ -675,8 +675,9 @@ export class CurrentUserUtils {
                     CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
                     CollectionViewType.Grid, CollectionViewType.NoteTaking]),
               title: "Perspective",                    toolTip: "View",                 btnType: ButtonType.DropdownList,                                                                                    ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
+            { title: "Pin",     icon: "map-pin",       toolTip: "Pin View to Trail",    btnType: ButtonType.ClickButton,         funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "tab")'},                        width: 20,  scripts: { onClick: 'pinWithView(_readOnly_)'}},
             { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
-            { title: "Num",     icon: "",              toolTip: "Frame Number",         btnType: ButtonType.TextButton,          funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20,  scripts: {}},
+            { title: "Num",     icon: "",              toolTip: "Frame Number",         btnType: ButtonType.TextButton,          funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement()?.currentFrame.toString()'}, width: 20,  scripts: {}},
             { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,         funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
             { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",btnType: ButtonType.ColorButton,         funcs: {hidden: '!SelectionManager_selectedDocType()'},                     ignoreClick: true, width: 20,  scripts: { script:  'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected
             { title: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         funcs: {hidden: '!SelectionManager_selectedDocType()'},                     ignoreClick: true,             scripts: { script:  'return setHeaderColor(value, _readOnly_)'}},
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 1c84af94a..7a555d5f8 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -101,6 +101,9 @@ export namespace SelectionManager {
     }
 }
 ScriptingGlobals.add(function SelectionManager_selectedDocType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) {
+    if (colType === ('tab' as any)) {
+        return SelectionManager.Views().lastElement()?.props.renderDepth === 0;
+    }
     let selected = (sel => (checkContext ? DocCast(sel?.context) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
     return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true;
 });
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index eb55650e4..0dc30e0fd 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -586,49 +586,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         );
     }
 
-    @undoBatch
-    @action
-    pinWithView = (targetDoc: Opt<Doc>) => {
-        if (targetDoc) {
-            TabDocView.PinDoc(targetDoc);
-            const presArray: Doc[] = PresBox.Instance?.sortArray();
-            const size: number = PresBox.Instance?._selectedArray.size;
-            const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
-            const activeDoc = presSelected ? PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.indexOf(presSelected) + 1] : PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.length - 1];
-            if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking || targetDoc._viewType === CollectionViewType.NoteTaking) {
-                const scroll = targetDoc._scrollTop;
-                activeDoc.presPinView = true;
-                activeDoc.presPinViewScroll = scroll;
-            } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.MAP) {
-                const x = targetDoc._panX;
-                const y = targetDoc._panY;
-                const scale = targetDoc._viewScale;
-                activeDoc.presPinView = true;
-                activeDoc.presPinViewX = x;
-                activeDoc.presPinViewY = y;
-                activeDoc.presPinViewScale = scale;
-            } else if (targetDoc.type === DocumentType.VID) {
-                activeDoc.presPinView = true;
-            } else if (targetDoc.type === DocumentType.COMPARISON) {
-                const width = targetDoc._clipWidth;
-                activeDoc.presPinClipWidth = width;
-                activeDoc.presPinView = true;
-            }
-        }
-    };
-
-    @computed
-    get pinWithViewButton() {
-        const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: 'auto', width: 19 }} />;
-        return !this.selectedDoc ? null : (
-            <Tooltip title={<div className="dash-tooltip">{'Pin with current view'}</div>} placement="top">
-                <button className="antimodeMenu-button" style={{ justifyContent: 'center' }} onClick={() => this.pinWithView(this.selectedDoc)}>
-                    {presPinWithViewIcon}
-                </button>
-            </Tooltip>
-        );
-    }
-
     @undoBatch
     onAlias = () => {
         if (this.selectedDoc && this.selectedDocumentView) {
@@ -722,7 +679,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                         {this.aliasButton}
                         {/* {this.pinButton} */}
                         {this.toggleOverlayButton}
-                        {this.pinWithViewButton}
                         <div className="collectionMenu-divider" key="divider2"></div>
                         {this.subChrome}
                         <div className="collectionMenu-divider" key="divider3"></div>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e147f34d2..73574bdb3 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -242,43 +242,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 const size: number = PresBox.Instance?._selectedArray.size;
                 const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
                 const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
-                // If pinWithView option set then update scale and x / y props of slide
-                if (pinProps?.pinWithView) {
-                    const viewProps = pinProps.pinWithView;
-                    pinDoc.presPinView = true;
-                    pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
-                    pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
-                    pinDoc.presPinViewScale = viewProps.scale;
-                    pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left + viewProps.bounds.width, viewProps.bounds.top + viewProps.bounds.height]);
-                }
-                if (pinProps?.pinDocView) {
-                    const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking;
-                    const pannable: boolean = (pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG;
-                    if (scrollable) {
-                        const scroll = doc._scrollTop;
-                        pinDoc.presPinView = true;
-                        pinDoc.presPinViewScroll = scroll;
-                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) {
-                        pinDoc.presPinView = true;
-                        pinDoc.presStartTime = doc._currentTimecode;
-                        pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1;
-                    } else if (pannable) {
-                        pinDoc.presPinView = true;
-                        pinDoc.presPinViewX = pinDoc._panX;
-                        pinDoc.presPinViewY = pinDoc._panY;
-                        pinDoc.presPinViewScale = pinDoc._viewScale;
-                        const pw = NumCast(pinProps.panelWidth);
-                        const ph = NumCast(pinProps.panelHeight);
-                        const ps = NumCast(pinDoc._viewScale);
-                        if (pw && ph && ps) {
-                            pinDoc.contentBounds = new List<number>([NumCast(pinDoc.panX) - pw / 2 / ps, NumCast(pinDoc.panY) - ph / 2 / ps, NumCast(pinDoc.panX) + pw / 2 / ps, NumCast(pinDoc.panY) + ph / 2 / ps]);
-                        }
-                    } else if (doc.type === DocumentType.COMPARISON) {
-                        const width = doc._clipWidth;
-                        pinDoc.presPinClipWidth = width;
-                        pinDoc.presPinView = true;
-                    }
-                }
+
+                PresBox.pinDocView(pinDoc, pinProps);
                 pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
                 Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
                 if (!pinProps?.audioRange && duration !== undefined) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 03beaf65e..052cbd3bb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -2213,3 +2213,6 @@ ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
 ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
     !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
 });
+ScriptingGlobals.add(function pinWithView(readOnly: boolean) {
+    !readOnly && SelectionManager.Views().forEach(view => TabDocView.PinDoc(view.rootDoc, { pinDocView: true, panelWidth: view.props.PanelWidth(), panelHeight: view.props.PanelHeight() }));
+});
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index fc5bf86f4..c72b5ca9b 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -254,16 +254,13 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
         const script = ScriptCast(this.rootDoc.script);
-        if (!script) {
-            return null;
-        }
 
         let noviceList: string[] = [];
         let text: string | undefined;
         let dropdown = true;
         let icon: IconProp = 'caret-down';
         try {
-            if (script.script.originalScript.startsWith('setView')) {
+            if (script?.script.originalScript.startsWith('setView')) {
                 const selected = SelectionManager.Docs().lastElement();
                 if (selected) {
                     if (StrCast(selected.type) === DocumentType.COL) {
@@ -279,7 +276,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     text = 'User Default';
                 }
                 noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
-            } else if (script.script.originalScript.startsWith('setFont')) {
+            } else if (script?.script.originalScript.startsWith('setFont')) {
                 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'];
@@ -289,11 +286,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         }
 
         // Get items to place into the list
-        const list = this.buttonList.map(value => {
-            if (Doc.noviceMode && !noviceList.includes(value)) {
-                return;
-            }
-            return (
+        const list = this.buttonList
+            .filter(value => !Doc.noviceMode || noviceList.includes(value))
+            .map(value => (
                 <div
                     className="list-item"
                     key={`${value}`}
@@ -304,8 +299,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     onClick={() => script.script.run({ value }).result}>
                     {value[0].toUpperCase() + value.slice(1)}
                 </div>
-            );
-        });
+            ));
 
         const label =
             !this.label || !FontIconBox.GetShowLabels() ? null : (
@@ -451,7 +445,6 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     @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">
@@ -487,89 +480,50 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     render() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const label =
-            !this.label || !FontIconBox.GetShowLabels() ? null : (
-                <div className="fontIconBox-label" style={{ color, backgroundColor }}>
-                    {this.label}
-                </div>
-            );
-
-        const menuLabel =
+        const label = (noBackground: boolean = false) =>
             !this.label || !FontIconBox.GetShowLabels() ? null : (
-                <div className="fontIconBox-label" style={{ color, backgroundColor: 'transparent' }}>
+                <div className="fontIconBox-label" style={{ color, backgroundColor: noBackground ? 'transparent' : backgroundColor }}>
                     {this.label}
                 </div>
             );
-
         // TODO:glr Add label of button type
-        let button: JSX.Element | null = this.defaultButton;
+        let button: JSX.Element = this.defaultButton;
 
+        // prettier-ignore
         switch (this.type) {
-            case ButtonType.TextButton:
-                button = (
+            case ButtonType.DropdownList: return this.dropdownListButton;
+            case ButtonType.ColorButton: return this.colorButton; 
+            case ButtonType.NumberButton: return this.numberButton; 
+            case ButtonType.EditableText: return this.editableText;
+            case ButtonType.DropdownButton: button = this.dropdownButton; break;
+            case ButtonType.ToggleButton: button = this.toggleButton; break;
+            case ButtonType.TextButton: button = (
                     <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
                         {this.Icon(color)}
                         {StrCast(this.rootDoc.buttonText) ? <div className="button-text">{StrCast(this.rootDoc.buttonText)}</div> : null}
-                        {label}
-                    </div>
-                );
-                // button = <TextButton {...buttonProps}></TextButton>
-                break;
-            case ButtonType.EditableText:
-                button = this.editableText;
-                break;
-            case ButtonType.NumberButton:
-                button = this.numberButton;
-                break;
-            case ButtonType.DropdownButton:
-                button = this.dropdownButton;
-                break;
-            case ButtonType.DropdownList:
-                button = this.dropdownListButton;
-                break;
-            case ButtonType.ColorButton:
-                button = this.colorButton;
-                break;
-            case ButtonType.ToolButton:
-                button = (
-                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ opacity: 1, backgroundColor, color }}>
-                        {this.Icon(color)}
-                        {label}
+                        {label()}
                     </div>
                 );
                 break;
-            case ButtonType.ToggleButton:
-                button = this.toggleButton;
-                // button = <ToggleButton {...buttonProps}></ToggleButton>
-                break;
             case ButtonType.ClickButton:
-                button = (
-                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ color, backgroundColor, opacity: 1 }}>
+            case ButtonType.ToolButton: button = (
+                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ backgroundColor, color, opacity: 1 }}>
                         {this.Icon(color)}
-                        {label}
+                        {label()}
                     </div>
                 );
                 break;
-            case ButtonType.MenuButton:
-                button = (
+            case ButtonType.MenuButton: button = (
                     <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
                         {this.Icon(color)}
-                        {menuLabel}
+                        {label(true)}
                         <FontIconBadge value={Cast(this.Document.badgeValue, 'string', null)} />
                     </div>
                 );
                 break;
-            default:
-                break;
         }
 
-        const retval =
-            !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? (
-                button
-            ) : button !== null ? (
-                <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip>
-            ) : null;
-        return retval;
+        return !this.layoutDoc.toolTip ? button : <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip>;
     }
 }
 
@@ -698,7 +652,7 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo
 ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        return RichTextMenu.Instance.fontSize.replace('px', '');
+        return RichTextMenu.Instance?.fontSize.replace('px', '');
     }
     if (typeof size === 'number') size = size.toString();
     if (size && Number(size).toString() === size) size += 'px';
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 2a77210ae..0cbe60c0c 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -16,7 +16,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 import { FieldViewProps } from '../FieldView';
-import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox';
+import { FormattedTextBox } from './FormattedTextBox';
 import { updateBullets } from './ProsemirrorExampleTransfer';
 import './RichTextMenu.scss';
 import { schema } from './schema_rts';
@@ -29,7 +29,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     private _linkToRef = React.createRef<HTMLInputElement>();
 
     @observable public view?: EditorView;
-    public editorProps: (FieldViewProps & FormattedTextBoxProps) | undefined;
+    public editorProps: FieldViewProps | undefined;
 
     public _brushMap: Map<string, Set<Mark>> = new Map();
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 05e09361b..eb40089ec 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -358,20 +358,24 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
     };
 
+    static pinDataTypes(target: Doc) {
+        const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(target.type as any) || target._viewType === CollectionViewType.Stacking;
+        const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform);
+        const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any);
+        const clippable = [DocumentType.COMPARISON].includes(target.type as any);
+        return { scrollable, pannable, temporal, clippable };
+    }
     // navigates to the bestTarget document by making sure it is on screen,
     // then it applies the view specs stored in activeItem to
     @action
     static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
-        if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
-            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
-            bestTarget._scrollTop = activeItem.presPinViewScroll;
-        } else if (bestTarget.type === DocumentType.COMPARISON) {
-            bestTarget._clipWidth = activeItem.presPinClipWidth;
-        } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
-            bestTarget._currentTimecode = activeItem.presStartTime;
-        } else {
+        bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+        const { scrollable, pannable, temporal, clippable } = this.pinDataTypes(bestTarget);
+        if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth;
+        if (temporal) bestTarget._currentTimecode = activeItem.presStartTime;
+        if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll;
+        if (pannable) {
             const contentBounds = Cast(activeItem.contentBounds, listSpec('number'));
-            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
             if (contentBounds) {
                 bestTarget._panX = (contentBounds[0] + contentBounds[2]) / 2;
                 bestTarget._panY = (contentBounds[1] + contentBounds[3]) / 2;
@@ -388,6 +392,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return setTimeout(() => (bestTarget._viewTransition = undefined), activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
     }
 
+    /// copies values from the targetDoc (which is the prototype of the pinDoc) to
+    /// reserved fields on the pinDoc so that those values can be restored to the
+    /// target doc when navigating to it.
+    @action
+    static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined) {
+        if (pinProps?.pinWithView) {
+            // If pinWithView option set then update scale and x / y props of slide
+            const bounds = pinProps.pinWithView.bounds;
+            pinDoc.presPinView = true;
+            pinDoc.presPinViewX = bounds.left + bounds.width / 2;
+            pinDoc.presPinViewY = bounds.top + bounds.height / 2;
+            pinDoc.presPinViewScale = pinProps.pinWithView.scale;
+            pinDoc.contentBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
+        }
+        if (pinProps?.pinDocView) {
+            const { scrollable, pannable, temporal, clippable } = this.pinDataTypes(pinDoc);
+            pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable;
+
+            if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop;
+            else if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth;
+            else if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1;
+            else if (pannable) {
+                const panX = NumCast(pinDoc._panX);
+                const panY = NumCast(pinDoc._panY);
+                const pw = NumCast(pinProps.panelWidth);
+                const ph = NumCast(pinProps.panelHeight);
+                const ps = NumCast(pinDoc._viewScale);
+                if (pw && ph && ps) {
+                    pinDoc.contentBounds = new List<number>([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]);
+                }
+                pinDoc.presPinViewX = panX;
+                pinDoc.presPinViewY = panY;
+                pinDoc.presPinViewScale = ps;
+            }
+        }
+    }
+
     /**
      * This method makes sure that cursor navigates to the element that
      * has the option open and last in the group.
@@ -1540,26 +1581,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     }
 
-    @computed get effectDirection(): string {
-        let effect = '';
+    @computed get effectDirection() {
+        // prettier-ignore
         switch (this.activeItem.presEffectDirection) {
-            case 'left':
-                effect = 'Enter from left';
-                break;
-            case 'right':
-                effect = 'Enter from right';
-                break;
-            case 'top':
-                effect = 'Enter from top';
-                break;
-            case 'bottom':
-                effect = 'Enter from bottom';
-                break;
-            default:
-                effect = 'Enter from center';
-                break;
+            case 'left': return 'Enter from left'; 
+            case 'right': return 'Enter from right'; 
+            case 'top':  return'Enter from top';
+            case 'bottom': return 'Enter from bottom';
         }
-        return effect;
+        return 'Enter from center';
     }
 
     @undoBatch
@@ -1582,179 +1612,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
     };
 
-    @computed get presPinViewOptionsDropdown() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 16, filter: 'invert(1)' }} />;
-        return (
-            <>
-                {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : null}
-                <div className="ribbon-doubleButton">
-                    <Tooltip
-                        title={
-                            <>
-                                <div className="dash-tooltip">{activeItem.presPinView ? 'Turn off pin with view' : 'Turn on pin with view'}</div>
-                            </>
-                        }>
-                        <div
-                            className="ribbon-toggle"
-                            style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : '' }}
-                            onClick={() => {
-                                activeItem.presPinView = !activeItem.presPinView;
-                                targetDoc.presPinView = activeItem.presPinView;
-                                if (activeItem.presPinView) {
-                                    if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
-                                        const scroll = targetDoc._scrollTop;
-                                        activeItem.presPinView = true;
-                                        activeItem.presPinViewScroll = scroll;
-                                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                                        activeItem.presStartTime = targetDoc._currentTimecode;
-                                        activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                                    } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
-                                        const x = targetDoc._panX;
-                                        const y = targetDoc._panY;
-                                        const scale = targetDoc._viewScale;
-                                        activeItem.presPinView = true;
-                                        activeItem.presPinViewX = x;
-                                        activeItem.presPinViewY = y;
-                                        activeItem.presPinViewScale = scale;
-                                    } else if (targetDoc.type === DocumentType.COMPARISON) {
-                                        const width = targetDoc._clipWidth;
-                                        activeItem.presPinClipWidth = width;
-                                        activeItem.presPinView = true;
-                                    }
-                                }
-                            }}>
-                            {presPinWithViewIcon}
-                        </div>
-                    </Tooltip>
-                    {activeItem.presPinView ? (
-                        <Tooltip
-                            title={
-                                <>
-                                    <div className="dash-tooltip">{'Update the pinned view with the view of the selected document'}</div>
-                                </>
-                            }>
-                            <div
-                                className="ribbon-button"
-                                onClick={() => {
-                                    if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
-                                        const scroll = targetDoc._scrollTop;
-                                        activeItem.presPinViewScroll = scroll;
-                                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                                        activeItem.presStartTime = targetDoc._currentTimecode;
-                                        activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                                    } else if (targetDoc.type === DocumentType.COMPARISON) {
-                                        const clipWidth = targetDoc._clipWidth;
-                                        activeItem.presPinClipWidth = clipWidth;
-                                    } else {
-                                        const x = targetDoc._panX;
-                                        const y = targetDoc._panY;
-                                        const scale = targetDoc._viewScale;
-                                        activeItem.presPinViewX = x;
-                                        activeItem.presPinViewY = y;
-                                        activeItem.presPinViewScale = scale;
-                                    }
-                                }}>
-                                Update
-                            </div>
-                        </Tooltip>
-                    ) : null}
-                </div>
-            </>
-        );
-    }
-
-    @computed get panOptionsDropdown() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        return (
-            <>
-                {this.panable ? (
-                    <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
-                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                            <div className="presBox-subheading">Pan X</div>
-                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                                <input
-                                    className="presBox-input"
-                                    style={{ textAlign: 'left', width: 50 }}
-                                    type="number"
-                                    value={NumCast(activeItem.presPinViewX)}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                        const val = e.target.value;
-                                        activeItem.presPinViewX = Number(val);
-                                    })}
-                                />
-                            </div>
-                        </div>
-                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                            <div className="presBox-subheading">Pan Y</div>
-                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                                <input
-                                    className="presBox-input"
-                                    style={{ textAlign: 'left', width: 50 }}
-                                    type="number"
-                                    value={NumCast(activeItem.presPinViewY)}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                        const val = e.target.value;
-                                        activeItem.presPinViewY = Number(val);
-                                    })}
-                                />
-                            </div>
-                        </div>
-                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                            <div className="presBox-subheading">Scale</div>
-                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                                <input
-                                    className="presBox-input"
-                                    style={{ textAlign: 'left', width: 50 }}
-                                    type="number"
-                                    value={NumCast(activeItem.presPinViewScale)}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                        const val = e.target.value;
-                                        activeItem.presPinViewScale = Number(val);
-                                    })}
-                                />
-                            </div>
-                        </div>
-                    </div>
-                ) : null}
-            </>
-        );
-    }
-
-    @computed get scrollOptionsDropdown() {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        return (
-            <>
-                {this.scrollable ? (
-                    <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
-                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                            <div className="presBox-subheading">Scroll</div>
-                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                                <input
-                                    className="presBox-input"
-                                    style={{ textAlign: 'left', width: 50 }}
-                                    type="number"
-                                    value={NumCast(activeItem.presPinViewScroll)}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
-                                        const val = e.target.value;
-                                        activeItem.presPinViewScroll = Number(val);
-                                    })}
-                                />
-                            </div>
-                        </div>
-                    </div>
-                ) : null}
-            </>
-        );
-    }
-
     @computed get mediaStopSlides() {
         const activeItem: Doc = this.activeItem;
         const list = this.childDocs.map((doc, i) => {
@@ -2102,42 +1959,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     };
 
     createTemplate = (layout: string, input?: string) => {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        let x = 0;
-        let y = 0;
-        if (activeItem && targetDoc) {
-            x = NumCast(targetDoc.x);
-            y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20;
-        }
-        let doc = undefined;
-        const title = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
-        const subtitle = Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
-        const header = Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
-        const contentTitle = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
-        const content = Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
-        const content1 = Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
-        const content2 = Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
+        const x = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.x) : 0;
+        const y = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.y) + NumCast(this.targetDoc._height) + 20 : 0;
+        const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
+        const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
+        const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
+        const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
+        const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
+        const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
+        const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
+        // prettier-ignore
         switch (layout) {
-            case 'blank':
-                doc = Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x: x, y: y });
-                break;
-            case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
-                break;
-            case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
-                break;
-            case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
-                break;
-            case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
-                break;
-            default:
-                break;
+            case 'blank': return Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x, y });
+            case 'title': return Docs.Create.FreeformDocument([title(), subtitle()], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+            case 'header': return Docs.Create.FreeformDocument([header()], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+            case 'content': return Docs.Create.FreeformDocument([contentTitle(), content()], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x, y });
+            case 'twoColumns': return Docs.Create.FreeformDocument([contentTitle(), content1(), content2()], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x, y })
         }
-        return doc;
     };
 
     // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view)
@@ -2201,42 +2039,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Returns the collection type as a string for headers
      */
-    @computed get stringType(): string {
-        const activeItem: Doc = this.activeItem;
-        const targetDoc: Doc = this.targetDoc;
-        let type: string = '';
-        if (activeItem) {
-            switch (targetDoc.type) {
-                case DocumentType.PDF:
-                    type = 'PDF';
-                    break;
-                case DocumentType.RTF:
-                    type = 'Text node';
-                    break;
-                case DocumentType.COL:
-                    type = 'Collection';
-                    break;
-                case DocumentType.AUDIO:
-                    type = 'Audio';
-                    break;
-                case DocumentType.VID:
-                    type = 'Video';
-                    break;
-                case DocumentType.IMG:
-                    type = 'Image';
-                    break;
-                case DocumentType.WEB:
-                    type = 'Web page';
-                    break;
-                case DocumentType.MAP:
-                    type = 'Map';
-                    break;
-                default:
-                    type = 'Other node';
-                    break;
+    @computed get stringType() {
+        if (this.activeItem) {
+            // prettier-ignore
+            switch (this.targetDoc.type) {
+                case DocumentType.PDF: return 'PDF';
+                case DocumentType.RTF: return 'Text node';
+                case DocumentType.COL: return 'Collection';
+                case DocumentType.AUDIO: return 'Audio';
+                case DocumentType.VID: return 'Video';
+                case DocumentType.IMG: return 'Image';
+                case DocumentType.WEB: return 'Web page';
+                case DocumentType.MAP: return 'Map';
+                default: return 'Other node';
             }
         }
-        return type;
+        return '';
     }
 
     @observable private openActiveColorPicker: boolean = false;
@@ -2845,10 +2663,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             case DocumentType.PDF || DocumentType.RTF || DocumentType.WEB:
                 this.updateList(activeItem.frameList);
                 break;
-            case DocumentType.COL:
-                break;
-            default:
-                break;
         }
     };
 
-- 
cgit v1.2.3-70-g09d2


From 2c5942d76ad6e9b5874b98658b7c5af59cdfa367 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 8 Sep 2022 14:38:38 -0400
Subject: fixed font menu settings to be accurate of current selection.

---
 src/client/documents/Documents.ts                  | 16 ++++--
 src/client/util/CurrentUserUtils.ts                |  4 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 36 ++++---------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 62 +++++++++-------------
 5 files changed, 49 insertions(+), 73 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7111cb233..57a24b304 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1386,11 +1386,17 @@ export namespace DocUtils {
         scripts &&
             Object.keys(scripts).map(key => {
                 if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
-                    doc[key] = ScriptField.MakeScript(
-                        scripts[key],
-                        { dragData: DragManager.DocumentDragData.name, value: 'any', scriptContext: 'any', thisContainer: Doc.name, documentView: Doc.name, heading: Doc.name, checked: 'boolean', containingTreeView: Doc.name },
-                        { _readOnly_: true }
-                    );
+                    doc[key] = ScriptField.MakeScript(scripts[key], {
+                        dragData: DragManager.DocumentDragData.name,
+                        value: 'any',
+                        _readOnly_: 'boolean',
+                        scriptContext: 'any',
+                        thisContainer: Doc.name,
+                        documentView: Doc.name,
+                        heading: Doc.name,
+                        checked: 'boolean',
+                        containingTreeView: Doc.name,
+                    });
                 }
             });
         funcs &&
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 25852c6ce..9d3f19e50 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -623,8 +623,8 @@ export class CurrentUserUtils {
         return [
             { title: "Font",    toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,               ignoreClick: true, scripts: {script: 'setFont(value, _readOnly_)'},
               btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
-            { title: "Size",    toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton,               ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions },
-            { title: "Color",   toolTip: "Font color",           btnType: ButtonType.ColorButton,  icon: "font", ignoreClick: true, scripts: {script: '{ return setFontColor(value, _readOnly_); }'}},
+            { title: "Size",    toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton,               ignoreClick: true, scripts: {script:  '{ return setFontSize(value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions },
+            { title: "Color",   toolTip: "Font color",           btnType: ButtonType.ColorButton,  icon: "font", ignoreClick: true, scripts: {script:  '{ return setFontColor(value, _readOnly_);}'}},
             { title: "Bold",    toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",                    scripts: {onClick: '{ return toggleBold(_readOnly_); }'} },
             { title: "Italic",  toolTip: "Italic (Ctrl+I)",      btnType: ButtonType.ToggleButton, icon: "italic",                  scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} },
             { title: "Under",   toolTip: "Underline (Ctrl+U)",   btnType: ButtonType.ToggleButton, icon: "underline",               scripts: {onClick: '{ return toggleUnderline(_readOnly_);}'} },
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index c72b5ca9b..4a6099fb3 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -576,27 +576,19 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) {
-    SelectionManager.Docs().map(doc => (doc._fontFamily = font));
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    if (checkResult) {
-        return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-    }
-    if (editorView) RichTextMenu.Instance.setFontFamily(font);
-    else Doc.UserDoc().fontFamily = font;
+    if (checkResult) return RichTextMenu.Instance?.fontFamily;
+    font && RichTextMenu.Instance.setFontFamily(font);
 });
 
 ScriptingGlobals.add(function getActiveTextInfo(info: 'family' | 'size' | 'color' | 'highlight') {
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
     const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
+    // prettier-ignore
     switch (info) {
-        case 'family':
-            return style?.activeFamilies[0];
-        case 'size':
-            return style?.activeSizes[0];
-        case 'color':
-            return style?.activeColors[0];
-        case 'highlight':
-            return style?.activeHighlights[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];
     }
 });
 
@@ -621,14 +613,8 @@ ScriptingGlobals.add(function setBulletList(mapStyle: 'bullet' | 'decimal', chec
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-
-    if (checkResult) {
-        return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
-    }
-
-    if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
-    else Doc.UserDoc().fontColor = color;
+    if (checkResult) return RichTextMenu.Instance.fontColor;
+    color && RichTextMenu.Instance.setColor(color);
 });
 
 // toggle: Set overlay status of selected document
@@ -650,14 +636,12 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
         return RichTextMenu.Instance?.fontSize.replace('px', '');
     }
     if (typeof size === 'number') size = size.toString();
     if (size && Number(size).toString() === size) size += 'px';
-    if (editorView) RichTextMenu.Instance.setFontSize(size);
-    else Doc.UserDoc()._fontSize = size;
+    RichTextMenu.Instance.setFontSize(size);
 });
 ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 81ac45521..314696251 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1387,10 +1387,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
         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)) {
+        if (this._editorView) {
             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?.some(mark => mark.type === schema.marks.user_mark) ? [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)] : []),
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 0cbe60c0c..42a204e1d 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -64,6 +64,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         super(props);
         runInAction(() => {
             RichTextMenu.Instance = this;
+            this.updateMenu(undefined, undefined, props);
             this._canFade = false;
             this.Pinned = true;
         });
@@ -103,13 +104,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             return;
         }
         this.view = view;
-        if (!view || !view.hasFocus()) {
-            return;
-        }
         props && (this.editorProps = props);
 
         // Don't do anything if the document/selection didn't change
-        if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return;
+        if (view && view.hasFocus()) {
+            if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return;
+        }
 
         // update active marks
         const activeMarks = this.getActiveMarksOnSelection();
@@ -124,9 +124,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
         this.activeListType = this.getActiveListStyle();
         this._activeAlignment = this.getActiveAlignment();
-        this._activeFontFamily = !activeFamilies.length ? 'Arial' : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
-        this._activeFontSize = !activeSizes.length ? StrCast(this.TextView.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0];
-        this._activeFontColor = !activeColors.length ? 'black' : activeColors.length > 0 ? String(activeColors[0]) : '...';
+        this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document.fontFamily, StrCast(Doc.UserDoc().fontFamily, 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
+        this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0];
+        this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, StrCast(Doc.UserDoc().fontColor, 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
         this.activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
 
         // update link in current selection
@@ -279,28 +279,15 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         this._superscriptActive = false;
 
         activeMarks.forEach(mark => {
+            // prettier-ignore
             switch (mark.name) {
-                case 'noAutoLinkAnchor':
-                    this._noLinkActive = true;
-                    break;
-                case 'strong':
-                    this._boldActive = true;
-                    break;
-                case 'em':
-                    this._italicsActive = true;
-                    break;
-                case 'underline':
-                    this._underlineActive = true;
-                    break;
-                case 'strikethrough':
-                    this._strikethroughActive = true;
-                    break;
-                case 'subscript':
-                    this._subscriptActive = true;
-                    break;
-                case 'superscript':
-                    this._superscriptActive = true;
-                    break;
+                case 'noAutoLinkAnchor': this._noLinkActive = true; break;
+                case 'strong':           this._boldActive = true; break;
+                case 'em':               this._italicsActive = true; break;
+                case 'underline':        this._underlineActive = true; break;
+                case 'strikethrough':    this._strikethroughActive = true; break;
+                case 'subscript':        this._subscriptActive = true; break;
+                case 'superscript':      this._superscriptActive = true; break;
             }
         });
     }
@@ -342,14 +329,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             if (this.view.state.selection.from === 1 && this.view.state.selection.empty && (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === fontSize))) {
                 this.TextView.dataDoc.fontSize = fontSize;
                 this.view.focus();
-                this.updateMenu(this.view, undefined, this.props);
             } else {
                 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);
             }
-        }
+        } else Doc.UserDoc()._fontSize = fontSize;
+        this.updateMenu(this.view, undefined, this.props);
     };
 
     setFontFamily = (family: string) => {
@@ -357,8 +343,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             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);
-        }
+        } else Doc.UserDoc()._fontFamily = family;
+        this.updateMenu(this.view, undefined, this.props);
     };
 
     setHighlight(color: String, view: EditorView, dispatch: any) {
@@ -368,13 +354,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         this.setMark(highlightMark, view.state, dispatch, false);
     }
 
-    setColor(color: String, view: EditorView, dispatch: any) {
+    setColor(color: string) {
         if (this.view) {
-            const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color });
+            const colorMark = this.view.state.schema.mark(this.view.state.schema.marks.pFontColor, { color });
             this.setMark(colorMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(colorMark)), true);
-            view.focus();
-            this.updateMenu(this.view, undefined, this.props);
-        }
+            this.view.focus();
+        } else Doc.UserDoc().fontColor = color;
+        this.updateMenu(this.view, undefined, this.props);
     }
 
     // TODO: remove doesn't work
-- 
cgit v1.2.3-70-g09d2


From d8753c8de6cc9ef9d4bfdb87f11605f0ef80929c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 8 Sep 2022 17:30:17 -0400
Subject: fixed maintaining selections after ctrl-a + setting a font property

---
 src/client/views/nodes/formattedText/RichTextMenu.tsx | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 42a204e1d..c548e211b 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -144,13 +144,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                 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))));
             } else if (dontToggle) {
-                toggleMark(mark.type, mark.attrs)(state, (tx: any) => {
-                    const { from, $from, to, empty } = tx.selection;
-                    if (!tx.doc.rangeHasMark(from, to, mark.type)) {
-                        // hack -- should have just set the mark in the first place
-                        toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch);
-                    } else dispatch(tx);
-                });
+                const tr = state.tr.addMark(state.selection.from, state.selection.to, mark);
+                dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise
             } else {
                 toggleMark(mark.type, mark.attrs)(state, dispatch);
             }
@@ -198,7 +193,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             const state = this.view.state;
             const pos = this.view.state.selection.$from;
             const marks: Mark[] = [...(state.storedMarks ?? [])];
-            if (state.selection.empty) {
+            if (state.storedMarks !== null) {
+            } else if (state.selection.empty) {
                 const ref_node = this.reference_node(pos);
                 marks.push(...(ref_node !== this.view.state.doc && ref_node?.isText ? Array.from(ref_node.marks) : []));
             } else {
-- 
cgit v1.2.3-70-g09d2


From 5e0ababf6d323ff599fa469693d5a6b20e438baf Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 11 Sep 2022 13:18:35 -0400
Subject: fixed crash when selecting ink strokes

---
 src/client/views/nodes/formattedText/RichTextMenu.tsx | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index c548e211b..9faaa0f3a 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -154,7 +154,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     // finds font sizes and families in selection
     getActiveAlignment() {
-        if (this.view && this.TextView.props.isSelected(true)) {
+        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) {
@@ -167,7 +167,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     // finds font sizes and families in selection
     getActiveListStyle() {
-        if (this.view && this.TextView.props.isSelected(true)) {
+        if (this.view && this.TextView?.props.isSelected(true)) {
             const path = (this.view.state.selection.$from as any).path;
             for (let i = 0; i < path.length; i += 3) {
                 if (path[i].type === this.view.state.schema.nodes.ordered_list) {
@@ -189,7 +189,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         const activeSizes: string[] = [];
         const activeColors: string[] = [];
         const activeHighlights: string[] = [];
-        if (this.TextView.props.isSelected(true)) {
+        if (this.TextView?.props.isSelected(true)) {
             const state = this.view.state;
             const pos = this.view.state.selection.$from;
             const marks: Mark[] = [...(state.storedMarks ?? [])];
@@ -222,7 +222,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     //finds all active marks on selection in given group
     getActiveMarksOnSelection() {
         let activeMarks: MarkType[] = [];
-        if (!this.view || !this.TextView.props.isSelected(true)) return activeMarks;
+        if (!this.view || !this.TextView?.props.isSelected(true)) return activeMarks;
 
         const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
         if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
@@ -412,7 +412,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     }
 
     align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => {
-        if (this.TextView.props.isSelected(true)) {
+        if (this.TextView?.props.isSelected(true)) {
             var tr = view.state.tr;
             view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => {
                 if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) {
-- 
cgit v1.2.3-70-g09d2


From a0b595c00111404e9a4fb6b9a926ef22c6e2979b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 11 Sep 2022 13:27:44 -0400
Subject: fixed fontFamily menu for ink strokes

---
 .../views/nodes/formattedText/RichTextMenu.tsx     | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

(limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')

diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9faaa0f3a..6c6d26af5 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -183,13 +183,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     // finds font sizes and families in selection
     getActiveFontStylesOnSelection() {
-        if (!this.view) return { activeFamilies: [], activeSizes: [], activeColors: [], activeHighlights: [] };
-
-        const activeFamilies: string[] = [];
-        const activeSizes: string[] = [];
-        const activeColors: string[] = [];
-        const activeHighlights: string[] = [];
-        if (this.TextView?.props.isSelected(true)) {
+        const activeFamilies = new Set<string>();
+        const activeSizes = new Set<string>();
+        const activeColors = new Set<string>();
+        const activeHighlights = new Set<string>();
+        if (this.view && this.TextView?.props.isSelected(true)) {
             const state = this.view.state;
             const pos = this.view.state.selection.$from;
             const marks: Mark[] = [...(state.storedMarks ?? [])];
@@ -203,13 +201,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                 });
             }
             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));
+                m.type === state.schema.marks.pFontFamily && activeFamilies.add(m.attrs.family);
+                m.type === state.schema.marks.pFontColor && activeColors.add(m.attrs.color);
+                m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize);
+                m.type === state.schema.marks.marker && activeHighlights.add(String(m.attrs.highlight));
             });
         }
-        return { activeFamilies, activeSizes, activeColors, activeHighlights };
+        return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) };
     }
 
     getMarksInSelection(state: EditorState) {
-- 
cgit v1.2.3-70-g09d2

