From 91ac58011296efa00cc081f239913174ce27113f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 12 Oct 2024 19:37:32 -0400
Subject: disabled lightbox showing docCreatorMenu

---
 src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx')

diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
index 6c649bde3..16c016d6c 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
@@ -231,7 +231,8 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
         this._disposers.lightbox = reaction(
             () => LightboxView.LightboxDoc(),
             doc => {
-                doc ? this._shouldDisplay && this.closeMenu() : !this._shouldDisplay && this.openMenu();
+                // NOTE: bcz; commented this out because the doc creator would appear everytime I close out of the lightbox
+                // doc ? this._shouldDisplay && this.closeMenu() : !this._shouldDisplay && this.openMenu();
             }
         );
         //this._disposers.fields = reaction(() => this._dataViz?.axes, cols => this._selectedCols = cols?.map(col => { return {title: col, type: '', desc: ''}}))
-- 
cgit v1.2.3-70-g09d2


From 63f9f9573dab9745d2f570e1917b325a474fdd93 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 28 Oct 2024 20:07:51 -0400
Subject: reworked LabelBox's to be fully editable.  gor rid of singleLine for
 label boxes. made a text_fitBox field to render text with a labelBox. fixed
 styleprovider in freeformview clusters to be untracked() to avoid
 invalidations.  added a default text color to Settings panel.   fixed
 clicking text buttons to not lose focus in labelBox.

---
 package-lock.json                                  |   8 +-
 package.json                                       |   2 +-
 src/client/documents/DocUtils.ts                   |   5 +-
 src/client/documents/Documents.ts                  |  14 +-
 src/client/util/CurrentUserUtils.ts                |   9 +-
 src/client/util/SettingsManager.tsx                |  24 +-
 src/client/views/MarqueeAnnotator.tsx              |   3 +-
 src/client/views/PinFuncs.ts                       |   4 +-
 src/client/views/SidebarAnnos.tsx                  |   4 +-
 src/client/views/StyleProvider.tsx                 |   4 +-
 src/client/views/StyleProviderQuiz.tsx             |   2 +-
 .../views/collections/CollectionCardDeckView.tsx   |   1 -
 .../CollectionFreeFormClusters.ts                  |   6 +-
 .../collections/collectionSchema/SchemaRowBox.tsx  |   2 +-
 src/client/views/global/globalScripts.ts           |  24 +-
 .../views/nodes/DataVizBox/DocCreatorMenu.tsx      |  35 +--
 src/client/views/nodes/DocumentView.tsx            |  12 +-
 src/client/views/nodes/EquationBox.tsx             |   2 +-
 .../views/nodes/FontIconBox/FontIconBox.scss       |   4 +
 src/client/views/nodes/FontIconBox/FontIconBox.tsx |   4 +-
 src/client/views/nodes/LabelBox.scss               |   1 -
 src/client/views/nodes/LabelBox.tsx                | 168 +++++++++----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 259 +++++++++++----------
 .../formattedText/ProsemirrorExampleTransfer.ts    |   4 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     |  71 +++---
 .../views/nodes/formattedText/RichTextRules.ts     |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  14 +-
 src/client/views/pdf/GPTPopup/GPTPopup.tsx         |  23 +-
 src/fields/documentSchemas.ts                      |   7 +-
 29 files changed, 426 insertions(+), 292 deletions(-)

(limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx')

diff --git a/package-lock.json b/package-lock.json
index 4e95fcee0..43dcb1c3f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -72,7 +72,7 @@
                 "body-parser": "^1.20.2",
                 "bootstrap": "^5.3.2",
                 "brotli": "^1.3.3",
-                "browndash-components": "^0.1.50",
+                "browndash-components": "0.1.53",
                 "browser-assert": "^1.2.1",
                 "bson": "^6.2.0",
                 "canvas": "^2.11.2",
@@ -13575,9 +13575,9 @@
             }
         },
         "node_modules/browndash-components": {
-            "version": "0.1.50",
-            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.50.tgz",
-            "integrity": "sha512-8EgU82os8/Tg3gayh+nYXCQZ8P7GGQWnibz0U2RM0sXTbc3BWTSYiC1Sj1VpL7HPkZ1KMGHzWynCo9QKIOWZBg==",
+            "version": "0.1.53",
+            "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.53.tgz",
+            "integrity": "sha512-lBZlXe2u/6d9NlrR+fH+9ncCFGRUxGqJRejpfhbhVmQ4Q4uvP9LlmTuaVQElqys5wGUyK0SuqrLYPGhJthdQLQ==",
             "dependencies": {
                 "@emotion/react": "^11.11.1",
                 "@emotion/styled": "^11.11.0",
diff --git a/package.json b/package.json
index 06179fd7d..63d5e387d 100644
--- a/package.json
+++ b/package.json
@@ -155,7 +155,7 @@
         "body-parser": "^1.20.2",
         "bootstrap": "^5.3.2",
         "brotli": "^1.3.3",
-        "browndash-components": "^0.1.50",
+        "browndash-components": "0.1.53",
         "browser-assert": "^1.2.1",
         "bson": "^6.2.0",
         "canvas": "^2.11.2",
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 19f3c89ef..e3cb5e72c 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -679,10 +679,13 @@ export namespace DocUtils {
                 ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
                 : {
                       _width: width || 200,
-                      _height: 35,
+                      _height: BoolCast(Doc.UserDoc().fitBox) ? 70 : 35,
                       _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
                       _layout_fitWidth: true,
                       _layout_autoHeight: true,
+                      text_fitBox: BoolCast(Doc.UserDoc().fitBox),
+                      text_align: StrCast(Doc.UserDoc().textAlign),
+                      text_fontColor: StrCast(Doc.UserDoc().fontColor),
                   }),
         });
 
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 98e8bc932..b0e1a7545 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -294,7 +294,6 @@ export class DocumentOptions {
     _yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts', false);
     _xPadding?: NUMt = new NumInfo('x padding', false);
     _yPadding?: NUMt = new NumInfo('y padding', false);
-    _singleLine?: boolean; // whether label box is restricted to one line of text
     _createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents
     _columnWidth?: NUMt = new NumInfo('width of table column', false);
     _columnsHideIfEmpty?: BOOLt = new BoolInfo('whether stacking view column headings should be hidden');
@@ -302,10 +301,14 @@ export class DocumentOptions {
     _caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', false, true);
     icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', false, true);
     icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', false, true);
-    _text_fontSize?: string;
-    _text_fontFamily?: string;
-    _text_fontWeight?: string;
-    text_align?: STRt = new StrInfo('horizontal text alignment default');
+    text_fontSize?: string;
+    text_fontFamily?: string;
+    text_fontWeight?: string;
+    text_fitBox?: BOOLt = new BoolInfo("whether text box should be scaled to fit it's containing render box");
+    text_align?: STRt = new StrInfo('horizontal text alignment default', undefined, undefined, ['left', 'center', 'right']);
+    title_align?: STRt = new StrInfo('horizontal title alignment in label box', undefined, undefined, ['left', 'center', 'right']);
+    title_transform?: STRt = new StrInfo('transformation to apply to title in label box (eg., uppercase)', undefined, undefined, ['uppercase', 'lowercase', 'capitalize']);
+    text_transform?: STRt = new StrInfo('transformation to apply to text in text box (eg., uppercase)', undefined, undefined, ['uppercase', 'lowercase', 'capitalize']);
     text_placeholder?: BOOLt = new BoolInfo('makes the text act like a placeholder and automatically select when the text box is selected');
     fontSize?: string;
     _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
@@ -496,7 +499,6 @@ export class DocumentOptions {
     sidebar_type_collection?: string; // collection type of text sidebar
 
     data_dashboards?: List<FieldType>; // list of dashboards used in shareddocs;
-    textTransform?: string;
     letterSpacing?: string;
     iconTemplate?: string; // name of icon template style
     icon_fieldKey?: string; // specifies the icon template to use (e.g., icon_fieldKey='george', then the icon template's name is icon_george; otherwise, the template's name would be icon_<type> where type is the Doc's type(pdf,rich text, etc))
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 30c75c659..b3aa5e145 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -186,7 +186,7 @@ export class CurrentUserUtils {
         const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
         const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
-            layout: LabelBox.LayoutString(fieldKey), textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+            layout: LabelBox.LayoutString(fieldKey), letterSpacing: "unset", _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
         });
         const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG,  _width: 360 / 4, _height: 270 / 4,  _layout_showTitle: "title", ...opts });
         const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
@@ -268,7 +268,7 @@ export class CurrentUserUtils {
             MakeTemplate(Docs.Create.MultirowDocument(
                     [
                         Docs.Create.MulticolumnDocument([], { title: "hero", _height: 200,  isSystem: true }),
-                        Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true,  _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
+                        Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true,  _height: 100, isSystem: true, text_fontFamily: StrCast(Doc.UserDoc().fontFamily), text_fontSize: StrCast(Doc.UserDoc().fontSize) })
                     ], {...opts, title: "Slide View Template"}));
         const plotlyApi = () => {
             let plotly = Doc.MyPublishedDocs.find(fdoc => fdoc.title === "@plotly");
@@ -392,7 +392,7 @@ pie title Minerals in my tap water
             {key: "Trail",      creator: Docs.Create.PresDocument,                          opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, _layout_dontCenter:'xy', dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
             {key: "Tab",        creator: opts => Docs.Create.FreeformDocument([], opts),    opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
             {key: "Slide",      creator: opts => Docs.Create.TreeDocument([], opts),        opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, 
-                                                                                                treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, 
+                                                                                                treeView_HasOverlay: true, text_fontSize: "20px", _layout_autoHeight: true, 
                                                                                                 dropAction:dropActionType.move, treeView_Type: TreeViewType.outline, 
                                                                                                 backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true 
                                                                                             }, funcs: {title: 'this.text?.Text'}},
@@ -731,7 +731,7 @@ pie title Minerals in my tap water
         return [
             { title: "Font",     toolTip: "Font", width: 100,     btnType: ButtonType.DropdownList,                      toolType:"font",     ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'},
               btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
-            { title: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton,              toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 6 },
+            { title: "Font Size",toolTip: "Font size (%size)",    btnType: ButtonType.NumberDropdownButton,              toolType:"fontSize", ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 9 },
             { title: "Color",    toolTip: "Font color (%color)",  btnType: ButtonType.ColorButton,  icon: "font",        toolType:"fontColor",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'} },
             { title: "Highlight",toolTip: "Font highlight",       btnType: ButtonType.ColorButton,  icon: "highlighter", toolType:"highlight",ignoreClick: true,   scripts: {script:  '{ return setFontAttr(this.toolType, value, _readOnly_);}'} },
             { title: "Bold",     toolTip: "Bold (Ctrl+B)",        btnType: ButtonType.ToggleButton, icon: "bold",        toolType:"bold",     ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
@@ -746,6 +746,7 @@ pie title Minerals in my tap water
                 { title: "Center",   toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
                 { title: "Right",    toolTip: "Right align (Cmd-])",  btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             ]},
+            { title: "Fit Box",  toolTip: "Fit text to box",      btnType: ButtonType.ToggleButton, icon: "object-group",toolType:"fitBox",    ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "Elide",    toolTip: "Elide selection",      btnType: ButtonType.ToggleButton, icon: "eye",         toolType:"elide",     ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "Dictate",  toolTip: "Dictate",              btnType: ButtonType.ToggleButton, icon: "microphone",  toolType:"dictation", ignoreClick: true,  scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "NoLink",   toolTip: "Auto Link",            btnType: ButtonType.ToggleButton, icon: "link",        toolType:"noAutoLink", expertMode:true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 9200d68db..33d7d90a8 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
+import { Button, ColorPicker, Colors, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
 import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -45,7 +45,6 @@ export class SettingsManager extends React.Component<object> {
     public closeMgr = action(() => {
         this._isOpen = false;
     });
-    // eslint-disable-next-line react/no-unused-class-component-methods
     public openMgr = action(() => {
         this._isOpen = true;
     });
@@ -354,6 +353,27 @@ export class SettingsManager extends React.Component<object> {
                                     Doc.UserDoc().fontSize = val + 'px';
                                 }}
                             />
+                            <ColorPicker
+                                color={SettingsManager.userColor}
+                                type={Type.PRIM}
+                                defaultPickerType="Classic"
+                                selectedColor={StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY)}
+                                background={SnappingManager.userBackgroundColor}
+                                icon={<FontAwesomeIcon icon="palette" size="lg" />}
+                                tooltip="default text background color"
+                                label="background"
+                                setSelectedColor={value => {
+                                    Doc.UserDoc().textBackgroundColor = value;
+                                    // if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
+                                    // this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+                                }}
+                                setFinalColor={value => {
+                                    Doc.UserDoc().textBackgroundColor = value;
+                                    // this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+                                    // this.colorBatch?.end();
+                                    // this.colorBatch = undefined;
+                                }}
+                            />
                             <Dropdown
                                 items={fontFamilies.map(val => ({
                                     text: val,
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 7266875c5..fa1123a2d 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -34,7 +34,7 @@ export interface MarqueeAnnotatorProps {
     getPageFromScroll?: (top: number) => number;
     finishMarquee: (x?: number, y?: number) => void;
     anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
-    anchorMenuFlashcard?: () => Promise<String>;
+    anchorMenuFlashcard?: () => Promise<string>;
     anchorMenuCrop?: (anchor: Doc | undefined, addCrop: boolean) => Doc | undefined;
     highlightDragSrcColor?: string;
 }
@@ -220,7 +220,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
                   e.preventDefault();
                   e.stopPropagation();
                   let cropRegion: Doc | undefined;
-                  // eslint-disable-next-line no-return-assign
                   const sourceAnchorCreator = () => (cropRegion = this.highlight('', true, undefined, true)); // hyperlink color
                   const targetCreator = (/* annotationOn: Doc | undefined */) => this.props.anchorMenuCrop!(cropRegion, false)!;
                   DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts
index bed473eec..ab02c2d07 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -1,4 +1,4 @@
-import { Doc, DocListCast } from '../../fields/Doc';
+import { Doc, DocListCast, Field } from '../../fields/Doc';
 import { DocData } from '../../fields/DocSymbols';
 import { Copy, Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
@@ -74,7 +74,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
         const fkey = Doc.LayoutFieldKey(targetDoc);
         if (pinProps.pinData.dataview) {
             pinDoc.config_usePath = targetDoc[fkey + '_usePath'];
-            pinDoc.config_data = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
+            pinDoc.config_data = Field.Copy(targetDoc[fkey]);
         }
         if (pinProps.pinData.dataannos) {
             const fieldKey = Doc.LayoutFieldKey(targetDoc);
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 1f3ad8444..87076bf65 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -78,8 +78,8 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
             _height: 50,
             _layout_fitWidth: true,
             _layout_autoHeight: true,
-            _text_fontSize: StrCast(Doc.UserDoc().fontSize),
-            _text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+            text_fontSize: StrCast(Doc.UserDoc().fontSize),
+            text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
         });
         Doc.SetSelectOnLoad(target);
         FormattedTextBox.DontSelectInitialText = true;
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 8859f6464..12c032964 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -251,7 +251,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                 case DocumentType.PRESELEMENT: docColor = docColor || ""; break;
                 case DocumentType.PRES:        docColor = docColor || 'transparent'; break;
                 case DocumentType.FONTICON:    docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
-                case DocumentType.RTF:         docColor = docColor || Colors.LIGHT_GRAY; break;
+                case DocumentType.RTF:         docColor = docColor || StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY); break;
                 case DocumentType.LINK:        docColor = (isAnchor ? docColor : undefined); break;
                 case DocumentType.INK:         docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
                 case DocumentType.EQUATION:    docColor = docColor || 'transparent'; break;
@@ -313,7 +313,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
                         : fieldKey.includes('_inline')  // if doc is an inline document in a text box
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0vw 0vw 0.1vw')}`
-                        : DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline)
+                        :doc.rootDocument !== doc.embedContainer && DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline) and this isn't a simple text template (where the layoutDoc's rootDocument is its embed container)
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
                         : StrCast(doc.layout_boxShadow, '');
             }
diff --git a/src/client/views/StyleProviderQuiz.tsx b/src/client/views/StyleProviderQuiz.tsx
index 1f2ad1485..f07a2c803 100644
--- a/src/client/views/StyleProviderQuiz.tsx
+++ b/src/client/views/StyleProviderQuiz.tsx
@@ -66,7 +66,7 @@ export namespace styleProviderQuiz {
             newCol.zIndex = 1000;
             newCol.forceActive = true;
             newCol.quiz = text;
-            newCol[DocData].textTransform = 'none';
+            newCol[DocData][Doc.LayoutFieldKey(newCol) + '_transform'] = 'none';
             Doc.AddDocToList(img.Document, '_quizBoxes', newCol);
             img.addDocument(newCol);
             //   img._loading = false;
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index beb45f326..0ba6b679c 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -620,7 +620,6 @@ export class CollectionCardView extends CollectionSubView() {
      * Actually renders all the cards
      */
     @computed get renderCards() {
-        console.log('CHILDPw = ' + this.childPanelWidth());
         // Map sorted documents to their rendered components
         return this.sortedDocs.map((doc, index) => {
             const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
index 6ad67a864..8530f7a23 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -1,4 +1,4 @@
-import { action, observable } from 'mobx';
+import { action, observable, untracked } from 'mobx';
 import { CollectionFreeFormView } from '.';
 import { intersectRect } from '../../../../Utils';
 import { Doc, Opt } from '../../../../fields/Doc';
@@ -179,7 +179,9 @@ export class CollectionFreeFormClusters {
     };
 
     styleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
-        if (doc && this.childDocs?.includes(doc))
+        // without untracked, every inquired style property for any Doc will be invalidated if a change is made to the collection's childDocs.
+        // this prevents that by assuming that a Doc is generally always (or never) a member of childDocs - if it's removed or added, then all of its properties get updated anyway.
+        if (doc && untracked(() => this.childDocs)?.includes(doc))
             switch (property.split(':')[0]) {
                 case StyleProp.BackgroundColor:
                     {
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 6ffb0865a..7f519b065 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -188,7 +188,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
                             selectedCells={this.selectedCells}
                             selectedCol={this.selectedCol}
                             setColumnValues={this.setColumnValues}
-                            oneLine={BoolCast(this.schemaDoc?._singleLine)}
+                            oneLine={BoolCast(this.schemaDoc?._schema_singleLine)}
                             menuTarget={this.schemaView.MenuTarget}
                             transform={() => {
                                 const ind = index === this.schemaView.columnKeys.length - 1 ? this.schemaView.columnKeys.length - 3 : index;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 954c79f7d..013b18a97 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -295,10 +295,10 @@ ScriptingGlobals.add(function setTagFilter(tag: string, added: boolean, checkRes
 }, '');
 
 // eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: string | number, checkResult?: boolean) {
+ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize', value: string | number, checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     // prettier-ignore
-    const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => string | undefined; setDoc: () => void;}> = new Map([
+    const map: Map<'font'|'fontColor'|'highlight'|'fontSize', { checkResult: () => string | undefined; setDoc: () => void;}> = new Map([
         ['font', {
             checkResult: () => RichTextMenu.Instance?.fontFamily,
             setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontFamily'),
@@ -311,10 +311,6 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
             checkResult: () => RichTextMenu.Instance?.fontColor,
             setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontColor'),
         }],
-        ['alignment', {
-            checkResult: () => RichTextMenu.Instance?.textAlign,
-            setDoc: () => { value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value.toString() as "center"|"left"|"right"):(Doc.UserDoc().textAlign = value); },
-        }],
         ['fontSize', {
             checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
             setDoc: () => {
@@ -334,7 +330,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
     return undefined;
 });
 
-type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
+type attrname = 'noAutoLink' | 'dictation' | 'fitBox' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
 type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => unknown }];
 
 // eslint-disable-next-line prefer-arrow-callback
@@ -343,14 +339,10 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
     const editorView = textView?.EditorView;
     // prettier-ignore
     const alignments:attrfuncs[] = (['left','right','center','vcent'] as ("left"|"center"|"right"|"vcent")[]).map((where) =>
-        [ where, {      checkResult: () => editorView ? (where === 'vcent' ?  RichTextMenu.Instance?.textVcenter ?? false:
-                                                                             (RichTextMenu.Instance?.textAlign === where)): 
-                                                         where === 'vcent' ?  BoolCast(Doc.UserDoc()._layout_centered):
-                                                                            (Doc.UserDoc().textAlign === where),
-                        toggle: () => { editorView?.state ? (where === 'vcent' ? RichTextMenu.Instance?.vcenterToggle():
-                                                                                 RichTextMenu.Instance?.align(editorView, editorView.dispatch, where)):
-                                                            where === 'vcent' ?  Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered:
-                                                                                (Doc.UserDoc().textAlign = where); }
+        [ where, {      checkResult: () => (where === 'vcent' ?  RichTextMenu.Instance?.textVcenter ?? false:
+                                                             (RichTextMenu.Instance?.textAlign === where)),
+                        toggle: () => { (where === 'vcent' ? RichTextMenu.Instance?.vcenterToggle():
+                                                            RichTextMenu.Instance?.align(editorView, editorView?.dispatch, where)); }
         }]); // prettier-ignore
     // prettier-ignore
     const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
@@ -360,6 +352,8 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
     const attrs:attrfuncs[] = [
         ['dictation', { checkResult: () => !!textView?._recordingDictation,
                         toggle: () => textView && runInAction(() => { textView._recordingDictation = !textView._recordingDictation;} ) }],
+        ['fitBox',    { checkResult: () => RichTextMenu.Instance?.fitBox ?? false,
+                        toggle: () => RichTextMenu.Instance?.toggleFitBox()}],
         ['elide',      { checkResult: () => false,
                         toggle: () => editorView ? RichTextMenu.Instance?.elideSelection(): 0}],
         ['noAutoLink',{ checkResult: () => ((editorView && RichTextMenu.Instance?.noAutoLink) ?? false),
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
index 16c016d6c..7c601185e 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
@@ -245,7 +245,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
     }
 
     updateIcons = (docs: Doc[]) => {
-        console.log('called')
+        console.log('called');
         docs.map(this.getIcon);
     };
 
@@ -416,7 +416,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                 const height = bottom - top;
                 const width = right - left;
                 const doc = !field.title.includes('$$')
-                    ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, _text_fontSize: `${height / 2}` })
+                    ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, text_fontSize: `${height / 2}` })
                     : Docs.Create.ImageDocument('', { _height: height, _width: width, title: field.title.replace(/\$\$/g, ''), x: left, y: top });
                 return doc;
             });
@@ -919,17 +919,17 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
     @action setExpandedView = (info: { icon: ImageField; doc: Doc } | undefined) => {
         if (info) {
             const doc = info.doc;
-            const wrapper: Doc = Docs.Create.FreeformDocument([info.doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''});
-            const newInfo = {icon: new ImageField(''), doc: wrapper}
+            const wrapper: Doc = Docs.Create.FreeformDocument([info.doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: '' });
+            const newInfo = { icon: new ImageField(''), doc: wrapper };
             this._expandedPreview = newInfo;
         } else {
             this._expandedPreview = info;
         }
     };
 
-    get editingWindow(){
+    get editingWindow() {
         const doc = this._expandedPreview?.doc ?? new Doc();
-        const rendered = 
+        const rendered = (
             <div className="docCreatorMenu-expanded-template-preview">
                 <CollectionFreeFormView
                     Document={this._expandedPreview!.doc}
@@ -945,7 +945,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                     removeDocument={returnFalse}
                     PanelWidth={() => this._menuDimensions.width - 10}
                     PanelHeight={() => this._menuDimensions.height - 60}
-                    ScreenToLocalTransform={() => new Transform(-this._pageX,-this._pageY, 1)}
+                    ScreenToLocalTransform={() => new Transform(-this._pageX, -this._pageY, 1)}
                     renderDepth={5}
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={emptyFunction}
@@ -961,14 +961,21 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                     yPadding={0}
                 />
             </div>
-
+        );
 
         return (
             <div className="docCreatorMenu-expanded-template-preview">
-                <div className="top-panel"/>
+                <div className="top-panel" />
                 {rendered}
                 <div className="right-buttons-panel">
-                    <button className="docCreatorMenu-menu-button section-reveal-options top-right" onPointerDown={e => this.setUpButtonClick(e, () => {this._expandedPreview && this.updateIcons(this._suggestedTemplates.slice()); this.setExpandedView(undefined)})}>
+                    <button
+                        className="docCreatorMenu-menu-button section-reveal-options top-right"
+                        onPointerDown={e =>
+                            this.setUpButtonClick(e, () => {
+                                this._expandedPreview && this.updateIcons(this._suggestedTemplates.slice());
+                                this.setExpandedView(undefined);
+                            })
+                        }>
                         <FontAwesomeIcon icon="minimize" />
                     </button>
                     <button className="docCreatorMenu-menu-button section-reveal-options top-right-lower" onPointerDown={e => this.setUpButtonClick(e, () => this._expandedPreview && this._templateDocs.push(this._expandedPreview.doc))}>
@@ -976,7 +983,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
                     </button>
                 </div>
             </div>
-
         );
     }
 
@@ -2260,11 +2266,11 @@ export class FieldUtils {
             title: title,
             x: coord.x,
             y: coord.y,
-            _text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
+            text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
             backgroundColor: opts.backgroundColor ?? '',
             text_fontColor: opts.color,
             contentBold: opts.fontBold,
-            textTransform: opts.fontTransform,
+            text_transform: opts.fontTransform,
             color: opts.color,
             _layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
             borderColor: opts.borderColor,
@@ -2306,7 +2312,7 @@ export class FieldUtils {
     public static CarouselField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, fields: Doc[]) => {
         const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
 
-        const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, _text_fontSize: `${height / 2}` });
+        const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, text_fontSize: `${height / 2}` });
 
         return doc;
     };
@@ -2359,4 +2365,3 @@ export class FieldUtils {
 //         }]
 //     };
 //  }
-
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a343b9a39..eb7f333b9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -880,7 +880,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                           background: this.backgroundBoxColor,
                           opacity: this.opacity,
                           cursor: Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
-                          color: StrCast(this.layoutDoc.color, 'inherit'),
+                          color: StrCast(this.Document._color, 'inherit'),
                           fontFamily: StrCast(this.Document._text_fontFamily, 'inherit'),
                           fontSize: Cast(this.Document._text_fontSize, 'string', null),
                           transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
@@ -981,12 +981,10 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
             type: SpringType.GENTLE,
             ...springMappings.gentle,
         };
-        switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
-            case PresEffect.Expand:     return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Expand} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
-            case PresEffect.Flip:       return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Flip}   springSettings={timingConfig}>{renderDoc}</SpringAnimation>
-            case PresEffect.Rotate:     return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Rotate} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
-            case PresEffect.Bounce:     return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Bounce} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
-            case PresEffect.Roll:       return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Roll}   springSettings={timingConfig}>{renderDoc}</SpringAnimation>
+        const presEffect = StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect));
+        switch (presEffect) {
+            case PresEffect.Expand: case PresEffect.Flip: case PresEffect.Rotate: case PresEffect.Bounce:    
+            case PresEffect.Roll:       return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={presEffect}   springSettings={timingConfig}>{renderDoc}</SpringAnimation>
             // case PresEffect.Fade:    return <SlideEffect doc={root} dir={dir} presEffect={PresEffect.Fade} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
             case PresEffect.Fade:       return <Fade {...effectProps}>{renderDoc}</Fade>
             // keep as preset, doesn't really make sense with spring config
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index fefe25764..290c90d6e 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -122,7 +122,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     width: 'fit-content', // `${100 / scale}%`,
                     height: `${100 / scale}%`,
                     pointerEvents: !this._props.isSelected() ? 'none' : undefined,
-                    fontSize: StrCast(this.layoutDoc._text_fontSize),
+                    fontSize: StrCast(this.Document._text_fontSize),
                 }}
                 onKeyDown={e => e.stopPropagation()}>
                 <EquationEditor ref={this._ref} value={StrCast(this.dataDoc.text, 'x')} spaceBehavesLikeTab onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.scss b/src/client/views/nodes/FontIconBox/FontIconBox.scss
index 2db285910..ab03a2318 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.scss
@@ -10,6 +10,10 @@
         height: 3px !important;
     }
 }
+.fonticonbox {
+    margin: auto;
+    width: 100%;
+}
 .menuButton {
     height: 100%;
     display: flex;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index d4898eb3c..b45774a75 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -262,11 +262,13 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
 
         const script = ScriptCast(this.Document.onClick)?.script;
         const toggleStatus = script?.run({ this: this.Document, value: undefined, _readOnly_: true }).result as boolean;
+
         // Colors
         const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
         const background = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string;
         const items = DocListCast(this.dataDoc.data);
         const selectedItems = items.filter(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result).map(item => StrCast(item.toolType));
+
         return (
             <MultiToggle
                 tooltip={`Toggle ${tooltip}`}
@@ -392,7 +394,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
 
     render() {
         return (
-            <div style={{ margin: 'auto', width: '100%' }} onContextMenu={this.specificContextMenu}>
+            <div className="fonticonbox" onContextMenu={this.specificContextMenu}>
                 {this.renderButton()}
             </div>
         );
diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss
index ca4b3d467..889cdc0ca 100644
--- a/src/client/views/nodes/LabelBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -13,7 +13,6 @@
     height: 100%;
     border-radius: inherit;
     //letter-spacing: 2px; // bcz: doesn't work with LabelBigText
-    text-transform: uppercase;
     overflow: hidden;
     display: inline-block;
     margin: auto;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 94a9541f2..dcf9e1fed 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -1,19 +1,22 @@
 import { Property } from 'csstype';
-import { action, computed, makeObservable } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as textfit from 'textfit';
-import { Field, FieldType } from '../../../fields/Doc';
-import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { Doc, Field } from '../../../fields/Doc';
+import { NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
+import { undoable } from '../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { PinDocView, PinProps } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { FieldView, FieldViewProps } from './FieldView';
 import './LabelBox.scss';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { RichTextMenu } from './formattedText/RichTextMenu';
 
 @observer
 export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -22,7 +25,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     private dropDisposer?: DragManager.DragDropDisposer;
     private _timeout: NodeJS.Timeout | undefined;
-    _divRef: HTMLDivElement | null = null;
+    private _divRef: HTMLDivElement | null = null;
+    private _reaction: IReactionDisposer | undefined;
 
     constructor(props: FieldViewProps) {
         super(props);
@@ -36,21 +40,29 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     };
 
-    @computed get Title() {
-        return Field.toString(this.dataDoc[this.fieldKey] as FieldType) || StrCast(this.Document.title);
-    }
-
-    @computed get backgroundColor() {
-        return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string;
-    }
-
     componentDidMount() {
         this._props.setContentViewBox?.(this);
+        this._reaction = reaction(
+            () => this.Title,
+            () => document.activeElement !== this._divRef && this._forceRerender++
+        );
     }
     componentWillUnMount() {
         this._timeout && clearTimeout(this._timeout);
+        this.setText(this._divRef?.innerText ?? '');
+        this._reaction?.();
     }
 
+    @observable _forceRerender = 0;
+
+    @computed get Title()           { return Field.toString(this.dataDoc[this.fieldKey]); } // prettier-ignore
+    @computed get backgroundColor() { return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string; } // prettier-ignore
+    @computed get boxShadow()       { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) as string; } // prettier-ignore
+
+    setText = undoable((text: string) => {
+        this.dataDoc[this.fieldKey] = text;
+    }, 'set label text');
+
     drop = (/* e: Event, de: DragManager.DropEvent */) => {
         return false;
     };
@@ -82,10 +94,11 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const textfitParams = {
             minFontSize: NumCast(this.layoutDoc._label_minFontSize, 1),
             maxFontSize: NumCast(this.layoutDoc._label_maxFontSize, 100),
-            multiLine: BoolCast(this.layoutDoc._singleLine, true) ? false : true,
-            alignHoriz: true,
+            multiLine: r?.textContent?.includes('\n') ? true : false,
+            // hack because tetFit doesn't support align 'right', but we need mobx to invalidate, so treat null as false and set to right inline
+            alignHoriz: StrCast(this.layoutDoc[this.fieldKey + '_align']) === 'center' ? true : StrCast(this.layoutDoc[this.fieldKey + '_align']) === 'right' ? (null as unknown as boolean) : false,
             alignVert: true,
-            detectMultiLine: true,
+            detectMultiLine: false,
         };
         if (r) {
             if (!r.offsetHeight || !r.offsetWidth) {
@@ -94,65 +107,140 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 this._timeout = setTimeout(() => this.fitTextToBox(r));
                 return textfitParams;
             }
+            r.style.whiteSpace = ''; // textfit sets to nowrap if not multiline, but doesn't reeset if it becomes multiline
+            r.style.textAlign = StrCast(this.layoutDoc[this.fieldKey + '_align']); // textfit doesn't reset textAlign if it has been set to center, so we just set it to what we want
+            r.firstChild instanceof HTMLElement && (r.firstChild.style.textAlign = StrCast(this.layoutDoc[this.fieldKey + '_align']));
             textfit(r, textfitParams);
         }
         return textfitParams;
     };
+    resetCursor = (cranchor?: number) => {
+        if (this._divRef && (cranchor || this._divRef === document.activeElement)) {
+            const range = document.createRange();
+            const anchor = cranchor ?? this._divRef.childNodes.length;
+            const container = cranchor === undefined ? this._divRef : (this._divRef.firstChild?.firstChild ?? this._divRef);
+            range.setStart(container, anchor);
+            range.setEnd(container, anchor);
+            const sel = window.getSelection();
+            sel?.removeAllRanges();
+            sel?.addRange(range);
+        }
+    };
+
+    beforeInput = action((event: InputEvent) => {
+        const spanChild = this._divRef?.firstChild?.firstChild;
+        if (spanChild?.nodeName === '#text' && ['insertLineBreak', 'insertParagraph'].includes(event.inputType)) {
+            event.preventDefault();
+            event.stopPropagation();
+
+            const selection = document.getSelection();
+            if (selection && document.activeElement === event.target) {
+                const text = spanChild.textContent ?? '';
+                const cranchor = selection.anchorNode === this._divRef ? (selection.anchorOffset ? text.length : 0) : selection.anchorOffset;
+                const addReturnHack = text.length <= cranchor && text[text.length - 1] !== '\n' ? '\n\n' : '\n'; // not sure why, but need to add a second carriage return if typing enter at the end of the text
+                const splitText = text.substring(0, cranchor) + addReturnHack + text.substring(cranchor);
+                spanChild.textContent = splitText;
+                this.resetCursor(cranchor + addReturnHack.length);
+            }
+            // const span = document.createElement('span');
+            // span.innerHTML = '&#8203;';
+            // this._divRef!.append(span);
+        }
+    });
+    // .labelBox-mainButton > div > span:nth-child(2) {
+
+    /**
+     * When an IconButton is clicked, it will receive focus.  However, we don't want that since we want or need that since we really want
+     * to maintain focus in the label's editing div (and cursor position).  so this relies on IconButton's having a tabindex set to -1 so that
+     * we can march up the tree from the 'relatedTarget' to determine if the loss of focus was caused by a fonticonbox.   If it is, we then
+     * restore focus
+     * @param e focusout event on the editing div
+     */
+    keepFocus = (e: FocusEvent) => {
+        if (e.relatedTarget instanceof HTMLElement && e.relatedTarget.tabIndex === -1) {
+            for (let ele: HTMLElement | null = e.relatedTarget; ele; ele = (ele as HTMLElement)?.parentElement) {
+                if ((ele as HTMLElement)?.className === 'fonticonbox') {
+                    setTimeout(() => this._divRef?.focus());
+                    break;
+                }
+            }
+        }
+    };
+
     render() {
         TraceMobx();
         const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes
-        const label = this.Title.startsWith('#') ? null : this.Title;
         return (
-            <div key={label?.length} className="labelBox-outerDiv" ref={this.createDropTarget} style={{ boxShadow: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) as string }}>
+            <div className="labelBox-outerDiv" ref={this.createDropTarget} style={{ boxShadow: this.boxShadow }}>
                 <div
                     className="labelBox-mainButton"
                     style={{
                         backgroundColor: this.backgroundColor,
-                        // fontSize: StrCast(this.layoutDoc._text_fontSize),
-                        color: StrCast(this.layoutDoc._color),
-                        fontFamily: StrCast(this.layoutDoc._text_fontFamily) || 'inherit',
+                        color: StrCast(this.layoutDoc._text_fontColor, StrCast(this.layoutDoc._color)),
+                        fontFamily: StrCast(this.layoutDoc._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)) || 'inherit',
                         letterSpacing: StrCast(this.layoutDoc.letterSpacing),
-                        textTransform: StrCast(this.layoutDoc.textTransform) as Property.TextTransform,
+                        textTransform: StrCast(this.layoutDoc[this.fieldKey + '_transform']) as Property.TextTransform,
                         paddingLeft: NumCast(this.layoutDoc._xPadding),
                         paddingRight: NumCast(this.layoutDoc._xPadding),
                         paddingTop: NumCast(this.layoutDoc._yPadding),
                         paddingBottom: NumCast(this.layoutDoc._yPadding),
                         width: this._props.PanelWidth(),
                         height: this._props.PanelHeight(),
-                        whiteSpace: 'multiLine' in boxParams && boxParams.multiLine ? 'pre-wrap' : 'pre',
+                        whiteSpace: boxParams.multiLine ? 'pre-wrap' : 'pre',
                     }}>
                     <div
+                        key={this._forceRerender}
                         style={{
                             width: this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xPadding),
                             height: this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yPadding),
                             outline: 'unset !important',
                         }}
-                        onKeyDown={action(e => {
+                        onKeyDown={e => {
                             e.stopPropagation();
-                        })}
+                        }}
                         onKeyUp={action(e => {
                             e.stopPropagation();
-                            this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
-                            setTimeout(() => this._props.select(false));
+                            const text = this._divRef?.firstChild;
+                            if (text && (text as HTMLElement)?.nodeType === 3) {
+                                this._divRef?.removeChild(text);
+                                this._divRef?.firstChild?.appendChild(text);
+                                this.resetCursor();
+                            }
+                            this.fitTextToBox(this._divRef);
                         })}
+                        onFocus={() => {
+                            RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, this.dataDoc);
+                            this._divRef?.removeEventListener('focusout', this.keepFocus);
+                            this._divRef?.addEventListener('focusout', this.keepFocus);
+                        }}
                         onBlur={() => {
-                            this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
+                            this._divRef?.removeEventListener('focusout', this.keepFocus);
+                            this.setText(this._divRef?.innerText ?? '');
+                            RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
+                            FormattedTextBox.LiveTextUndo?.end();
+                            FormattedTextBox.LiveTextUndo = undefined;
+                        }}
+                        dangerouslySetInnerHTML={{
+                            __html: `<span class="textFitted textFitAlignVert" style="display: inline-block; text-align: center; font-size: 100px; height: 0px;">${this.Title.startsWith('#') ? null : (this.Title ?? '')}</span>`,
                         }}
                         contentEditable={this._props.onClickScript?.() ? undefined : true}
                         ref={r => {
+                            this._divRef?.removeEventListener('beforeinput', this.beforeInput);
                             this._divRef = r;
-                            this.fitTextToBox(r);
-                            if (this._props.isSelected() && this._divRef) {
-                                const range = document.createRange();
-                                range.setStart(this._divRef, this._divRef.childNodes.length);
-                                range.setEnd(this._divRef, this._divRef.childNodes.length);
-                                const sel = window.getSelection();
-                                sel?.removeAllRanges();
-                                sel?.addRange(range);
+                            if (this._divRef) {
+                                this._divRef.addEventListener('beforeinput', this.beforeInput);
+
+                                if (Doc.SelectOnLoad === this.Document) {
+                                    Doc.SelectOnLoad = undefined;
+                                    this._divRef.focus();
+                                }
+                                this.fitTextToBox(this._divRef);
+                                if (this.Title) {
+                                    this.resetCursor();
+                                }
                             }
-                        }}>
-                        {label}
-                    </div>
+                        }}
+                    />
                 </div>
             </div>
         );
@@ -161,9 +249,9 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
 Docs.Prototypes.TemplateMap.set(DocumentType.LABEL, {
     layout: { view: LabelBox, dataField: 'title' },
-    options: { acl: '', _singleLine: true, _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true },
+    options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true, title_align: 'center', title_transform: 'uppercase' },
 });
 Docs.Prototypes.TemplateMap.set(DocumentType.BUTTON, {
     layout: { view: LabelBox, dataField: 'title' },
-    options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true },
+    options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true, title_align: 'center', title_transform: 'uppercase' },
 });
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2aee7b6b3..659cda9dd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -65,6 +65,7 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
 import { RichTextRules } from './RichTextRules';
 import { schema } from './schema_rts';
 import { Property } from 'csstype';
+import { LabelBox } from '../LabelBox';
 // import * as applyDevTools from 'prosemirror-dev-tools';
 
 export interface FormattedTextBoxProps extends FieldViewProps {
@@ -158,6 +159,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     @computed get titleHeight()                { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HeaderMargin) as number || 0; } // prettier-ignore
     @computed get layout_autoHeightMargins()   { return this.titleHeight + NumCast(this.layoutDoc._layout_autoHeightMargins); } // prettier-ignore
     @computed get sidebarKey()                 { return this.fieldKey + '_sidebar'; } // prettier-ignore
+    @computed get isLabel()                    { return this.dataDoc[this.fieldKey+"_fitBox"]; } // prettier-ignore
 
     constructor(props: FormattedTextBoxProps) {
         super(props);
@@ -165,7 +167,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         this._recordingStart = Date.now();
     }
 
-    public get EditorView() { return this._editorView; } // prettier-ignore
+    public get EditorView() { return this.isLabel ? undefined : this._editorView; } // prettier-ignore
 
     // public makeAIFlashcards: () => void = unimplementedFunction;
     public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -175,10 +177,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     // but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing.
     public RemoveLinkFromDoc(linkDoc?: Doc) {
         this.unhighlightSearchTerms();
-        const state = this._editorView?.state;
+        const state = this.EditorView?.state;
         const a1 = DocCast(linkDoc?.link_anchor_1);
         const a2 = DocCast(linkDoc?.link_anchor_2);
-        if (state && a1 && a2 && this._editorView) {
+        if (state && a1 && a2 && this.EditorView) {
             this.removeDocument(a1);
             this.removeDocument(a2);
             let allFoundLinkAnchors: { href: string; title: string; anchorId: string }[] = [];
@@ -188,7 +190,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 return true;
             });
             if (allFoundLinkAnchors.length) {
-                this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allAnchors: allFoundLinkAnchors }));
+                this.EditorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allAnchors: allFoundLinkAnchors }));
 
                 this.setupEditor(this.config, this.fieldKey);
             }
@@ -197,16 +199,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     // removes all the specified link references from the selection.
     // NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references.
     public RemoveAnchorFromSelection(allAnchors: { href: string; title: string; linkId: string; targetId: string }[]) {
-        const state = this._editorView?.state;
-        if (state && this._editorView) {
-            this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors }));
+        const state = this.EditorView?.state;
+        if (state && this.EditorView) {
+            this.EditorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors }));
             this.setupEditor(this.config, this.fieldKey);
         }
     }
 
     getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
         const rootDoc: Doc = Doc.isTemplateDoc(this._props.docViewPath().lastElement()?.Document) ? this.Document : DocCast(this.Document.rootDocument, this.Document);
-        if (!pinProps && this._editorView?.state.selection.empty) return rootDoc;
+        if (!pinProps && this.EditorView?.state.selection.empty) return rootDoc;
         const anchor = Docs.Create.ConfigDocument({ title: StrCast(rootDoc.title), annotationOn: rootDoc });
         this.addDocument(anchor);
         this._finishingLink = true;
@@ -263,7 +265,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 }
             });
         };
-        AnchorMenu.Instance.Highlight = undoable((color: string) => this._editorView?.state && RichTextMenu.Instance?.setFontField(color, 'fontHighlight'), 'highlght text');
+        AnchorMenu.Instance.Highlight = undoable((color: string) => this.EditorView?.state && RichTextMenu.Instance?.setFontField(color, 'fontHighlight'), 'highlght text');
         AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
         AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
         /**
@@ -292,7 +294,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         };
 
         AnchorMenu.Instance.setSelectedText(window.getSelection()?.toString() ?? '');
-        const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
+        const coordsB = this.EditorView!.coordsAtPos(this.EditorView!.state.selection.to);
         this._props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
         let ele: Opt<HTMLDivElement>;
         try {
@@ -309,7 +311,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
 
     leafText = (node: Node) => {
-        if (node.type === this._editorView?.state.schema.nodes.dashField) {
+        if (node.type === this.EditorView?.state.schema.nodes.dashField) {
             const refDoc = !node.attrs.docId ? DocCast(this.Document.rootDocument, this.Document) : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
             const fieldKey = StrCast(node.attrs.fieldKey);
             return (
@@ -320,16 +322,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         return '';
     };
     dispatchTransaction = (tx: Transaction) => {
-        if (this._editorView && !this._editorView.isDestroyed) {
-            const state = this._editorView.state.apply(tx);
-            this._editorView.updateState(state);
+        if (this.EditorView && !this.EditorView.isDestroyed) {
+            const state = this.EditorView.state.apply(tx);
+            this.EditorView.updateState(state);
             this.tryUpdateDoc(false);
         }
     };
 
     tryUpdateDoc = (force: boolean) => {
-        if (this._editorView) {
-            const { state } = this._editorView;
+        if (this.EditorView) {
+            const { state } = this.EditorView;
             const { dataDoc } = this;
             const newText = state.doc.textBetween(0, state.doc.content.size, ' \n', this.leafText);
             const newJson = JSON.stringify(state.toJSON());
@@ -372,7 +374,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                         textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
                         // if we've deleted all the text in a note driven by a template, then restore the template data
                         dataDoc[this.fieldKey] = undefined;
-                        this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse(rtField.Data)));
+                        this.EditorView.updateState(EditorState.fromJSON(this.config, JSON.parse(rtField.Data)));
                         ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, text: newText });
                         unchanged = false;
                     }
@@ -387,7 +389,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 if (jsonstring) {
                     const json = JSON.parse(jsonstring);
                     json.selection = state.toJSON().selection;
-                    this._editorView.updateState(EditorState.fromJSON(this.config, json));
+                    this.EditorView.updateState(EditorState.fromJSON(this.config, json));
                 }
             }
             if (window.getSelection()?.isCollapsed && this._props.rootSelected?.()) {
@@ -407,8 +409,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 linkAnchor = anchor;
             }
         });
-        if (this._editorView && linkTime) {
-            const { state } = this._editorView;
+        if (this.EditorView && linkTime) {
+            const { state } = this.EditorView;
             const node = state.selection.$from.node();
             if (linkAnchor && node.type !== state.schema.nodes.code_block) {
                 const time = linkTime + Date.now() / 1000 - this._recordingStart / 1000;
@@ -416,7 +418,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 const { from } = state.selection;
                 const value = state.schema.nodes.audiotag.create({ timeCode: time, audioId: linkAnchor[Id] });
                 const replaced = state.tr.insert(from - 1, value);
-                this._editorView.dispatch(replaced.setSelection(new TextSelection(replaced.doc.resolve(from + 1))));
+                this.EditorView.dispatch(replaced.setSelection(new TextSelection(replaced.doc.resolve(from + 1))));
             }
         }
     };
@@ -431,16 +433,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                  (Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
                 link.link_relationship === LinkManager.AutoKeywords
         ); // prettier-ignore
-        if (this._editorView?.state.doc.textContent) {
-            let { tr } = this._editorView.state;
-            const { from, to } = this._editorView.state.selection;
-            const { autoLinkAnchor } = this._editorView.state.schema.marks;
+        if (this.EditorView?.state.doc.textContent) {
+            let { tr } = this.EditorView.state;
+            const { from, to } = this.EditorView.state.selection;
+            const { autoLinkAnchor } = this.EditorView.state.schema.marks;
             tr = tr.removeMark(0, tr.doc.content.size, autoLinkAnchor);
             Doc.MyPublishedDocs.filter(term => term.title).forEach(term => {
                 tr = this.hyperlinkTerm(tr, term, newAutoLinks);
             });
             tr = tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)));
-            this._editorView?.dispatch(tr);
+            this.EditorView?.dispatch(tr);
         }
         oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(doc => Doc.DeleteLink?.(doc));
     };
@@ -450,11 +452,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         if (
             !this._props.dontRegisterView && // (this.Document.isTemplateForField === "text" || !this.Document.isTemplateForField) && // only update the title if the data document's data field is changing
             title.startsWith('-') &&
-            this._editorView &&
+            this.EditorView &&
             !this.dataDoc.title_custom &&
             (Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
         ) {
-            let node = this._editorView.state.doc;
+            let node = this.EditorView.state.doc;
             while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
             const str = node.textContent;
             const prefix = '-';
@@ -477,7 +479,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
      */
     hyperlinkTerm = (trIn: Transaction, target: Doc, newAutoLinks: Set<Doc>) => {
         let tr = trIn;
-        const editorView = this._editorView;
+        const editorView = this.EditorView;
         if (editorView && !Doc.AreProtosEqual(target, this.Document)) {
             const autoLinkTerm = Field.toString(target.title as FieldType).replace(/^@/, '');
             let alink: Doc | undefined;
@@ -553,18 +555,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
 
     unhighlightSearchTerms = () => {
-        if (this._editorView) {
-            const { state } = this._editorView;
+        if (this.EditorView) {
+            const { state } = this.EditorView;
             if (state) {
                 const mark = state.schema.mark(state.schema.marks.search_highlight);
                 const activeMark = state.schema.mark(state.schema.marks.search_highlight, { selected: true });
                 const end = state.doc.nodeSize - 2;
-                this._editorView.dispatch(state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
+                this.EditorView.dispatch(state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
             }
         }
     };
     adoptAnnotation = (start: number, end: number, mark: Mark) => {
-        const view = this._editorView!;
+        const view = this.EditorView!;
         const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: ClientUtils.CurrentUserEmail() });
         view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark));
     };
@@ -616,7 +618,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     if (added) {
                         draggedDoc._freeform_fitContentsToBox = true;
                         Doc.SetContainer(draggedDoc, this.Document);
-                        const view = this._editorView!;
+                        const view = this.EditorView!;
                         try {
                             this._inDrop = true;
                             const pos = view.posAtCoords({ left: de.x, top: de.y })?.pos;
@@ -810,7 +812,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
         let target: Element | HTMLElement | null = e.target as HTMLElement; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span>
         while (target && (!(target instanceof HTMLElement) || !target.dataset?.targethrefs)) target = target.parentElement;
-        const editor = this._editorView;
+        const editor = this.EditorView;
         if (editor && target && !(e.nativeEvent instanceof simMouseEvent ? e.nativeEvent.dash : false)) {
             const hrefs = (target.dataset?.targethrefs as string)
                 ?.trim()
@@ -996,8 +998,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const c = this.ProseRef?.getElementsByTagName('img');
         if (c) {
             for (const i of c) {
-                console.log(i);
-
                 // console.log(canvas.toDataURL());
                 // canvas.style.zIndex = '2000000';
                 // document.body.appendChild(canvas);
@@ -1022,8 +1022,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
     animateRes = (resIndex: number, newText: string) => {
         if (resIndex < newText.length) {
-            const marks = this._editorView?.state.storedMarks ?? [];
-            this._editorView?.dispatch(this._editorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(marks));
+            const marks = this.EditorView?.state.storedMarks ?? [];
+            this.EditorView?.dispatch(this.EditorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(marks));
             setTimeout(() => this.animateRes(resIndex + 1, newText), 20);
         }
     };
@@ -1035,8 +1035,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             const res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
             if (!res) {
                 this.animateRes(0, 'Something went wrong.');
-            } else if (this._editorView) {
-                const { dispatch, state } = this._editorView;
+            } else if (this.EditorView) {
+                const { dispatch, state } = this.EditorView;
                 // for no animation, use: dispatch(state.tr.insertText(res));
                 // for animted response starting at end of text, use:
                 dispatch(state.tr.setSelection(Selection.atEnd(state.doc)));
@@ -1057,13 +1057,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
 
     breakupDictation = () => {
-        if (this._editorView && this._recordingDictation) {
+        if (this.EditorView && this._recordingDictation) {
             this.stopDictation(/* true */);
             this._break = true;
-            const { state } = this._editorView;
+            const { state } = this.EditorView;
             const { to } = state.selection;
             const updated = TextSelection.create(state.doc, to, to);
-            this._editorView.dispatch(state.tr.setSelection(updated).insert(to, state.schema.nodes.paragraph.create({})));
+            this.EditorView.dispatch(state.tr.setSelection(updated).insert(to, state.schema.nodes.paragraph.create({})));
             if (this._recordingDictation) {
                 this.recordDictation();
             }
@@ -1082,7 +1082,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     stopDictation = (/* abort: boolean */) => DictationManager.Controls.stop(/* !abort */);
 
     setDictationContent = (value: string) => {
-        if (this._editorView && this._recordingStart) {
+        if (this.EditorView && this._recordingStart) {
             if (this._break) {
                 const textanchorFunc = () => {
                     const tanch = Docs.Create.ConfigDocument({ title: 'dictation anchor' });
@@ -1095,22 +1095,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     const textanchor = Cast(link.link_anchor_1, Doc, null);
                     if (audioanchor) {
                         audioanchor.backgroundColor = 'tan';
-                        const audiotag = this._editorView.state.schema.nodes.audiotag.create({
+                        const audiotag = this.EditorView.state.schema.nodes.audiotag.create({
                             timeCode: NumCast(audioanchor._timecodeToShow),
                             audioId: audioanchor[Id],
                             textId: textanchor[Id],
                         });
                         textanchor[DocData].title = 'dictation:' + audiotag.attrs.timeCode;
-                        const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag);
+                        const tr = this.EditorView.state.tr.insert(this.EditorView.state.doc.content.size, audiotag);
                         const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
-                        this._editorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
+                        this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
                     }
                 }
             }
-            const { from } = this._editorView.state.selection;
+            const { from } = this.EditorView.state.selection;
             this._break = false;
-            const tr = this._editorView.state.tr.insertText(value);
-            this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, tr.doc.content.size)).scrollIntoView());
+            const tr = this.EditorView.state.tr.insertText(value);
+            this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, tr.doc.content.size)).scrollIntoView());
         }
     };
 
@@ -1143,7 +1143,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                     }
                 });
                 this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true; // need to allow permissions for adding links to readonly/augment only documents
-                this._editorView!.dispatch(tr.removeMark(selection.from, selection.to, splitter));
+                this.EditorView!.dispatch(tr.removeMark(selection.from, selection.to, splitter));
                 this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
                 anchor.text = selectedText;
                 anchor.text_html = this._selectionHTML ?? selectedText;
@@ -1177,7 +1177,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             let hadStart = start !== 0;
             frag.forEach((node, index) => {
                 const examinedNode = findAnchorNode(node, editor);
-                if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) {
+                if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this.EditorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this.EditorView?.state.schema.nodes.audiotag)) {
                     nodes.push(examinedNode.node);
                     !hadStart && (start = index + examinedNode.start);
                     hadStart = true;
@@ -1186,13 +1186,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             return { frag: Fragment.fromArray(nodes), start };
         };
         const findAnchorNode = (node: Node, editor: EditorView) => {
-            if (node.type === this._editorView?.state.schema.nodes.audiotag) {
+            if (node.type === this.EditorView?.state.schema.nodes.audiotag) {
                 if (node.attrs.textId === textAnchorId) {
                     return { node, start: 0 };
                 }
                 return undefined;
             }
-            if (node.type === this._editorView?.state.schema.nodes.dashDoc) {
+            if (node.type === this.EditorView?.state.schema.nodes.dashDoc) {
                 if (node.attrs.docId === textAnchorId) {
                     return { node, start: 0 };
                 }
@@ -1208,9 +1208,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         };
 
         this._didScroll = false; // assume we don't need to scroll.  if we do, this will get set to true in handleScrollToSelextion when we dispatch the setSelection below
-        if (this._editorView && textAnchorId) {
-            const { state } = this._editorView;
-            const ret = findAnchorFrag(state.doc.content, this._editorView);
+        if (this.EditorView && textAnchorId) {
+            const { state } = this.EditorView;
+            const ret = findAnchorFrag(state.doc.content, this.EditorView);
 
             const firstChild = ret.frag.childCount ? ret.frag.child(0) : undefined;
             if (ret.start >= 0 && (ret.frag.size || (firstChild && [state.schema.nodes.dashDoc, state.schema.nodes.audioTag].includes(firstChild.type)))) {
@@ -1219,7 +1219,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 if (ret.frag.firstChild) {
                     selection = TextSelection.between(state.doc.resolve(ret.start), state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
                 }
-                this._editorView.dispatch(state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
+                this.EditorView.dispatch(state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
                 const escAnchorId = textAnchorId[0] >= '0' && textAnchorId[0] <= '9' ? `\\3${textAnchorId[0]} ${textAnchorId.substr(1)}` : textAnchorId;
                 addStyleSheetRule(FormattedTextBox._highlightStyleSheet, `${escAnchorId}`, { background: 'yellow', transform: 'scale(3)', 'transform-origin': 'left bottom' });
                 setTimeout(() => {
@@ -1307,15 +1307,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
             },
             incomingValue => {
-                if (this._editorView && this._applyingChange !== this.fieldKey) {
+                if (this.EditorView && this._applyingChange !== this.fieldKey) {
                     if (incomingValue?.data) {
                         const updatedState = JSON.parse(incomingValue.data.Data);
-                        if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) {
-                            this._editorView.updateState(EditorState.fromJSON(this.config, updatedState));
+                        if (JSON.stringify(this.EditorView.state.toJSON()) !== JSON.stringify(updatedState)) {
+                            this.EditorView.updateState(EditorState.fromJSON(this.config, updatedState));
                             this.tryUpdateScrollHeight();
                         }
-                    } else if (this._editorView.state.doc.textContent !== incomingValue?.str) {
-                        selectAll(this._editorView.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
+                    } else if (this.EditorView.state.doc.textContent !== (incomingValue?.str ?? '')) {
+                        selectAll(this.EditorView.state, tx => this.EditorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
                     }
                 }
             },
@@ -1333,9 +1333,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             action(selected => {
                 if (selected && this.dataDoc[this.fieldKey + '_placeholder']) {
                     setTimeout(() => {
-                        selectAll(this._editorView!.state, (tx: Transaction) => {
-                            this._editorView?.dispatch(tx);
-                            this._editorView!.focus();
+                        selectAll(this.EditorView!.state, (tx: Transaction) => {
+                            this.EditorView?.dispatch(tx);
+                            this.EditorView!.focus();
                         });
                     });
                 }
@@ -1343,12 +1343,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 if (FormattedTextBox._globalHighlights.has('Bold Text')) {
                     this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
                 }
-                if (RichTextMenu.Instance?.view === this._editorView && !selected) {
+                if (((RichTextMenu.Instance?.view === this.EditorView && this.EditorView) || this.isLabel) && !selected) {
                     RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
                 }
-                if (this._editorView && selected) {
-                    RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this._props, this.layoutDoc);
-                    setTimeout(this.autoLink, 20);
+                if (selected) {
+                    RichTextMenu.Instance?.updateMenu(this.EditorView, undefined, this._props, this.dataDoc);
+                    this.EditorView && setTimeout(this.autoLink, 20);
                 }
             }),
             { fireImmediately: true }
@@ -1389,7 +1389,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
 
             // DocCast(this.Document.image)._freeform_fitContentsToBox = true;
             // Doc.SetContainer(DocCast(this.Document.image), this.Document);
-            // const view = this._editorView!;
+            // const view = this.EditorView!;
             // try {
             //     this._inDrop = true;
             //     const pos = view.posAtCoords({ left: 0, top: 0 })?.pos;
@@ -1436,7 +1436,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     };
 
     addPdfReference = (pdfAnchorId: string) => {
-        const view = this._editorView!;
+        const view = this.EditorView!;
         if (pdfAnchorId) {
             DocServer.GetRefField(pdfAnchorId).then(pdfAnchor => {
                 if (pdfAnchor instanceof Doc) {
@@ -1487,7 +1487,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const curText = Cast(this.dataDoc[this.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.fieldKey]);
         const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
         if (this.ProseRef) {
-            this._editorView?.destroy();
+            this.EditorView?.destroy();
             this._editorView = new EditorView(this.ProseRef, {
                 state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
                 handleScrollToSelection: editorView => {
@@ -1519,14 +1519,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             if (!rtfField) {
                 const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
                 const startupText = Field.toString(dataDoc[fieldKey] as FieldType);
-                const textAlign = StrCast(this.dataDoc.text_align, StrCast(Doc.UserDoc().textAlign)) || 'left';
+                const textAlign = StrCast(this.dataDoc[this.fieldKey + '_align'], StrCast(Doc.UserDoc().textAlign)) || 'left';
                 if (textAlign !== 'left') {
                     selectAll(this._editorView.state, tr => {
-                        this._editorView?.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
+                        this.EditorView?.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
                     });
                 }
                 if (startupText) {
-                    this._editorView?.dispatch(this._editorView.state.tr.insertText(startupText));
+                    this.EditorView?.dispatch(this.EditorView.state.tr.insertText(startupText));
                 }
                 this.tryUpdateDoc(true);
             }
@@ -1539,56 +1539,57 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             Doc.SetSelectOnLoad(undefined);
             FormattedTextBox.SelectOnLoadChar = '';
         }
-        if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
+        if (this.EditorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
             this._props.select(false);
             if (selLoadChar) {
-                const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
+                const $from = this.EditorView.state.selection.anchor ? this.EditorView.state.doc.resolve(this.EditorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
-                const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
+                const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
                 const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-                const tr1 = this._editorView.state.tr.setStoredMarks(storedMarks);
-                const tr2 = selLoadChar === 'Enter' ? tr1.insert(this._editorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this._editorView.state.doc.content.size - 1);
+                const tr1 = this.EditorView.state.tr.setStoredMarks(storedMarks);
+                const tr2 = selLoadChar === 'Enter' ? tr1.insert(this.EditorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this.EditorView.state.doc.content.size - 1);
                 const tr = tr2.setStoredMarks(storedMarks);
 
-                this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
+                this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
                 this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
             } else if (!FormattedTextBox.DontSelectInitialText) {
                 const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
-                selectAll(this._editorView.state, (tx: Transaction) => {
-                    this._editorView?.dispatch(tx.addStoredMark(mark));
+                selectAll(this.EditorView.state, (tx: Transaction) => {
+                    this.EditorView?.dispatch(tx.addStoredMark(mark));
                 });
+                this.EditorView?.dispatch(this.EditorView.state.tr.setSelection(new TextSelection(this.EditorView.state.doc.resolve(1))));
                 this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
             } else {
-                const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
+                const $from = this.EditorView.state.selection.anchor ? this.EditorView.state.doc.resolve(this.EditorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
-                const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
+                const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
                 const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-                const { tr } = this._editorView.state;
-                this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))).setStoredMarks(storedMarks));
+                const { tr } = this.EditorView.state;
+                this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))).setStoredMarks(storedMarks));
                 this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
             }
         }
         if (selectOnLoad) {
             FormattedTextBox.DontSelectInitialText = false;
-            this._editorView!.focus();
+            this.EditorView!.focus();
         }
         if (this._props.isContentActive()) this.prepareForTyping();
-        if (this._editorView && FormattedTextBox.PasteOnLoad) {
+        if (this.EditorView && FormattedTextBox.PasteOnLoad) {
             const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
             FormattedTextBox.PasteOnLoad = undefined;
             pdfAnchorId && this.addPdfReference(pdfAnchorId);
         }
-        if (this._props.autoFocus) setTimeout(() => this._editorView!.focus()); // not sure why setTimeout is needed but editing dashFieldView's doesn't work without it.
+        if (this._props.autoFocus) setTimeout(() => this.EditorView!.focus()); // not sure why setTimeout is needed but editing dashFieldView's doesn't work without it.
     }
 
     // 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.
     prepareForTyping = () => {
-        if (this._editorView) {
+        if (this.EditorView) {
             const { text, paragraph } = schema.nodes;
-            const selNode = this._editorView.state.selection.$anchor.node();
-            if (this._editorView.state.selection.from === 1 && this._editorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
+            const selNode = this.EditorView.state.selection.$anchor.node();
+            if (this.EditorView.state.selection.from === 1 && this.EditorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
                 const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })];
-                this._editorView.state.selection.empty && this._editorView.state.selection.from === 1 && this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
+                this.EditorView.state.selection.empty && this.EditorView.state.selection.from === 1 && this.EditorView?.dispatch(this.EditorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
             }
         }
     };
@@ -1602,7 +1603,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         FormattedTextBox.LiveTextUndo?.end();
         FormattedTextBox.LiveTextUndo = undefined;
         this.unhighlightSearchTerms();
-        this._editorView?.destroy();
+        this.EditorView?.destroy();
         RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined, undefined);
         FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = 'none');
     }
@@ -1681,26 +1682,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
     };
     setFocus = (ipos?: number) => {
-        const pos = ipos ?? (this._editorView?.state.selection.$from.pos || 1);
-        setTimeout(() => this._editorView?.dispatch(this._editorView.state.tr.setSelection(TextSelection.near(this._editorView.state.doc.resolve(pos)))), 100);
+        const pos = ipos ?? (this.EditorView?.state.selection.$from.pos || 1);
+        setTimeout(() => this.EditorView?.dispatch(this.EditorView.state.tr.setSelection(TextSelection.near(this.EditorView.state.doc.resolve(pos)))), 100);
         setTimeout(() => (this.ProseRef?.children?.[0] as HTMLElement).focus(), 200);
     };
     @action
     onFocused = (e: React.FocusEvent): void => {
-        // applyDevTools.applyDevTools(this._editorView);
+        // applyDevTools.applyDevTools(this.EditorView);
         e.stopPropagation();
     };
 
     onClick = (e: React.MouseEvent): void => {
         if (!this._props.isContentActive()) return;
-        const editorView = this._editorView;
+        const editorView = this.EditorView;
         const editorRoot = editorView?.root instanceof Document ? editorView.root : undefined;
         if (editorView && (!this._forceUncollapse || editorRoot?.getSelection()?.isCollapsed)) {
             // this is a hack to allow the cursor to be placed at the end of a document when the document ends in an inline dash comment.  Apparently Chrome on Windows has a bug/feature which breaks this when clicking after the end of the text.
             const pcords = editorView.posAtCoords({ left: e.clientX, top: e.clientY });
             const node = pcords && editorView.state.doc.nodeAt(pcords.pos); // get what prosemirror thinks the clicked node is (if it's null, then we didn't click on any text)
             if (pcords && node?.type === editorView.state.schema.nodes.dashComment) {
-                this._editorView!.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, pcords.pos + 2)));
+                this.EditorView!.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, pcords.pos + 2)));
                 e.preventDefault();
             }
             if (!node && this.ProseRef) {
@@ -1726,33 +1727,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     hitBulletTargets(x: number, y: number, collapse: boolean, highlightOnly: boolean, selectOrderedList: boolean = false) {
         this._forceUncollapse = false;
         clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
-        const clickPos = this._editorView!.posAtCoords({ left: x, top: y });
+        const clickPos = this.EditorView!.posAtCoords({ left: x, top: y });
         const clickPosVal = clickPos?.pos || 1;
         let olistPos = clickPosVal;
         if (clickPos && olistPos && this._props.rootSelected?.()) {
-            const clickNode = this._editorView?.state.doc.resolve(olistPos).node();
-            const nodeBef = this._editorView?.state.doc.resolve(Math.max(0, olistPos - 1)).node();
-            olistPos = nodeBef?.type === this._editorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos;
-            let $olistPos = this._editorView?.state.doc.resolve(olistPos);
-            let olistNode = (nodeBef !== null || clickNode?.type === this._editorView?.state.schema.nodes.list_item) && olistPos === clickPos?.pos ? clickNode : nodeBef;
-            if (olistNode?.type === this._editorView?.state.schema.nodes.list_item) {
+            const clickNode = this.EditorView?.state.doc.resolve(olistPos).node();
+            const nodeBef = this.EditorView?.state.doc.resolve(Math.max(0, olistPos - 1)).node();
+            olistPos = nodeBef?.type === this.EditorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos;
+            let $olistPos = this.EditorView?.state.doc.resolve(olistPos);
+            let olistNode = (nodeBef !== null || clickNode?.type === this.EditorView?.state.schema.nodes.list_item) && olistPos === clickPos?.pos ? clickNode : nodeBef;
+            if (olistNode?.type === this.EditorView?.state.schema.nodes.list_item) {
                 if ($olistPos && $olistPos.depth) {
                     olistNode = $olistPos.parent;
-                    $olistPos = this._editorView?.state.doc.resolve($olistPos.start($olistPos.depth - 1));
+                    $olistPos = this.EditorView?.state.doc.resolve($olistPos.start($olistPos.depth - 1));
                 }
             }
-            const maxSize = this._editorView?.state.doc.content.size ?? 0;
-            const listPos = this._editorView?.state.doc.resolve(Math.min(maxSize, clickPosVal === olistPos ? clickPosVal + 1 : clickPosVal));
+            const maxSize = this.EditorView?.state.doc.content.size ?? 0;
+            const listPos = this.EditorView?.state.doc.resolve(Math.min(maxSize, clickPosVal === olistPos ? clickPosVal + 1 : clickPosVal));
             const listNode = listPos?.node();
-            if (olistNode && olistNode.type === this._editorView?.state.schema.nodes.ordered_list && listNode) {
+            if (olistNode && olistNode.type === this.EditorView?.state.schema.nodes.ordered_list && listNode) {
                 if (!highlightOnly) {
                     if (selectOrderedList) {
-                        this._editorView.dispatch(this._editorView.state.tr.setSelection(new NodeSelection(selectOrderedList ? $olistPos! : listPos!)));
+                        this.EditorView.dispatch(this.EditorView.state.tr.setSelection(new NodeSelection(selectOrderedList ? $olistPos! : listPos!)));
                     } else {
                         const nodePos = clickPosVal - (olistPos === clickPosVal ? 0 : 1);
-                        if (this._editorView.state.doc.nodeAt(nodePos)) {
-                            const tr = this._editorView.state.tr.setNodeMarkup(nodePos, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility });
-                            this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, nodePos)));
+                        if (this.EditorView.state.doc.nodeAt(nodePos)) {
+                            const tr = this.EditorView.state.tr.setNodeMarkup(nodePos, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility });
+                            this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, nodePos)));
                         }
                     }
                 }
@@ -1772,19 +1773,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     onBlur = (e: React.FocusEvent) => {
         if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
         if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) {
-            const stordMarks = this._editorView?.state.storedMarks?.slice();
+            const stordMarks = this.EditorView?.state.storedMarks?.slice();
             if (!(this.EditorView?.state.selection instanceof NodeSelection)) {
                 this.autoLink();
-                if (this._editorView?.state.tr) {
+                if (this.EditorView?.state.tr) {
                     const tr = stordMarks?.reduce((tr2, m) => {
                         tr2.addStoredMark(m);
                         return tr2;
-                    }, this._editorView.state.tr);
-                    tr && this._editorView.dispatch(tr);
+                    }, this.EditorView.state.tr);
+                    tr && this.EditorView.dispatch(tr);
                 }
             }
         }
-        if (RichTextMenu.Instance?.view === this._editorView && !(this._props.isContentActive() || this._props.rootSelected?.())) {
+        if (RichTextMenu.Instance?.view === this.EditorView && !(this._props.isContentActive() || this._props.rootSelected?.())) {
             RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
         }
 
@@ -1831,7 +1832,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         }
         switch (e.key) {
             case 'Escape':
-                this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
+                this.EditorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
                 (document.activeElement as HTMLElement).blur?.();
                 DocumentView.DeselectAll();
                 RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
@@ -1857,7 +1858,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         this.startUndoTypingBatch();
     };
     ondrop = (e: React.DragEvent) => {
-        this._editorView!.dispatch(updateBullets(this._editorView!.state.tr, this._editorView!.state.schema));
+        this.EditorView?.dispatch(updateBullets(this.EditorView.state.tr, this.EditorView.state.schema));
         e.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
     };
     onScroll = (e: React.UIEvent) => {
@@ -1872,7 +1873,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
     tryUpdateScrollHeight = () => {
         const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0);
         const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
-        if (children && !SnappingManager.IsDragging) {
+        if (this.EditorView && children && !SnappingManager.IsDragging) {
             const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0;
             const toNum = (val: string) => Number(val.replace('px', ''));
             const toHgt = (node: Element): number => {
@@ -2093,7 +2094,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), this._props.xPadding ?? 0, 0, ((this._props.screenXPadding?.() ?? 0) - scrMargin[0]) * this.ScreenToLocalBoxXf().Scale);
         const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, ((this._props.yPadding ?? 0) - scrMargin[1]) * this.ScreenToLocalBoxXf().Scale);
         const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props.  e.g., <FormattedTextBox height='{this._header_height}px' >
-        return styleFromLayout?.height === '0px' ? null : (
+        return this.isLabel ? (
+            <LabelBox {...this._props} />
+        ) : styleFromLayout?.height === '0px' ? null : (
             <div
                 className="formattedTextBox"
                 ref={r => {
@@ -2156,8 +2159,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                                 paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
                                 paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
                                 color: StrCast(this.layoutDoc.text_fontColor),
-                                fontWeight: `${this.layoutDoc.contentBold ? 'bold' : ''}`,
-                                textTransform: `${this.layoutDoc.textTransform}` as Property.TextTransform,
+                                fontWeight: this.layoutDoc.contentBold ? 'bold' : '',
+                                textTransform: StrCast(this.dataDoc[this.fieldKey + '_transform']) as Property.TextTransform,
                             }}
                         />
                     </div>
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 7a8b72be0..3c84e5a10 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -349,7 +349,9 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
                                 dispatch(tx4);
                             }
 
-                            if (view.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
+                            if (view.state.selection.$anchor.depth > 0 &&
+                                view.state.selection.$anchor.node(view.state.selection.$anchor.depth-1).type === schema.nodes.list_item &&
+                                view.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
                                 // if text is selected across list items, then we need to forcibly insert a new line since the splitBlock code joins the two list items.
                                 enter(view.state, dispatch, view, false);
                             }
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 55e6a3a5b..65a415a23 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { lift, toggleMark, wrapIn } from 'prosemirror-commands';
 import { Mark, MarkType } from 'prosemirror-model';
@@ -32,7 +32,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
     private _linkToRef = React.createRef<HTMLInputElement>();
 
-    layoutDoc: Doc | undefined;
+    dataDoc: Doc | undefined;
     @observable public view?: EditorView & { TextView?: FormattedTextBox } = undefined;
     public editorProps: FieldViewProps | AntimodeMenuProps | undefined;
 
@@ -49,6 +49,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     @observable private _activeFontSize: string = '13px';
     @observable private _activeFontFamily: string = '';
+    @observable private _activeFitBox: boolean = false;
     @observable private _activeListType: string = '';
     @observable private _activeAlignment: string = 'left';
 
@@ -64,13 +65,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @observable private currentLink: string | undefined = '';
     @observable private showLinkDropdown: boolean = false;
 
-    _reaction: IReactionDisposer | undefined;
     constructor(props: AntimodeMenuProps) {
         super(props);
         makeObservable(this);
         runInAction(() => {
             RichTextMenu._instance.menu = this;
-            this.updateMenu(undefined, undefined, props, this.layoutDoc);
+            this.updateMenu(undefined, undefined, props, this.dataDoc);
             this._canFade = false;
             this.Pinned = true;
         });
@@ -101,6 +101,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @computed get fontHighlight() {
         return this._activeHighlightColor;
     }
+    @computed get fitBox() {
+        return this._activeFitBox;
+    }
     @computed get fontFamily() {
         return this._activeFontFamily;
     }
@@ -114,26 +117,16 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         return this._activeAlignment;
     }
     @computed get textVcenter() {
-        return BoolCast(this.layoutDoc?._layout_centered);
-    }
-    _disposer: IReactionDisposer | undefined;
-    componentDidMount() {
-        // this._disposer = reaction(
-        //     () => DocumentView.Selected().slice(),
-        //     () => this.updateMenu(undefined, undefined, undefined, undefined)
-        // );
-    }
-    componentWillUnmount() {
-        this._disposer?.();
+        return BoolCast(this.dataDoc?._layout_centered, BoolCast(Doc.UserDoc().layout_centered));
     }
 
     @action
-    public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: FormattedTextBoxProps | AntimodeMenuProps | undefined, layoutDoc: Doc | undefined) {
+    public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: FormattedTextBoxProps | AntimodeMenuProps | undefined, dataDoc: Doc | undefined) {
         if (this._linkToRef.current?.getBoundingClientRect().width) {
             return;
         }
         this.view = view;
-        this.layoutDoc = layoutDoc;
+        this.dataDoc = dataDoc;
         props && (this.editorProps = props);
 
         // Don't do anything if the document/selection didn't change
@@ -147,12 +140,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         const { activeSizes } = active;
         const { activeColors } = active;
         const { activeHighlights } = active;
-        const refDoc = DocumentView.Selected().lastElement()?.layoutDoc ?? Doc.UserDoc();
+        const refDoc = DocumentView.Selected().lastElement()?.dataDoc ?? Doc.UserDoc();
         const refField = (pfx => (pfx ? pfx + '_' : ''))(DocumentView.Selected().lastElement()?.LayoutFieldKey);
         const refVal = (field: string, dflt: string) => StrCast(refDoc[refField + field], StrCast(Doc.UserDoc()[field], dflt));
 
         this._activeListType = this.getActiveListStyle();
         this._activeAlignment = this.getActiveAlignment();
+        this._activeFitBox = BoolCast(refDoc[refField + 'fitBox'], BoolCast(Doc.UserDoc().fitBox));
         this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, refVal('fontFamily', 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
         this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, refVal('fontSize', '10px')) : activeSizes[0];
         this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, refVal('fontColor', 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
@@ -181,7 +175,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                     toggleMark(mark.type, mark.attrs)(state, dispatch);
                 }
             }
-            //    this.updateMenu(this.view, undefined, undefined, this.layoutDoc);
         }
     };
 
@@ -195,8 +188,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                     return node.attrs.align || 'left';
                 }
             }
+        } else if (this.dataDoc) {
+            return StrCast(this.dataDoc.text_align) || 'left';
         }
-        return 'left';
+        return StrCast(Doc.UserDoc().textAlign) || 'left';
     };
 
     // finds font sizes and families in selection
@@ -330,6 +325,17 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.view.focus();
         }
     };
+    toggleFitBox = () => {
+        if (this.dataDoc) {
+            const doc = this.dataDoc;
+            (document.activeElement as HTMLElement)?.blur();
+            doc.text_fitBox = !doc.text_fitBox;
+        } else {
+            Doc.UserDoc().fitBox = !Doc.UserDoc().fitBox;
+            Doc.UserDoc().textAlign = Doc.UserDoc().fitBox ? 'center' : undefined;
+        }
+        this.updateMenu(undefined, undefined, undefined, this.dataDoc);
+    };
     toggleBold = () => {
         if (this.view) {
             const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
@@ -354,11 +360,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         }
     };
 
-    setFontField = (value: string, fontField: 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
-        if (this.TextView && this.view) {
+    setFontField = (value: string, fontField: 'fitBox' | 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
+        if (this.dataDoc) {
+            this.dataDoc[`text_${fontField}`] = value;
+            this.updateMenu(undefined, undefined, undefined, this.dataDoc);
+        } else if (this.TextView && this.view) {
             const { text, paragraph } = this.view.state.schema.nodes;
             const selNode = this.view.state.selection.$anchor.node();
-            if (this.view.state.selection.from === 1 && this.view.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
+            if ((fontField === 'fontSize' && value === '0px') || (this.view.state.selection.from === 1 && this.view.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type))) {
                 this.TextView.dataDoc[this.TextView.fieldKey + `_${fontField}`] = value;
                 this.view.focus();
             }
@@ -369,7 +378,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             this.view.focus();
         } else {
             Doc.UserDoc()[fontField] = value;
-            // this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
         }
     };
 
@@ -395,7 +403,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
                 this.view!.dispatch(tx3);
             });
         this.view.focus();
-        // this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
 
     insertSummarizer(state: EditorState, dispatch: (tr: Transaction) => void) {
@@ -410,10 +417,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     }
 
     vcenterToggle = () => {
-        this.layoutDoc && (this.layoutDoc._layout_centered = !this.layoutDoc._layout_centered);
+        if (this.dataDoc) this.dataDoc._layout_centered = !this.dataDoc._layout_centered;
+        else Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered;
     };
-    align = (view: EditorView, dispatch: (tr: Transaction) => void, alignment: 'left' | 'right' | 'center') => {
-        if (this.RootSelected) {
+    align = (view: EditorView | undefined, dispatch: undefined | ((tr: Transaction) => void), alignment: 'left' | 'right' | 'center') => {
+        if (view && dispatch && this.RootSelected) {
             let { tr } = view.state;
             view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos) => {
                 if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) {
@@ -425,6 +433,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             });
             view.focus();
             dispatch?.(tr);
+        } else {
+            if (this.dataDoc) {
+                this.dataDoc.text_align = alignment;
+            } else Doc.UserDoc().textAlign = alignment;
+            this.updateMenu(undefined, undefined, undefined, this.dataDoc);
         }
     };
 
@@ -702,7 +715,7 @@ interface RichTextMenuPluginProps {
 }
 export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps> {
     update(view: EditorView & { TextView?: FormattedTextBox }, lastState: EditorState | undefined) {
-        RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.layoutDoc);
+        RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.dataDoc);
     }
     render() {
         return null;
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index f58434906..3d14ce4c0 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -121,7 +121,7 @@ export class RichTextRules {
                     annotationOn: textDoc,
                     _layout_fitWidth: true,
                     _layout_autoHeight: true,
-                    _text_fontSize: '9px',
+                    text_fontSize: '9px',
                     title: 'inline comment',
                 });
                 textDocInline.title = inlineFieldKey; // give the annotation its own title
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 5d3672aee..06869717a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -2616,13 +2616,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     createTemplate = (layout: string, input?: string) => {
         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, _text_fontSize: '24pt' });
-        const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _text_fontSize: '16pt' });
-        const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _text_fontSize: '20pt' });
-        const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _text_fontSize: '24pt' });
-        const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _text_fontSize: '14pt' });
-        const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _text_fontSize: '14pt' });
-        const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _text_fontSize: '14pt' });
+        const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, text_fontSize: '24pt' });
+        const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, text_fontSize: '16pt' });
+        const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, text_fontSize: '20pt' });
+        const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, text_fontSize: '24pt' });
+        const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, text_fontSize: '14pt' });
+        const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, text_fontSize: '14pt' });
+        const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, text_fontSize: '14pt' });
         // prettier-ignore
         switch (layout) {
             case 'blank': return Docs.Create.FreeformDocument([], { title: input || 'Blank slide', _width: 400, _height: 225, x, y });
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index d5f5f620c..a7e78bcea 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -572,18 +572,19 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
         <div style={{ height: '80%' }}>
             {this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')}
             <>
-                {!this.cardsDoneLoading ? (
-                    <div className="content-wrapper">
-                        <div className="loading-spinner">
-                            <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
-                            {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
+                {
+                    !this.cardsDoneLoading ? (
+                        <div className="content-wrapper">
+                            <div className="loading-spinner">
+                                <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
+                                {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
+                            </div>
                         </div>
-                    </div>
-                ) : this.mode === GPTPopupMode.CARD ? (
-                    this.cardMenu()
-                ) : (
-                    this.cardActual(this.mode)
-                ) // Call the functions to render JSX
+                    ) : this.mode === GPTPopupMode.CARD ? (
+                        this.cardMenu()
+                    ) : (
+                        this.cardActual(this.mode)
+                    ) // Call the functions to render JSX
                 }
             </>
         </div>
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 335683270..b27816f55 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -48,8 +48,8 @@ export const documentSchema = createSchema({
     _columnsHideIfEmpty: 'boolean', // whether empty stacking view column headings should be hidden
     // _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
     // _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
-    _text_fontSize: 'string',
-    _text_fontFamily: 'string',
+    text_fontSize: 'string',
+    text_fontFamily: 'string',
     _layout_sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
 
     // appearance properties on the data document
@@ -70,7 +70,7 @@ export const documentSchema = createSchema({
     stroke_startMarker: 'string',
     stroke_endMarker: 'string',
     stroke_dash: 'string',
-    textTransform: 'string',
+    text_transform: 'string',
     treeView_Open: 'boolean', //  flag denoting whether the documents sub-tree (contents) is visible or hidden
     treeView_ExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
     treeView_ExpandedViewLock: 'boolean', // whether the expanded view can be changed
@@ -112,5 +112,4 @@ export const collectionSchema = createSchema({
 });
 
 export type Document = makeInterface<[typeof documentSchema]>;
-// eslint-disable-next-line no-redeclare
 export const Document = makeInterface(documentSchema);
-- 
cgit v1.2.3-70-g09d2

