From 2b9c89e54c19a53f0e1978850e8d05ac31186e21 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 21 Mar 2024 17:34:23 -0400
Subject: changed gpt calls from text box to package result into an elision
 node.

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

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

diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index bee0d72e3..b5d0f28d8 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -318,16 +318,20 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         });
     }
 
-    elideSelection = () => {
-        const state = this.view?.state;
-        if (!state) return;
-        if (state.selection.empty) return false;
+    elideSelection = (txstate: EditorState | undefined = undefined, visibility = false) => {
+        const state = txstate ?? this.view?.state;
+        if (!state || state.selection.empty) return false;
         const mark = state.schema.marks.summarize.create();
-        const tr = state.tr;
-        tr.addMark(state.selection.from, state.selection.to, mark);
-        const content = tr.selection.content();
-        const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() });
-        this.view?.dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
+        const tr = state.tr.addMark(state.tr.selection.from, state.selection.to, mark);
+        const text = tr.selection.content();
+        const elideNode = state.schema.nodes.summary.create({ visibility, text, textslice: text.toJSON() });
+        const summary = tr.replaceSelectionWith(elideNode).removeMark(tr.selection.from - 1, tr.selection.from, mark);
+        const expanded = () => {
+            const endOfElidableText = summary.selection.to + text.content.size;
+            const res = summary.insert(summary.selection.to, text.content).insert(endOfElidableText, state.schema.nodes.paragraph.create({}));
+            return res.setSelection(new TextSelection(res.doc.resolve(endOfElidableText + 1)));
+        };
+        this.view?.dispatch?.(visibility ? expanded() : summary);
         return true;
     };
 
-- 
cgit v1.2.3-70-g09d2


From 8f3d3bef02a0c43f7e951cea32aea611eb247846 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 1 Apr 2024 16:11:27 -0400
Subject: fixing setting marks on ordererd_lists

---
 src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts | 7 ++++---
 src/client/views/nodes/formattedText/RichTextMenu.tsx              | 7 ++++---
 2 files changed, 8 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index ab49a53ea..92cc65fc5 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -11,7 +11,6 @@ import { Docs } from '../../../documents/Documents';
 import { RTFMarkup } from '../../../util/RTFMarkup';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { OpenWhere } from '../DocumentView';
-//import { liftListItem, sinkListItem } from './prosemirrorPatches.js';
 import { Doc } from '../../../../fields/Doc';
 import { EditorView } from 'prosemirror-view';
 
@@ -90,7 +89,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         if (!canEdit(state)) return true;
         const ref = state.selection;
         const range = ref.$from.blockRange(ref.$to);
-        const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
+        const marks = state.storedMarks || state.selection.$to.parentOffset ? state.selection.$from.marks() : undefined;
         if (
             !sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
                 const tx3 = updateBullets(tx2, schema);
@@ -103,7 +102,9 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
             const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
             if (
                 !wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
-                    const tx3 = updateBullets(tx2, schema);
+                    const tx25 = updateBullets(tx2, schema);
+                    const ol_node = tx25.doc.nodeAt(range!.start)!;
+                    const tx3 = tx25.setNodeMarkup(range!.start, ol_node.type, { ...ol_node.attrs, ...(marks?.[0]?.type === schema.marks.pFontSize ? { fontSize: marks[0].attrs.fontSize } : {}) });
                     // when promoting to a list, assume list will format things so don't copy the stored marks.
                     marks && tx3.ensureMarks([...marks]);
                     marks && tx3.setStoredMarks([...marks]);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index b5d0f28d8..9b6eb8b8d 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -161,7 +161,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             const liTo = numberRange(state.selection.$to.depth + 1).find(i => state.selection.$to.node(i)?.type === state.schema.nodes.list_item);
             const olFirst = numberRange(state.selection.$from.depth + 1).find(i => state.selection.$from.node(i)?.type === state.schema.nodes.ordered_list);
             const nodeOl = (liFirst && liTo && state.selection.$from.node(liFirst) !== state.selection.$to.node(liTo) && olFirst) || (!liFirst && !liTo && olFirst);
-            const newPos = nodeOl ? numberRange(state.selection.from).findIndex(i => state.doc.nodeAt(i)?.type === state.schema.nodes.ordered_list) : state.selection.from;
+            const fromRange = numberRange(state.selection.from).reverse();
+            const newPos = nodeOl ? fromRange.find(i => state.doc.nodeAt(i)?.type === state.schema.nodes.ordered_list) ?? state.selection.from : state.selection.from;
             const node = (state.selection as NodeSelection).node ?? (newPos >= 0 ? state.doc.nodeAt(newPos) : undefined);
             if (node?.type === schema.nodes.ordered_list) {
                 let attrs = node.attrs;
@@ -370,7 +371,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     setFontSize = (fontSize: string) => {
         if (this.view) {
             if (this.view.state.selection.from === 1 && this.view.state.selection.empty && (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === fontSize))) {
-                this.TextView.dataDoc.fontSize = fontSize;
+                this.TextView.dataDoc[this.TextView.fieldKey + '_fontSize'] = fontSize;
                 this.view.focus();
             } else {
                 const fmark = this.view.state.schema.marks.pFontSize.create({ fontSize });
@@ -385,7 +386,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     setFontFamily = (family: string) => {
         if (this.view) {
-            const fmark = this.view.state.schema.marks.pFontFamily.create({ family: family });
+            const fmark = this.view.state.schema.marks.pFontFamily.create({ family });
             this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
             this.view.focus();
         } else if (SelectionManager.Views.length) {
-- 
cgit v1.2.3-70-g09d2


From 75cbf3ffc1893d02b48ef65ec834e9ae597398bf Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 1 Apr 2024 20:23:24 -0400
Subject: fixed text toggle buttons to highlight based on selection.  enabled
 background colors for text lists.  cleaned up text insertion point setting on
 pointer up.  fixed autoHeight for text boxes with 'auto' nodes like ordered
 lists.

---
 src/client/util/CurrentUserUtils.ts                |   2 +-
 src/client/views/global/globalScripts.ts           |  50 +++---
 .../views/nodes/formattedText/DashFieldView.scss   |   1 +
 .../nodes/formattedText/FormattedTextBox.scss      |  14 ++
 .../views/nodes/formattedText/FormattedTextBox.tsx |  47 ++---
 .../formattedText/ProsemirrorExampleTransfer.ts    |   2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 199 ++++-----------------
 .../views/nodes/formattedText/RichTextRules.ts     |   4 +-
 src/client/views/nodes/formattedText/nodes_rts.ts  |  30 ++--
 9 files changed, 113 insertions(+), 236 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b73111062..38ebc86e7 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -725,7 +725,7 @@ pie title Minerals in my tap water
               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: "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_);}'},funcs: {hidden: "IsNoviceMode()"} },
+            { 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_);}'} },
             { title: "Italic",   toolTip: "Italic (Ctrl+I)",      btnType: ButtonType.ToggleButton, icon: "italic",      toolType:"italics",  ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
             { title: "Under",    toolTip: "Underline (Ctrl+U)",   btnType: ButtonType.ToggleButton, icon: "underline",   toolType:"underline",ignoreClick: true,   scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index dab642499..497ab98d8 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -151,26 +151,26 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
     const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => any; setDoc: () => void;}> = new Map([
         ['font', {
             checkResult: () => RichTextMenu.Instance?.fontFamily,
-            setDoc: () => value && RichTextMenu.Instance.setFontFamily(value),
+            setDoc: () => value && RichTextMenu.Instance?.setFontFamily(value),
         }],
         ['highlight', {
-            checkResult: () =>(selected ?? Doc.UserDoc())._fontHighlight,
-            setDoc: () => value  && RichTextMenu.Instance.setHighlight(value),
+            checkResult: () => RichTextMenu.Instance?.fontHighlight,
+            setDoc: () => value  && RichTextMenu.Instance?.setHighlight(value),
         }],
         ['fontColor', {
             checkResult: () => RichTextMenu.Instance?.fontColor,
-            setDoc: () => value && RichTextMenu.Instance.setColor(value),
+            setDoc: () => value && RichTextMenu.Instance?.setColor(value),
         }],
         ['alignment', {
-            checkResult: () => RichTextMenu.Instance.textAlign,
-            setDoc: () => value && editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, value):(Doc.UserDoc().textAlign = value),
+            checkResult: () => RichTextMenu.Instance?.textAlign,
+            setDoc: () => value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value):(Doc.UserDoc().textAlign = value),
         }],
         ['fontSize', {
             checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
             setDoc: () => {
                 if (typeof value === 'number') value = value.toString();
                 if (value && Number(value).toString() === value) value += 'px';
-                RichTextMenu.Instance.setFontSize(value);
+                RichTextMenu.Instance?.setFontSize(value);
             },
         }],
     ]);
@@ -182,45 +182,45 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
 });
 
 type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
-type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }];
+type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => any }];
 
 ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) {
     const textView = RichTextMenu.Instance?.TextView;
     const editorView = textView?.EditorView;
     // prettier-ignore
     const alignments:attrfuncs[] = (['left','right','center','vcent'] as ("left"|"center"|"right"|"vcent")[]).map((where) =>
-        [ where, {      checkResult: () =>(editorView ? (where === 'vcent' ?  RichTextMenu.Instance.textVcenter:
-                                                                              (RichTextMenu.Instance.textAlign === 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) ? true:false),
-                        toggle: () => (editorView?.state ? (where === 'vcent' ? RichTextMenu.Instance.vcenterToggle(editorView, editorView.dispatch):
-                                                                              RichTextMenu.Instance.align(editorView, editorView.dispatch, where)):
+                        toggle: () => (editorView?.state ? (where === 'vcent' ? RichTextMenu.Instance?.vcenterToggle(editorView, editorView.dispatch):
+                                                                              RichTextMenu.Instance?.align(editorView, editorView.dispatch, where)):
                                                             where === 'vcent' ?  Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered:
                                                                               (Doc.UserDoc().textAlign = where))}]); // prettier-ignore
     // prettier-ignore
     const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
-        [ list, {       checkResult: () => (editorView ? RichTextMenu.Instance.getActiveListStyle() === list:false),
-                        toggle: () => editorView?.state && RichTextMenu.Instance.changeListType(list) }]);
+        [ list, {       checkResult: () => (editorView ? RichTextMenu.Instance?.getActiveListStyle() === list:false),
+                        toggle: () => editorView?.state && RichTextMenu.Instance?.changeListType(list) }]);
     // prettier-ignore
     const attrs:attrfuncs[] = [
         ['dictation', { checkResult: () => textView?._recordingDictation ? true:false,
                         toggle: () => textView && runInAction(() => (textView._recordingDictation = !textView._recordingDictation)) }],
         ['elide',      { checkResult: () => false,
                         toggle: () => editorView ? RichTextMenu.Instance?.elideSelection(): 0}],
-        ['noAutoLink',{ checkResult: () => (editorView ? RichTextMenu.Instance.noAutoLink : false),
+        ['noAutoLink',{ checkResult: () => ((editorView && RichTextMenu.Instance?.noAutoLink) ?? false),
                         toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}],
-        ['bold',      { checkResult: () => (editorView ? RichTextMenu.Instance.bold : (Doc.UserDoc().fontWeight === 'bold') ? true:false),
-                        toggle: editorView ? RichTextMenu.Instance.toggleBold : () => (Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold')}],
-        ['italics',   { checkResult: () => (editorView ? RichTextMenu.Instance.italics : (Doc.UserDoc().fontStyle === 'italics') ? true:false),
-                        toggle: editorView ? RichTextMenu.Instance.toggleItalics : () => (Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics')}],
-        ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance.underline : (Doc.UserDoc().textDecoration === 'underline') ? true:false),
-                        toggle: editorView ? RichTextMenu.Instance.toggleUnderline : () => (Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline') }]]
+        ['bold',      { checkResult: () => (editorView ? RichTextMenu.Instance?.bold??false : (Doc.UserDoc().fontWeight === 'bold') ? true:false),
+                        toggle: editorView ? RichTextMenu.Instance?.toggleBold : () => (Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold')}],
+        ['italics',   { checkResult: () => (editorView ? RichTextMenu.Instance?.italics ?? false : (Doc.UserDoc().fontStyle === 'italics') ? true:false),
+                        toggle: editorView ? RichTextMenu.Instance?.toggleItalics : () => (Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics')}],
+        ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance?.underline ?? false: (Doc.UserDoc().textDecoration === 'underline') ? true:false),
+                        toggle: editorView ? RichTextMenu.Instance?.toggleUnderline : () => (Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline') }]]
 
     const map = new Map(attrs.concat(alignments).concat(listings));
     if (checkResult) {
         return map.get(charStyle)?.checkResult();
     }
-    undoable(() => map.get(charStyle)?.toggle(), 'toggle ' + charStyle)();
+    undoable(() => map.get(charStyle)?.toggle?.(), 'toggle ' + charStyle)();
 });
 
 export function checkInksToGroup() {
@@ -448,10 +448,10 @@ ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
  */
 ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
     SelectionManager.Docs.map(doc => (doc._text_fontFamily = key));
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+        return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc())?.fontFamily);
     }
-    if (editorView) RichTextMenu.Instance.setFontFamily(key);
+    if (editorView) RichTextMenu.Instance?.setFontFamily(key);
     else Doc.UserDoc().fontFamily = key;
 });
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 74eeb014c..d79df4272 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -26,6 +26,7 @@
         display: inline-block;
         font-weight: normal;
         background: rgba(0, 0, 0, 0.1);
+        cursor: default;
     }
     .dashFieldView-fieldSpan {
         min-width: 8px;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 03ff0436b..3dcc45c96 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -273,6 +273,7 @@ footnote::before {
     height: 20px;
     &::before {
         content: '→';
+        cursor: default;
     }
     &:hover {
         background: orange;
@@ -348,6 +349,7 @@ footnote::before {
     touch-action: none;
     span {
         font-family: inherit;
+        background-color: inherit;
     }
 
     blockquote {
@@ -397,6 +399,7 @@ footnote::before {
             font-family: inherit;
         }
         margin-left: 0;
+        background-color: inherit;
     }
     .decimal2-ol {
         counter-reset: deci2;
@@ -406,6 +409,7 @@ footnote::before {
         }
         font-size: smaller;
         padding-left: 2.1em;
+        background-color: inherit;
     }
     .decimal3-ol {
         counter-reset: deci3;
@@ -415,6 +419,7 @@ footnote::before {
         }
         font-size: smaller;
         padding-left: 2.85em;
+        background-color: inherit;
     }
     .decimal4-ol {
         counter-reset: deci4;
@@ -424,6 +429,7 @@ footnote::before {
         }
         font-size: smaller;
         padding-left: 3.85em;
+        background-color: inherit;
     }
     .decimal5-ol {
         counter-reset: deci5;
@@ -432,6 +438,7 @@ footnote::before {
             font-family: inherit;
         }
         font-size: smaller;
+        background-color: inherit;
     }
     .decimal6-ol {
         counter-reset: deci6;
@@ -440,6 +447,7 @@ footnote::before {
             font-family: inherit;
         }
         font-size: smaller;
+        background-color: inherit;
     }
     .decimal7-ol {
         counter-reset: deci7;
@@ -448,6 +456,7 @@ footnote::before {
             font-family: inherit;
         }
         font-size: smaller;
+        background-color: inherit;
     }
 
     .multi1-ol {
@@ -458,6 +467,7 @@ footnote::before {
         }
         margin-left: 0;
         padding-left: 1.2em;
+        background-color: inherit;
     }
     .multi2-ol {
         counter-reset: multi2;
@@ -467,6 +477,7 @@ footnote::before {
         }
         font-size: smaller;
         padding-left: 2em;
+        background-color: inherit;
     }
     .multi3-ol {
         counter-reset: multi3;
@@ -476,6 +487,7 @@ footnote::before {
         }
         font-size: smaller;
         padding-left: 2.85em;
+        background-color: inherit;
     }
     .multi4-ol {
         counter-reset: multi4;
@@ -485,6 +497,7 @@ footnote::before {
         }
         font-size: smaller;
         padding-left: 3.85em;
+        background-color: inherit;
     }
 
     //.bullet:before, .bullet1:before, .bullet2:before, .bullet3:before, .bullet4:before, .bullet5:before { transition: 0.5s; display: inline-block; vertical-align: top; margin-left: -1em; width: 1em; content:" " }
@@ -788,6 +801,7 @@ footnote::before {
         height: 20px;
         &::before {
             content: '→';
+            cursor: default;
         }
         &:hover {
             background: orange;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2e8444379..c2f3a6e4b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -291,7 +291,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             });
         };
         AnchorMenu.Instance.Highlight = undoable((color: string) => {
-            this._editorView?.state && RichTextMenu.Instance.setHighlight(color);
+            this._editorView?.state && RichTextMenu.Instance?.setHighlight(color);
             return undefined;
         }, 'highlght text');
         AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
@@ -1577,32 +1577,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         document.removeEventListener('pointerup', this.onSelectEnd);
     };
     onPointerUp = (e: React.PointerEvent): void => {
-        const editor = this._editorView!;
-        const state = editor?.state;
-        if (!state || !editor || !this.ProseRef?.children[0].className.includes('-focused')) return;
-        if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
-        else if (this._props.isContentActive() && !e.button) {
-            const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
-            let xpos = pcords?.pos || 0;
-            while (xpos > 0 && !state.doc.resolve(xpos).node()?.isTextblock) {
-                xpos = xpos - 1;
-            }
-            let node: any;
-            try {
-                node = state.doc.nodeAt(xpos);
-            } catch (e) {}
-            if (node?.type !== schema.nodes.dashFieldView) {
-                editor.dispatch(state.tr.setSelection(TextSelection.near(state.doc.resolve(xpos))));
-                let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
-                while (target && !target.dataset?.targethrefs) target = target.parentElement;
-                FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
-            } else if (node) {
-                try {
-                    editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(xpos))));
-                } catch (e) {
-                    editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(xpos - 1))));
-                }
-            }
+        const state = this.EditorView?.state;
+        if (state && this.ProseRef?.children[0].className.includes('-focused') && this._props.isContentActive() && !e.button) {
+            if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
+            let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
+            for (let target = e.target as any; target && !target.dataset?.targethrefs; target = target.parentElement);
+            while (target && !target.dataset?.targethrefs) target = target.parentElement;
+            FormattedTextBoxComment.update(this, this.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
         }
     };
     @action
@@ -1788,7 +1769,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
                 (document.activeElement as any).blur?.();
                 SelectionManager.DeselectAll();
-                RichTextMenu.Instance.updateMenu(undefined, undefined, undefined, undefined);
+                RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
                 return;
             case 'Enter':
                 this.insertTime();
@@ -1827,12 +1808,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
         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) {
-            const toNum = (val: string) => Number(val.replace('px', '').replace('auto', '0'));
-            const toHgt = (node: Element) => {
+            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 => {
                 const { height, marginTop, marginBottom } = getComputedStyle(node);
-                return toNum(height) + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
+                const childHeight = height === 'auto' ? getChildrenHeights(Array.from(node.children)) : toNum(height);
+                return childHeight + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
             };
-            const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + toHgt(child), margins);
+            const proseHeight = !this.ProseRef ? 0 : getChildrenHeights(children);
             const scrollHeight = this.ProseRef && proseHeight;
             if (this._props.setHeight && !this._props.suppressSetHeight && scrollHeight && !this._props.dontRegisterView) {
                 // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 92cc65fc5..ec8879487 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -104,7 +104,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
                 !wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
                     const tx25 = updateBullets(tx2, schema);
                     const ol_node = tx25.doc.nodeAt(range!.start)!;
-                    const tx3 = tx25.setNodeMarkup(range!.start, ol_node.type, { ...ol_node.attrs, ...(marks?.[0]?.type === schema.marks.pFontSize ? { fontSize: marks[0].attrs.fontSize } : {}) });
+                    const tx3 = tx25.setNodeMarkup(range!.start, ol_node.type, ol_node.attrs, marks);
                     // when promoting to a list, assume list will format things so don't copy the stored marks.
                     marks && tx3.ensureMarks([...marks]);
                     marks && tx3.setStoredMarks([...marks]);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9b6eb8b8d..9c282a1d2 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -27,7 +27,10 @@ const { toggleMark } = require('prosemirror-commands');
 
 @observer
 export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
-    @observable static Instance: RichTextMenu;
+    static _instance: { menu: RichTextMenu | undefined } = observable({ menu: undefined });
+    static get Instance() {
+        return RichTextMenu._instance?.menu;
+    }
     public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
     private _linkToRef = React.createRef<HTMLInputElement>();
 
@@ -67,7 +70,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     constructor(props: AntimodeMenuProps) {
         super(props);
         makeObservable(this);
-        RichTextMenu.Instance = this;
+        RichTextMenu._instance.menu = this;
         this.updateMenu(undefined, undefined, props, this.layoutDoc);
         this._canFade = false;
         this.Pinned = true;
@@ -131,11 +134,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return;
         }
 
-        // update active marks
-        const activeMarks = this.getActiveMarksOnSelection();
-        this.setActiveMarkButtons(activeMarks);
-
-        // update active font family and size
+        this.setActiveMarkButtons(this.getActiveMarksOnSelection());
         const active = this.getActiveFontStylesOnSelection();
         const activeFamilies = active.activeFamilies;
         const activeSizes = active.activeSizes;
@@ -164,15 +163,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
             const fromRange = numberRange(state.selection.from).reverse();
             const newPos = nodeOl ? fromRange.find(i => state.doc.nodeAt(i)?.type === state.schema.nodes.ordered_list) ?? state.selection.from : state.selection.from;
             const node = (state.selection as NodeSelection).node ?? (newPos >= 0 ? state.doc.nodeAt(newPos) : undefined);
-            if (node?.type === schema.nodes.ordered_list) {
-                let attrs = node.attrs;
-                if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, fontFamily: mark.attrs.family };
-                if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: mark.attrs.fontSize };
-                if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color };
-                const tr = updateBullets(state.tr.setNodeMarkup(newPos, node.type, attrs), state.schema);
-                dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to))));
-            }
-            {
+            if (node?.type === schema.nodes.ordered_list || node?.type === schema.nodes.list_item) {
+                const hasMark = node.marks.some(m => m.type === mark.type);
+                const otherMarks = node.marks.filter(m => m.type !== mark.type);
+                const addAnyway = node.marks.filter(m => m.type === mark.type && Object.keys(m.attrs).some(akey => m.attrs[akey] !== mark.attrs[akey]));
+                const markup = state.tr.setNodeMarkup(newPos, node.type, node.attrs, hasMark && !addAnyway ? otherMarks : [...otherMarks, mark]);
+                dispatch(updateBullets(markup, state.schema));
+            } else {
                 const state = this.view?.state;
                 const tr = this.view?.state.tr;
                 if (tr && state) {
@@ -225,11 +222,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         if (this.view && this.TextView?._props.rootSelected?.()) {
             const state = this.view.state;
             const pos = this.view.state.selection.$from;
-            const marks: Mark[] = [...(state.storedMarks ?? [])];
+            var marks: Mark[] = [...(state.storedMarks ?? [])];
             if (state.storedMarks !== null) {
             } else if (state.selection.empty) {
-                const ref_node = this.reference_node(pos);
-                marks.push(...(ref_node !== this.view.state.doc && ref_node?.isText ? Array.from(ref_node.marks) : []));
+                for (let i = 0; i <= pos.depth; i++) {
+                    marks = [...Array.from(pos.node(i).marks), ...this.view.state.selection.$anchor.marks(), ...marks];
+                }
             } else {
                 state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
                     node.marks?.filter(mark => !mark.isInSet(marks)).map(mark => marks.push(mark));
@@ -256,41 +254,26 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     //finds all active marks on selection in given group
     getActiveMarksOnSelection() {
-        let activeMarks: MarkType[] = [];
-        if (!this.view || !this.TextView?._props.rootSelected?.()) return activeMarks;
+        if (!this.view || !this.TextView?._props.rootSelected?.()) return [] as MarkType[];
 
-        const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
-        if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
-        //current selection
-        const { empty, ranges, $to } = this.view.state.selection as TextSelection;
         const state = this.view.state;
-        if (!empty) {
-            activeMarks = markGroup.filter(mark => {
-                const has = false;
-                for (let i = 0; !has && i < ranges.length; i++) {
-                    return state.doc.rangeHasMark(ranges[i].$from.pos, ranges[i].$to.pos, mark);
-                }
-                return false;
-            });
-        } else {
-            const pos = this.view.state.selection.$from;
-            const ref_node: ProsNode | null = this.reference_node(pos);
-            if (ref_node !== null && ref_node !== this.view.state.doc) {
-                if (ref_node.isText) {
-                } else {
-                    return [];
-                }
-                activeMarks = markGroup.filter(mark_type => {
-                    // if (mark_type === state.schema.marks.pFontSize) {
-                    //     return mark.isINSet
-                    //     ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
-                    // }
-                    const mark = state.schema.mark(mark_type);
-                    return mark.isInSet(ref_node.marks);
-                });
+        var marks: Mark[] = [...(state.storedMarks ?? [])];
+        const pos = this.view.state.selection.$from;
+        if (state.storedMarks !== null) {
+        } else if (state.selection.empty) {
+            for (let i = 0; i <= pos.depth; i++) {
+                marks = [...Array.from(pos.node(i).marks), ...this.view.state.selection.$anchor.marks(), ...marks];
             }
+        } else {
+            state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => {
+                node.marks?.filter(mark => !mark.isInSet(marks)).map(mark => marks.push(mark));
+            });
         }
-        return activeMarks;
+        const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
+        return markGroup.filter(mark_type => {
+            const mark = state.schema.mark(mark_type);
+            return mark.isInSet(marks);
+        });
     }
 
     @action
@@ -418,7 +401,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     // TODO: remove doesn't work
     // remove all node type and apply the passed-in one to the selected text
     changeListType = (mapStyle: string) => {
-        const active = this.view?.state && RichTextMenu.Instance.getActiveListStyle();
+        const active = this.view?.state && RichTextMenu.Instance?.getActiveListStyle();
         const nodeType = this.view?.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? '' : mapStyle });
         if (!this.view || nodeType?.attrs.mapStyle === '') return;
 
@@ -566,7 +549,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     // todo: add brushes to brushMap to save with a style name
     onBrushNameKeyPress = (e: React.KeyboardEvent) => {
         if (e.key === 'Enter') {
-            RichTextMenu.Instance.brushMarks && RichTextMenu.Instance._brushMap.set(this._brushNameRef.current!.value, RichTextMenu.Instance.brushMarks);
+            RichTextMenu.Instance?.brushMarks && RichTextMenu.Instance?._brushMap.set(this._brushNameRef.current!.value, RichTextMenu.Instance.brushMarks);
             this._brushNameRef.current!.style.background = 'lightGray';
         }
     };
@@ -574,7 +557,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     @action
     clearBrush() {
-        RichTextMenu.Instance.brushMarks = new Set();
+        RichTextMenu.Instance && (RichTextMenu.Instance.brushMarks = new Set());
     }
 
     @action
@@ -710,118 +693,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         }
     };
 
-    linkExtend($start: ResolvedPos, href: string) {
-        const mark = this.view!.state.schema.marks.linkAnchor;
-
-        let startIndex = $start.index();
-        let endIndex = $start.indexAfter();
-
-        while (startIndex > 0 && $start.parent.child(startIndex - 1).marks.filter(m => m.type === mark && m.attrs.allAnchors.find((item: { href: string }) => item.href === href)).length) startIndex--;
-        while (endIndex < $start.parent.childCount && $start.parent.child(endIndex).marks.filter(m => m.type === mark && m.attrs.allAnchors.find((item: { href: string }) => item.href === href)).length) endIndex++;
-
-        let startPos = $start.start();
-        let endPos = startPos;
-        for (let i = 0; i < endIndex; i++) {
-            const size = $start.parent.child(i).nodeSize;
-            if (i < startIndex) startPos += size;
-            endPos += size;
-        }
-        return { from: startPos, to: endPos };
-    }
-
-    reference_node(pos: ResolvedPos): ProsNode | null {
-        if (!this.view) return null;
-
-        let ref_node: ProsNode = this.view.state.doc;
-        if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) {
-            ref_node = pos.nodeBefore;
-        }
-        if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) {
-            if (!pos.nodeBefore || this.view.state.selection.$from.pos !== this.view.state.selection.$to.pos) {
-                ref_node = pos.nodeAfter;
-            }
-        }
-        if (!ref_node && pos.pos > 0) {
-            let skip = false;
-            for (let i: number = pos.pos - 1; i > 0; i--) {
-                this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode) => {
-                    if (node.isLeaf && !skip) {
-                        ref_node = node;
-                        skip = true;
-                    }
-                });
-            }
-        }
-        if (!ref_node.isLeaf && ref_node.childCount > 0) {
-            ref_node = ref_node.child(0);
-        }
-        return ref_node;
-    }
-
     render() {
         return null;
-        // TraceMobx();
-        // const row1 = <div className="antimodeMenu-row" key="row 1" style={{ display: this.collapsed ? "none" : undefined }}>{[
-        //     //!this.collapsed ? this.getDragger() : (null),
-        //     // !this.Pinned ? (null) : <div key="frag1"> {[
-        //     //     this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
-        //     //     this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
-        //     //     this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
-        //     //     this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
-        //     //     this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
-        //     //     this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
-        //     //     <div className="richTextMenu-divider" key="divider" />
-        //     // ]}</div>,
-        //     this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
-        //     this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
-        //     this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
-        //     this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
-        //     this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
-        //     this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
-        //     this.createColorButton(),
-        //     this.createHighlighterButton(),
-        //     this.createLinkButton(),
-        //     this.createBrushButton(),
-        //     <div className="collectionMenu-divider" key="divider 2" />,
-        //     this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft),
-        //     this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter),
-        //     this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight),
-        //     this.createButton("indent", "Inset More", undefined, this.insetParagraph),
-        //     this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph),
-        //     this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph),
-        //     this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph),
-        // ]}</div>;
-
-        // const row2 = <div className="antimodeMenu-row row-2" key="row2">
-        //     {this.collapsed ? this.getDragger() : (null)}
-        //     <div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}>
-        //         <div className="collectionMenu-divider" key="divider 3" />
-        //         {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
-        //             this.activeFontSize = val;
-        //             SelectionManager.Views.map(dv => dv.Document._text_fontSize = val);
-        //         })),
-        //         this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
-        //             this.activeFontFamily = val;
-        //             SelectionManager.Views.map(dv => dv.Document._text_fontFamily = val);
-        //         })),
-        //         <div className="collectionMenu-divider" key="divider 4" />,
-        //         this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
-        //         this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
-        //         this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
-        //         this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule)
-        //         ]}
-        //     </div>
-        //     {/* <div key="collapser">
-        //         {<div key="collapser">
-        //             <button className="antimodeMenu-button" key="collapse menu" title="Collapse menu" onClick={this.toggleCollapse} style={{ backgroundColor: this.collapsed ? "#121212" : "", width: 25 }}>
-        //                 <FontAwesomeIcon icon="chevron-left" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.3s", transform: `rotate(${this.collapsed ? 180 : 0}deg)` }} />
-        //             </button>
-        //         </div> }
-        //         <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}>
-        //             <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
-        //         </button>
-        //     </div> */}
-        // </div>;
     }
 }
 
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index e8cf9e992..42665830f 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -246,7 +246,7 @@ export class RichTextRules {
             // activate a style by name using prefix '%<color name>'
             new InputRule(new RegExp(/%[a-zA-Z_]+$/), (state, match, start, end) => {
                 const color = match[0].substring(1, match[0].length);
-                const marks = RichTextMenu.Instance._brushMap.get(color);
+                const marks = RichTextMenu.Instance?._brushMap.get(color);
 
                 if (
                     DocListCast((Doc.UserDoc().template_notes as Doc).data)
@@ -367,7 +367,7 @@ export class RichTextRules {
                     if (count) {
                         const tr = this.TextBox.EditorView?.state.tr.insertText(' ' + (gptval as string));
                         tr && this.TextBox.EditorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(end + 2), tr.doc.resolve(end + 2 + (gptval as string).length))));
-                        RichTextMenu.Instance.elideSelection(this.TextBox.EditorView?.state, true);
+                        RichTextMenu.Instance?.elideSelection(this.TextBox.EditorView?.state, true);
                     }
                     count++;
                 });
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index cab3a6ef5..ceafc3ba1 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -24,6 +24,7 @@ export const nodes: { [index: string]: NodeSpec } = {
     // :: NodeSpec The top level document node.
     doc: {
         content: 'block+',
+        marks: '_',
     },
 
     paragraph: ParagraphNodeSpec,
@@ -331,12 +332,10 @@ export const nodes: { [index: string]: NodeSpec } = {
         ...orderedList,
         content: 'list_item+',
         group: 'block',
+        marks: '_',
         attrs: {
             bulletStyle: { default: 0 },
-            mapStyle: { default: 'decimal' }, // "decimal", "multi", "bullet"
-            fontColor: { default: 'inherit' },
-            fontSize: { default: undefined },
-            fontFamily: { default: undefined },
+            mapStyle: { default: 'decimal' }, // "decimal", "multi", "bullet",
             visibility: { default: true },
             indent: { default: undefined },
         },
@@ -376,9 +375,10 @@ export const nodes: { [index: string]: NodeSpec } = {
         ],
         toDOM(node: Node) {
             const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : '';
-            const fsize = node.attrs.fontSize ? `font-size: ${node.attrs.fontSize};` : '';
-            const ffam = node.attrs.fontFamily ? `font-family:${node.attrs.fontFamily};` : '';
-            const fcol = node.attrs.fontColor ? `color: ${node.attrs.fontColor};` : '';
+            const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type.name === 'marker')?.attrs.highlight);
+            const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontSize')?.attrs.fontSize);
+            const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontFamily')?.attrs.family);
+            const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontColor')?.attrs.color);
             const marg = node.attrs.indent ? `margin-left: ${node.attrs.indent};` : '';
             if (node.attrs.mapStyle === 'bullet') {
                 return [
@@ -386,7 +386,7 @@ export const nodes: { [index: string]: NodeSpec } = {
                     {
                         'data-mapStyle': node.attrs.mapStyle,
                         'data-bulletStyle': node.attrs.bulletStyle,
-                        style: `${fsize} ${ffam} ${fcol} ${marg}`,
+                        style: `${fhigh} ${fsize} ${ffam} ${fcol} ${marg}`,
                     },
                     0,
                 ];
@@ -398,7 +398,7 @@ export const nodes: { [index: string]: NodeSpec } = {
                           class: `${map}-ol`,
                           'data-mapStyle': node.attrs.mapStyle,
                           'data-bulletStyle': node.attrs.bulletStyle,
-                          style: `list-style: none; ${fsize} ${ffam} ${fcol} ${marg}`,
+                          style: `list-style: none; ${fhigh} ${fsize} ${ffam} ${fcol} ${marg}`,
                       },
                       0,
                   ]
@@ -422,16 +422,22 @@ export const nodes: { [index: string]: NodeSpec } = {
                 },
             },
         ],
-        toDOM(node: any) {
+        toDOM(node: Node) {
+            const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type.name === 'marker')?.attrs.highlight);
+            const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontSize')?.attrs.fontSize);
+            const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontFamily')?.attrs.family);
+            const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontColor')?.attrs.color);
             const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : '';
             return [
                 'li',
-                { class: `${map}`, 'data-mapStyle': node.attrs.mapStyle, 'data-bulletStyle': node.attrs.bulletStyle },
+                { class: `${map}`, style: `${fhigh} ${fsize} ${ffam} ${fcol} `, 'data-mapStyle': node.attrs.mapStyle, 'data-bulletStyle': node.attrs.bulletStyle },
                 node.attrs.visibility
                     ? 0
                     : [
                           'span',
-                          { style: `position: relative; width: 100%; height: 1.5em; overflow: hidden; display: ${node.attrs.mapStyle !== 'bullet' ? 'inline-block' : 'list-item'}; text-overflow: ellipsis; white-space: pre` },
+                          {
+                              style: `${fhigh} ${fsize} ${ffam} ${fcol} position: relative; width: 100%; height: 1.5em; overflow: hidden; display: ${node.attrs.mapStyle !== 'bullet' ? 'inline-block' : 'list-item'}; text-overflow: ellipsis; white-space: pre`,
+                          },
                           `${node.firstChild?.textContent}...`,
                       ],
             ];
-- 
cgit v1.2.3-70-g09d2


From ac58c8da5dc8ee5346bccd6da1f6883a7ce0d3d7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 2 Apr 2024 14:49:15 -0400
Subject: fixed highlight list type button

---
 src/client/views/global/globalScripts.ts           |  2 +-
 .../views/nodes/formattedText/RichTextMenu.tsx     | 22 ++++++++++++----------
 2 files changed, 13 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 497ab98d8..2a5732708 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -199,7 +199,7 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
                                                                               (Doc.UserDoc().textAlign = where))}]); // prettier-ignore
     // prettier-ignore
     const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
-        [ list, {       checkResult: () => (editorView ? RichTextMenu.Instance?.getActiveListStyle() === list:false),
+        [ list, {       checkResult: () => (editorView ? RichTextMenu.Instance?.listStyle === list:false),
                         toggle: () => editorView?.state && RichTextMenu.Instance?.changeListType(list) }]);
     // prettier-ignore
     const attrs:attrfuncs[] = [
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9c282a1d2..4bd4ca72b 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -51,7 +51,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     @observable private _activeFontSize: string = '13px';
     @observable private _activeFontFamily: string = '';
-    @observable private activeListType: string = '';
+    @observable private _activeListType: string = '';
     @observable private _activeAlignment: string = 'left';
 
     @observable private brushMarks: Set<Mark> = new Set();
@@ -103,6 +103,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     @computed get fontSize() {
         return this._activeFontSize;
     }
+    @computed get listStyle() {
+        return this._activeListType;
+    }
     @computed get textAlign() {
         return this._activeAlignment;
     }
@@ -143,7 +146,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         const refDoc = SelectionManager.Views.lastElement()?.layoutDoc ?? Doc.UserDoc();
         const refField = (pfx => (pfx ? pfx + '_' : ''))(SelectionManager.Views.lastElement()?.LayoutFieldKey);
 
-        this.activeListType = this.getActiveListStyle();
+        this._activeListType = this.getActiveListStyle();
         this._activeAlignment = this.getActiveAlignment();
         this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(refDoc[refField + 'fontFamily'], 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
         this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(refDoc[refField + 'fontSize'], '10px')) : activeSizes[0];
@@ -199,16 +202,15 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     // finds font sizes and families in selection
     getActiveListStyle() {
-        if (this.view && this.TextView?._props.rootSelected?.()) {
-            const path = (this.view.state.selection.$from as any).path;
-            for (let i = 0; i < path.length; i += 3) {
-                if (path[i].type === this.view.state.schema.nodes.ordered_list) {
-                    return path[i].attrs.mapStyle;
+        const state = this.view?.state;
+        if (state) {
+            const pos = state.selection.$anchor;
+            for (let i = 0; i < pos.depth; i++) {
+                const node = pos.node(i);
+                if (node.type === schema.nodes.ordered_list) {
+                    return node.attrs.mapStyle;
                 }
             }
-            if (this.view.state.selection.$from.nodeAfter?.type === this.view.state.schema.nodes.ordered_list) {
-                return this.view.state.selection.$from.nodeAfter?.attrs.mapStyle;
-            }
         }
         return '';
     }
-- 
cgit v1.2.3-70-g09d2


From 6704c128dc66ab9d7654785f4f1f6664cf68b3c0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 4 Apr 2024 11:07:45 -0400
Subject: cleaned up converting to bullets/list

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

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

diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 4bd4ca72b..cecf106a3 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -404,39 +404,23 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     // remove all node type and apply the passed-in one to the selected text
     changeListType = (mapStyle: string) => {
         const active = this.view?.state && RichTextMenu.Instance?.getActiveListStyle();
-        const nodeType = this.view?.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? '' : mapStyle });
-        if (!this.view || nodeType?.attrs.mapStyle === '') return;
-
-        const nextIsOL = this.view.state.selection.$from.nodeAfter?.type === schema.nodes.ordered_list;
-        let inList: any = undefined;
-        let fromList = -1;
-        const path: any = Array.from((this.view.state.selection.$from as any).path);
-        for (let i = 0; i < path.length; i++) {
-            if (path[i]?.type === schema.nodes.ordered_list) {
-                inList = path[i];
-                fromList = path[i - 1];
-            }
-        }
+        const newMapStyle = active === mapStyle ? '' : mapStyle;
+        if (!this.view || newMapStyle === '') return;
 
+        let inList = this.view.state.selection.$anchor.node(1).type === schema.nodes.ordered_list;
         const marks = this.view.state.storedMarks || (this.view.state.selection.$to.parentOffset && this.view.state.selection.$from.marks());
-        if (
-            inList ||
+        if (inList) {
+            const tx2 = updateBullets(this.view.state.tr, schema, newMapStyle, this.view.state.doc.resolve(this.view.state.selection.$anchor.before(1) + 1).pos, this.view.state.doc.resolve(this.view.state.selection.$anchor.after(1)).pos);
+            marks && tx2.ensureMarks([...marks]);
+            marks && tx2.setStoredMarks([...marks]);
+            this.view.dispatch(tx2);
+        } else
             !wrapInList(schema.nodes.ordered_list)(this.view.state, (tx2: any) => {
-                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, this.view!.state.selection.from - 1, this.view!.state.selection.to + 1);
-                marks && tx3.ensureMarks([...marks]);
-                marks && tx3.setStoredMarks([...marks]);
-
-                this.view!.dispatch(tx2);
-            })
-        ) {
-            const tx2 = this.view.state.tr;
-            if (nodeType && (inList || nextIsOL)) {
-                const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, inList ? fromList : this.view.state.selection.from, inList ? fromList + inList.nodeSize : this.view.state.selection.to);
+                const tx3 = updateBullets(tx2, schema, newMapStyle, this.view!.state.selection.from - 1, this.view!.state.selection.to + 1);
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
-                this.view.dispatch(tx3);
-            }
-        }
+                this.view!.dispatch(tx3);
+            });
         this.view.focus();
         this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
     };
-- 
cgit v1.2.3-70-g09d2

