FEATURE: prosemirror-codemark for a fake boundary cursor on rich editor (#32165)

Uses prosemirror-codemark to provide a fake boundary cursor to code marks, to easily
prepend/append text either code-marked or not.
This commit is contained in:
Renato Atilio
2025-04-08 10:32:15 -03:00
committed by GitHub
parent 6fb8f3ff7a
commit e84083ee36
7 changed files with 113 additions and 1 deletions
@@ -0,0 +1,10 @@
import codemark from "prosemirror-codemark";
/** @type {RichEditorExtension} */
const extension = {
plugins({ schema }) {
return codemark({ markType: schema.marks.code });
},
};
export default extension;
@@ -1,5 +1,6 @@
import { registerRichEditorExtension } from "discourse/lib/composer/rich-editor-extensions";
import bulletList from "./bullet-list";
import code from "./code";
import codeBlock from "./code-block";
import emoji from "./emoji";
import hashtag from "./hashtag";
@@ -29,6 +30,7 @@ const defaultExtensions = [
emoji,
image,
onebox,
code,
link,
heading,
codeBlock,
@@ -40,6 +40,7 @@
"morphlex": "^0.0.16",
"orderedmap": "^2.1.1",
"pretty-text": "workspace:1.0.0",
"prosemirror-codemark": "^0.4.2",
"prosemirror-commands": "^1.7.0",
"prosemirror-dropcursor": "^1.8.1",
"prosemirror-gapcursor": "^1.3.2",
@@ -61,7 +61,12 @@ export async function setupRichEditor(assert, markdown, multiToggle = false) {
// or artifacts
.replace(' class=""', "")
// or a trailing-paragraph with an optional <br class="ProseMirror-trailingBreak"> inside
.replace(/<p>(<br class="ProseMirror-trailingBreak">)?<\/p>$/, "");
.replace(/<p>(<br class="ProseMirror-trailingBreak">)?<\/p>$/, "")
// or the codemark fake cursor
.replace(
'<span class="fake-cursor ProseMirror-widget" contenteditable="false"></span>',
""
);
return [self, html];
}
@@ -276,3 +276,35 @@ li.ProseMirror-selectednode::after {
.ProseMirror-focused .ProseMirror-gapcursor {
display: block;
}
/************************************************************
Section below from prosemirror-codemark/src/codemark.css
***********************************************************/
@keyframes fake-cursor-blink {
49% {
border-color: unset;
}
50% {
border-color: transparent;
}
99% {
border-color: transparent;
}
}
.no-cursor {
caret-color: transparent;
}
div:focus .fake-cursor,
span:focus .fake-cursor {
margin-right: -1px;
border-left-width: 1px;
border-left-style: solid;
animation: fake-cursor-blink 1s;
animation-iteration-count: infinite;
position: relative;
z-index: 1;
}
+18
View File
@@ -329,6 +329,9 @@ importers:
pretty-text:
specifier: workspace:1.0.0
version: link:../pretty-text
prosemirror-codemark:
specifier: ^0.4.2
version: 0.4.2(prosemirror-inputrules@1.5.0)(prosemirror-model@1.25.0)(prosemirror-state@1.4.3)(prosemirror-view@1.39.1)
prosemirror-commands:
specifier: ^1.7.0
version: 1.7.0
@@ -7205,6 +7208,14 @@ packages:
proper-lockfile@4.1.2:
resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
prosemirror-codemark@0.4.2:
resolution: {integrity: sha512-4n+PnGQToa/vTjn0OiivUvE8/moLtguUAfry8UA4Q8p47MhqT2Qpf2zBLustX5Upi4mSp3z1ZYBqLLovZC6abA==}
peerDependencies:
prosemirror-inputrules: ^1.2.0
prosemirror-model: ^1.18.1
prosemirror-state: ^1.4.1
prosemirror-view: ^1.26.2
prosemirror-commands@1.7.0:
resolution: {integrity: sha512-6toodS4R/Aah5pdsrIwnTYPEjW70SlO5a66oo5Kk+CIrgJz3ukOoS+FYDGqvQlAX5PxoGWDX1oD++tn5X3pyRA==}
@@ -16477,6 +16488,13 @@ snapshots:
retry: 0.12.0
signal-exit: 3.0.7
prosemirror-codemark@0.4.2(prosemirror-inputrules@1.5.0)(prosemirror-model@1.25.0)(prosemirror-state@1.4.3)(prosemirror-view@1.39.1):
dependencies:
prosemirror-inputrules: 1.5.0
prosemirror-model: 1.25.0
prosemirror-state: 1.4.3
prosemirror-view: 1.39.1
prosemirror-commands@1.7.0:
dependencies:
prosemirror-model: 1.25.0
@@ -575,6 +575,50 @@ describe "Composer - ProseMirror editor", type: :system do
end
end
describe "code marks with fake cursor" do
it "allows typing after a code mark with/without the mark" do
open_composer_and_toggle_rich_editor
composer.type_content("This is ~~SPARTA!~~ `code!`.")
expect(rich).to have_css("code", text: "code!")
# within the code mark
composer.send_keys(%i[backspace backspace])
composer.type_content("!")
expect(rich).to have_css("code", text: "code!")
# after the code mark
composer.send_keys(:right)
composer.type_content(".")
composer.toggle_rich_editor
expect(composer).to have_value("This is ~~SPARTA!~~ `code!`.")
end
it "allows typing before a code mark with/without the mark" do
open_composer_and_toggle_rich_editor
composer.type_content("`code mark`")
expect(rich).to have_css("code", text: "code mark")
# before the code mark
composer.send_keys(:home)
composer.type_content("..")
# within the code mark
composer.send_keys(:right)
composer.type_content("!!")
composer.toggle_rich_editor
expect(composer).to have_value("..`!!code mark`")
end
end
describe "emojis" do
it "has the only-emoji class if 1-3 emojis are 'alone'" do
open_composer_and_toggle_rich_editor