Merge pull request #2880 from franzudev/fix/markdown-fence-caret

fix(composer): keep caret inside completed fence
This commit is contained in:
Bohdan Triapitsyn
2026-08-28 01:22:45 +03:00
committed by GitHub
4 changed files with 110 additions and 34 deletions
+10 -30
View File
@@ -109,6 +109,7 @@ import {
appendInlineText,
appendWithLineBreaks,
buildImagePasteInsertion,
getMarkdownAutoPairEdit,
shouldWrapSelectionAsLink,
withInlineInsertionBoundaries,
} from './composer/text';
@@ -1622,39 +1623,18 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({
const selEnd = ta?.getSelection().end ?? -1;
if (ta && selStart >= 0) {
const applyEdit = (next: string, caretStart: number, caretEnd: number) => {
const edit = getMarkdownAutoPairEdit(message, e.key, selStart, selEnd);
if (edit) {
e.preventDefault();
setMessage(next);
composerRef.current?.setSelection(caretStart, caretEnd);
updateAutocompleteState(next, caretEnd);
};
// Wrap the current selection: select text, press ` * _ ~ ( [ { " '
const WRAP_PAIRS: Record<string, [string, string]> = {
'`': ['`', '`'], '*': ['*', '*'], '_': ['_', '_'], '~': ['~', '~'],
'(': ['(', ')'], '[': ['[', ']'], '{': ['{', '}'],
'"': ['"', '"'], "'": ["'", "'"],
};
if (selEnd > selStart && WRAP_PAIRS[e.key]) {
const [open, close] = WRAP_PAIRS[e.key];
const selected = message.slice(selStart, selEnd);
const next = `${message.slice(0, selStart)}${open}${selected}${close}${message.slice(selEnd)}`;
applyEdit(next, selStart + open.length, selEnd + open.length);
ta.replaceRange(
edit.from,
edit.to,
edit.insert,
edit.selectionStart,
edit.selectionEnd,
);
return;
}
// Typing the third backtick at line start expands into a fenced
// code block with the caret on the empty middle line (Slack-like).
if (e.key === '`' && selStart === selEnd) {
const before = message.slice(0, selStart);
if (/(^|\n)``$/.test(before)) {
const after = message.slice(selEnd);
const next = `${before}\`\n\n\`\`\`${after}`;
const caret = before.length + 2; // after the completed ``` and first newline
applyEdit(next, caret, caret);
return;
}
}
}
}