From 6df80c76b4046ef1ac9e98e52f766dfaf540ead2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 20 May 2022 12:51:51 -0400
Subject: removed enumerables field stuff which was hacky and never used. 
 cleaned up some of currentUserUtils initialiazation.  fixed treeView sorting
 for 'none" which fixes problem with slide template.

---
 src/client/views/nodes/formattedText/DashFieldView.tsx | 14 --------------
 1 file changed, 14 deletions(-)

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

diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 34908e54b..1a8352b72 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -58,7 +58,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     _textBoxDoc: Doc;
     _fieldKey: string;
     _fieldStringRef = React.createRef<HTMLSpanElement>();
-    @observable _showEnumerables: boolean = false;
     @observable _dashDoc: Doc | undefined;
 
     constructor(props: IDashFieldViewInternal) {
@@ -110,7 +109,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                         r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
                         r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
                         r?.addEventListener("pointerdown", action((e) => {
-                            this._showEnumerables = true;
                             e.stopPropagation();
                         }));
                     }} >
@@ -124,7 +122,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
     @action
     fieldSpanKeyDown = (e: KeyboardEvent, span: HTMLSpanElement) => {
         if (e.key === "Enter") {  // handle the enter key by "submitting" the current text to Dash's database. 
-            e.ctrlKey && DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: span.textContent! }]);
             this.updateText(span.textContent!, true);
             e.preventDefault();// prevent default to avoid a newline from being generated and wiping out this field view
         }
@@ -142,7 +139,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
 
     @action
     updateText = (nodeText: string, forceMatch: boolean) => {
-        this._showEnumerables = false;
         if (nodeText) {
             const newText = nodeText.startsWith(":=") || nodeText.startsWith("=:=") ? ":=-computed-" : nodeText;
 
@@ -154,7 +150,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                 (options instanceof Doc) && DocListCast(options.data).forEach(opt => (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title)));
                 if (modText) {
                     //  elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText;
-                    DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, []);
                     Doc.SetInPlace(this._dashDoc!, this._fieldKey, modText, true);
                 } // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
                 else if (nodeText.startsWith(":=")) {
@@ -178,13 +173,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         }
     }
 
-    // display a collection of all the enumerable values for this field
-    onPointerDownEnumerables = async (e: any) => {
-        e.stopPropagation();
-        const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]);
-        collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "add:right");
-    }
-
 
     // clicking on the label creates a pivot view collection of all documents
     // in the same collection.  The pivot field is the fieldKey of this label
@@ -220,8 +208,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
 
             {this.props.fieldKey.startsWith("#") ? (null) : this.fieldValueContent}
 
-            {!this._showEnumerables ? (null) : <div className="dashFieldView-enumerables" onPointerDown={this.onPointerDownEnumerables} />}
-
         </div >;
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 53cae5e2ab9267295a824ff721c119ada5e5dc20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 31 May 2022 13:03:19 -0400
Subject: added a pop up menu for viewing dashField view collection.  added a
 menu button for turning off autoLinking to text selection.  added ability to
 add button to top contextMenu buttons without blowing away db.

---
 src/client/util/CurrentUserUtils.ts                | 34 +++++++++
 src/client/views/MainView.tsx                      |  2 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  8 +++
 .../views/nodes/formattedText/DashFieldView.tsx    | 81 +++++++++++++++++-----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 12 ++--
 .../formattedText/ProsemirrorExampleTransfer.ts    |  2 +
 .../views/nodes/formattedText/RichTextMenu.tsx     | 14 +++-
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 src/client/views/nodes/formattedText/marks_rts.ts  | 14 ++++
 10 files changed, 144 insertions(+), 27 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 083530ce7..c4e2afd08 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -745,6 +745,7 @@ export class CurrentUserUtils {
                 { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
                 { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
                 { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
+                { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", click: 'toggleNoAutoLinkAnchor(_readOnly_)' },
             ];
         return tools;
     }
@@ -834,11 +835,44 @@ export class CurrentUserUtils {
                     !params.subMenu ?
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
+                            title: params.title,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
                             hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
                         }, params.subMenu.map(btnFunc))));
+        } else {
+            const menuBtnList = DocListCast((doc.contextMenuBtns as Doc).data);
+            let prev = "";
+            CurrentUserUtils.contextMenuTools(doc).forEach(params => {
+                const menuBtnDoc = menuBtnList.find(doc => doc.title === params.title);
+                if (!menuBtnDoc) {
+                    const newMenuBtnDoc = !params.subMenu ?
+                        btnFunc(params) :
+                        CurrentUserUtils.linearButtonList({
+                            title: params.title,
+                            linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
+                            linearViewExpandable: true, icon: params.title, _height: 30,
+                            linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
+                            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
+                        }, params.subMenu.map(btnFunc));
+                    const after = menuBtnList.find(doc => doc.title === prev);
+                    Doc.AddDocToList(doc.contextMenuBtns as Doc, "data", newMenuBtnDoc, after, false, !after);
+                }
+                const subMenuBtnList = menuBtnDoc?.data ? DocListCast(menuBtnDoc.data) : undefined;
+                if (menuBtnDoc && subMenuBtnList && params.subMenu && DocListCast(doc.data).length !== subMenuBtnList.length) {
+                    let prevSub = "";
+                    params.subMenu.forEach(sub => {
+                        if (!subMenuBtnList.find(doc => doc.title === sub.title)) {
+                            const newSubMenuBtnDoc = btnFunc(sub);
+                            const after = subMenuBtnList.find(doc => doc.title === prevSub);
+                            Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub);
+                        }
+                        prevSub = params.title;
+                    })
+                }
+                prev = params.title;
+            });
         }
     }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ff2857739..55190001b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -50,6 +50,7 @@ import { AudioBox } from './nodes/AudioBox';
 import { ButtonType } from './nodes/button/FontIconBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
+import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
@@ -686,6 +687,7 @@ export class MainView extends React.Component {
             <ContextMenu />
             <RadialMenu />
             <AnchorMenu />
+            <DashFieldViewMenu />
             <MarqueeOptionsMenu />
             <OverlayView />
             <TimelineMenu />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f4cb08f8f..259cc1ee5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1166,7 +1166,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
+        if (docView && (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== "Tab";
             const layoutKey = StrCast(docView.LayoutFieldKey);
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index e6d8fe88d..625f5d14a 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -666,6 +666,14 @@ ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: b
     if (editorView) RichTextMenu.Instance.setFontSize(size);
     else Doc.UserDoc()._fontSize = size;
 });
+ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+    if (checkResult) {
+        return (editorView ? RichTextMenu.Instance.noAutoLink : Doc.UserDoc().noAutoLink) ? Colors.MEDIUM_BLUE : "transparent";
+    }
+    if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor();
+    else Doc.UserDoc().noAutoLink = Doc.UserDoc().noAutoLink ? true : false;
+});
 
 ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 1a8352b72..6a3f9ed00 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -8,35 +8,41 @@ import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
 import { ComputedField } from "../../../../fields/ScriptField";
 import { Cast, StrCast } from "../../../../fields/Types";
 import { DocServer } from "../../../DocServer";
-import { DocUtils } from "../../../documents/Documents";
 import { CollectionViewType } from "../../collections/CollectionView";
 import "./DashFieldView.scss";
 import { FormattedTextBox } from "./FormattedTextBox";
 import React = require("react");
+import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils";
+import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
+import { Tooltip } from "@material-ui/core";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 
 export class DashFieldView {
     _fieldWrapper: HTMLDivElement; // container for label and value
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+        const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey);
+
         this._fieldWrapper = document.createElement("div");
         this._fieldWrapper.style.width = node.attrs.width;
         this._fieldWrapper.style.height = node.attrs.height;
         this._fieldWrapper.style.fontWeight = "bold";
         this._fieldWrapper.style.position = "relative";
         this._fieldWrapper.style.display = "inline-block";
+        this._fieldWrapper.textContent = node.attrs.fieldKey.startsWith("#") ? node.attrs.fieldKey : node.attrs.fieldKey + " " + strVal;
         this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
 
-        ReactDOM.render(<DashFieldViewInternal
+        setTimeout(() => ReactDOM.render(<DashFieldViewInternal
             fieldKey={node.attrs.fieldKey}
             docid={node.attrs.docid}
             width={node.attrs.width}
             height={node.attrs.height}
             hideKey={node.attrs.hideKey}
             tbox={tbox}
-        />, this._fieldWrapper);
+        />, this._fieldWrapper));
         (this as any).dom = this._fieldWrapper;
     }
     destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
@@ -76,16 +82,17 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         this._reactionDisposer?.();
     }
 
-    multiValueDelimeter = ";";
+    public static multiValueDelimeter = ";";
+    public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
+        const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === "PARAMS" ? textBoxDoc[fieldKey] : "");
+        const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
+        return { boolVal: Cast(fval, "boolean", null), strVal: Field.toString(fval as Field) || "" }
+    }
 
     // set the display of the field's value (checkbox for booleans, span of text for strings)
     @computed get fieldValueContent() {
         if (this._dashDoc) {
-            const dashVal = this._dashDoc[this._fieldKey] ?? this._dashDoc[DataSym][this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
-            const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
-            const boolVal = Cast(fval, "boolean", null);
-            const strVal = Field.toString(fval as Field) || "";
-
+            const { boolVal, strVal } = DashFieldViewInternal.fieldContent(this._textBoxDoc, this._dashDoc, this._fieldKey);
             // field value is a boolean, so use a checkbox or similar widget to display it
             if (boolVal === true || boolVal === false) {
                 return <input
@@ -108,9 +115,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                     ref={r => {
                         r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
                         r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
-                        r?.addEventListener("pointerdown", action((e) => {
-                            e.stopPropagation();
-                        }));
+                        r?.addEventListener("pointerdown", action(e => e.stopPropagation()));
                     }} >
                     {strVal}
                 </span>;
@@ -161,7 +166,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                         if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
                         Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
                     } else {
-                        const splits = newText.split(this.multiValueDelimeter);
+                        const splits = newText.split(DashFieldViewInternal.multiValueDelimeter);
                         if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
                             const strVal = splits.length > 1 ? new List<string>(splits) : newText;
                             if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
@@ -173,11 +178,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         }
     }
 
-
-    // clicking on the label creates a pivot view collection of all documents
-    // in the same collection.  The pivot field is the fieldKey of this label
-    onPointerDownLabelSpan = (e: any) => {
-        e.stopPropagation();
+    createPivotForField = (e: React.MouseEvent) => {
         let container = this.props.tbox.props.ContainingCollectionView;
         while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) {
             container = container.props.ContainingCollectionView;
@@ -196,6 +197,16 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         }
     }
 
+
+    // clicking on the label creates a pivot view collection of all documents
+    // in the same collection.  The pivot field is the fieldKey of this label
+    onPointerDownLabelSpan = (e: any) => {
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, (e) => {
+            DashFieldViewMenu.createFieldView = this.createPivotForField;
+            DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16);
+        });
+    }
+
     render() {
         return <div className="dashFieldView" style={{
             width: this.props.width,
@@ -210,4 +221,38 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
 
         </div >;
     }
+}
+@observer
+export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
+    static Instance: DashFieldViewMenu;
+    static createFieldView: (e: React.MouseEvent) => void = emptyFunction;
+    constructor(props: any) {
+        super(props);
+        DashFieldViewMenu.Instance = this;
+    }
+    @action
+    showFields = (e: React.MouseEvent) => {
+        DashFieldViewMenu.createFieldView(e);
+        DashFieldViewMenu.Instance.fadeOut(true);
+    }
+
+    public show = (x: number, y: number) => {
+        this.jumpTo(x, y, true);
+        const hideMenu = () => {
+            this.fadeOut(true);
+            document.removeEventListener("pointerdown", hideMenu);
+        }
+        document.addEventListener("pointerdown", hideMenu)
+    }
+    render() {
+        const buttons = [
+            <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
+                <button className="antimodeMenu-button" onPointerDown={this.showFields}>
+                    <FontAwesomeIcon icon="eye" size="lg" />
+                </button>
+            </Tooltip>,
+        ];
+
+        return this.getElement(buttons);
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d468822c0..ce82821b6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -391,16 +391,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             flattened1.forEach((flat, i) => {
                 const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, autoLinkTerm);
                 this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
-                alink = alink ?? (DocListCast(this.Document.links).find(link =>
-                    Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) &&
-                    Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target },
-                        LinkManager.AutoKeywords)!);
-                newAutoLinks.add(alink);
                 const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
                 const sel = flattened[i];
                 tr = tr.addMark(sel.from, sel.to, splitter);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
-                    if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+                    if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+                        alink = alink ?? (DocListCast(this.Document.links).find(link =>
+                            Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) &&
+                            Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target },
+                                LinkManager.AutoKeywords)!);
+                        newAutoLinks.add(alink);
                         const allAnchors = [{ href: Doc.localServerPath(target), title: "a link", anchorId: this.props.Document[Id] }];
                         allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
                         const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: "auto term", location: "add:right" });
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index e979ae59e..fb49b0698 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -82,6 +82,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     bind("Ctrl-Tab", () => props.onKey?.(event, props) ? true : true);
     bind("Alt-Tab", () => props.onKey?.(event, props) ? true : true);
+    bind("Meta-Tab", () => props.onKey?.(event, props) ? true : true);
+    bind("Meta-Enter", () => props.onKey?.(event, props) ? true : true);
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index c5f76cc8d..3df1e45a5 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -35,6 +35,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     public _brushMap: Map<string, Set<Mark>> = new Map();
 
     @observable private collapsed: boolean = false;
+    @observable private _noLinkActive: boolean = false;
     @observable private _boldActive: boolean = false;
     @observable private _italicsActive: boolean = false;
     @observable private _underlineActive: boolean = false;
@@ -79,6 +80,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         this._reaction?.();
     }
 
+    @computed get noAutoLink() { return this._noLinkActive; }
     @computed get bold() { return this._boldActive; }
     @computed get underline() { return this._underlineActive; }
     @computed get italics() { return this._italicsActive; }
@@ -220,7 +222,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         let activeMarks: MarkType[] = [];
         if (!this.view || !this.TextView.props.isSelected(true)) return activeMarks;
 
-        const markGroup = [schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
+        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;
@@ -264,6 +266,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     setActiveMarkButtons(activeMarks: MarkType[] | undefined) {
         if (!activeMarks) return;
 
+        this._noLinkActive = false;
         this._boldActive = false;
         this._italicsActive = false;
         this._underlineActive = false;
@@ -273,6 +276,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
         activeMarks.forEach(mark => {
             switch (mark.name) {
+                case "noAutoLinkAnchor": this._noLinkActive = true; break;
                 case "strong": this._boldActive = true; break;
                 case "em": this._italicsActive = true; break;
                 case "underline": this._underlineActive = true; break;
@@ -283,6 +287,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         });
     }
 
+    toggleNoAutoLinkAnchor = () => {
+        if (this.view) {
+            const mark = this.view.state.schema.mark(this.view.state.schema.marks.noAutoLinkAnchor);
+            this.setMark(mark, this.view.state, this.view.dispatch, false);
+            this.TextView.autoLink();
+            this.view.focus();
+        }
+    }
     toggleBold = () => {
         if (this.view) {
             const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 427e05edb..8851d52e4 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -348,7 +348,7 @@ export class RichTextRules {
                         this.Document[DataSym].tags = `${tags + "#" + tag + ':'}`;
                     }
                     const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag });
-                    return state.tr.deleteRange(start, end).insert(start, fieldView);
+                    return state.tr.deleteRange(start, end).insert(start, fieldView).insertText(" ");
                 }),
 
 
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 1f6ce014f..2fde5c7ba 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -48,6 +48,20 @@ export const marks: { [index: string]: MarkSpec } = {
             return ["a", { class: anchorids, "data-targethrefs": targethrefs, "data-linkdoc": node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0];
         }
     },
+    noAutoLinkAnchor: {
+        attrs: {},
+        inclusive: false,
+        parseDOM: [{
+            tag: "div", getAttrs(dom: any) {
+                return {
+                    noAutoLink: dom.getAttribute("data-noAutoLink"),
+                };
+            }
+        }],
+        toDOM(node: any) {
+            return ["span", { "data-noAutoLink": "true" }, 0];
+        }
+    },
     // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each linkAnchor specifies an href to the URL of the source selection Marker text,
     // and a title for use in menus and hover. `title`
     // defaults to the empty string. Rendered and parsed as an `<a>`
-- 
cgit v1.2.3-70-g09d2

