Compare commits

...

1 Commits

Author SHA1 Message Date
Tom Moor dbe78f5e2a fix: IME composition between backticks 2025-04-18 10:26:42 -04:00
3 changed files with 97 additions and 62 deletions
+2 -2
View File
@@ -179,7 +179,7 @@
"png-chunks-extract": "^1.0.0",
"polished": "^4.3.1",
"prosemirror-codemark": "^0.4.2",
"prosemirror-commands": "^1.7.0",
"prosemirror-commands": "^1.7.1",
"prosemirror-dropcursor": "^1.8.1",
"prosemirror-gapcursor": "^1.3.2",
"prosemirror-history": "^1.4.1",
@@ -191,7 +191,7 @@
"prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.6.4",
"prosemirror-transform": "1.10.0",
"prosemirror-view": "^1.38.1",
"prosemirror-view": "^1.39.1",
"query-string": "^7.1.3",
"randomstring": "1.3.1",
"rate-limiter-flexible": "^2.4.2",
+87 -52
View File
@@ -47,6 +47,66 @@ export default class Code extends Mark {
markType: this.editor.schema.marks.code_inline,
})[0];
/**
* Helper function to check if cursor is between backticks
* and handle the code marking appropriately
*/
const handleTextBetweenBackticks = (
view: EditorView,
from: number,
to: number,
text: string | Slice
) => {
const { state } = view;
// Prevent access out of document bounds
if (from === 0 || to === state.doc.nodeSize - 1) {
return false;
}
// Skip if we're adding a backtick character
if (typeof text === "string" && text === "`") {
return false;
}
// Check if we're between backticks
if (
state.doc.textBetween(from - 1, from) === "`" &&
state.doc.textBetween(to, to + 1) === "`"
) {
const start = from - 1;
const end = to + 1;
if (typeof text === "string") {
// Handle text input
view.dispatch(
state.tr
.delete(start, end)
.insertText(text, start)
.addMark(
start,
start + text.length,
state.schema.marks.code_inline.create()
)
);
} else {
// Handle paste/slice
view.dispatch(
state.tr
.replaceRange(start, end, text)
.addMark(
start,
start + text.size,
state.schema.marks.code_inline.create()
)
);
}
return true;
}
return false;
};
return [
codeCursorPlugin,
new Plugin({
@@ -59,34 +119,11 @@ export default class Code extends Mark {
to: number,
text: string
) => {
const { state } = view;
// Prevent access out of document bounds
if (from === 0 || to === state.doc.nodeSize - 1 || text === "`") {
// Skip this handler during IME composition or it will prevent the
if (view.composing) {
return false;
}
if (
from === to &&
state.doc.textBetween(from - 1, from) === "`" &&
state.doc.textBetween(to, to + 1) === "`"
) {
const start = from - 1;
const end = to + 1;
view.dispatch(
state.tr
.delete(start, end)
.insertText(text, start)
.addMark(
start,
start + text.length,
state.schema.marks.code_inline.create()
)
);
return true;
}
return false;
return handleTextBetweenBackticks(view, from, to, text);
},
// Pasting a character inside of two backticks will wrap the character
@@ -94,32 +131,7 @@ export default class Code extends Mark {
handlePaste: (view: EditorView, _event: Event, slice: Slice) => {
const { state } = view;
const { from, to } = state.selection;
// Prevent access out of document bounds
if (from === 0 || to === state.doc.nodeSize - 1) {
return false;
}
const start = from - 1;
const end = to + 1;
if (
from === to &&
state.doc.textBetween(start, from) === "`" &&
state.doc.textBetween(to, end) === "`"
) {
view.dispatch(
state.tr
.replaceRange(start, end, slice)
.addMark(
start,
start + slice.size,
state.schema.marks.code_inline.create()
)
);
return true;
}
return false;
return handleTextBetweenBackticks(view, from, to, slice);
},
// Triple clicking inside of an inline code mark will select the entire
@@ -143,6 +155,29 @@ export default class Code extends Mark {
return false;
},
// Handle composition end events for IME input
handleDOMEvents: {
compositionend: (view: EditorView) => {
setTimeout(() => {
const { $cursor } = view.state.selection as TextSelection;
if (!$cursor) {
return;
}
const from = $cursor.pos - 1;
const to = $cursor.pos;
// Process the composed text after IME composition completes
handleTextBetweenBackticks(
view,
from,
to,
view.state.doc.textBetween(from, to)
);
});
},
},
},
}),
];
+8 -8
View File
@@ -13052,10 +13052,10 @@ prosemirror-codemark@^0.4.2:
resolved "https://registry.yarnpkg.com/prosemirror-codemark/-/prosemirror-codemark-0.4.2.tgz#b4d0a57c0f1f6c6667e2a1ae7cfb6ba031dfb2e5"
integrity "sha1-tNClfA8fbGZn4qGufPtroDHfsuU= sha512-4n+PnGQToa/vTjn0OiivUvE8/moLtguUAfry8UA4Q8p47MhqT2Qpf2zBLustX5Upi4mSp3z1ZYBqLLovZC6abA=="
prosemirror-commands@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.7.0.tgz#c0a60c808f51157caa146922494fc59fe257f27c"
integrity sha512-6toodS4R/Aah5pdsrIwnTYPEjW70SlO5a66oo5Kk+CIrgJz3ukOoS+FYDGqvQlAX5PxoGWDX1oD++tn5X3pyRA==
prosemirror-commands@^1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz#d101fef85618b1be53d5b99ea17bee5600781b38"
integrity sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
@@ -13158,10 +13158,10 @@ prosemirror-transform@1.10.0, prosemirror-transform@^1.0.0, prosemirror-transfor
dependencies:
prosemirror-model "^1.21.0"
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.37.2, prosemirror-view@^1.38.1:
version "1.38.1"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.38.1.tgz#566d30cc8b00a68d6b4c60f5d8a6ab97c82990b3"
integrity sha512-4FH/uM1A4PNyrxXbD+RAbAsf0d/mM0D/wAKSVVWK7o0A9Q/oOXJBrw786mBf2Vnrs/Edly6dH6Z2gsb7zWwaUw==
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.37.2, prosemirror-view@^1.39.1:
version "1.39.1"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.39.1.tgz#9e24cc82649d37ed5d75bf59419694b0566927bb"
integrity sha512-GhLxH1xwnqa5VjhJ29LfcQITNDp+f1jzmMPXQfGW9oNrF0lfjPzKvV5y/bjIQkyKpwCX3Fp+GA4dBpMMk8g+ZQ==
dependencies:
prosemirror-model "^1.20.0"
prosemirror-state "^1.0.0"