Skip to content

Commit ea27d86

Browse files
committed
fix(mdviewer): arrow-down and shift-enter exit code block to paragraph below
1 parent b32e335 commit ea27d86

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

src-mdviewer/src/components/editor.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,24 @@ function getBlockType() {
208208
return "P";
209209
}
210210

211+
function _exitBlockBelow(blockEl, contentEl) {
212+
let next = blockEl.nextElementSibling;
213+
if (!next || next.tagName === "PRE" || next.classList?.contains("table-wrapper")) {
214+
next = document.createElement("p");
215+
next.innerHTML = "<br>";
216+
blockEl.parentNode.insertBefore(next, blockEl.nextSibling);
217+
}
218+
const range = document.createRange();
219+
range.selectNodeContents(next);
220+
range.collapse(true);
221+
const sel = window.getSelection();
222+
sel.removeAllRanges();
223+
sel.addRange(range);
224+
if (contentEl) {
225+
contentEl.dispatchEvent(new Event("input", { bubbles: true }));
226+
}
227+
}
228+
211229
function _exitTableBelow(tableEl, contentEl) {
212230
const wrapper = tableEl.closest(".table-wrapper") || tableEl;
213231
// Use existing next sibling if it's a block element, otherwise create a <p>
@@ -1696,6 +1714,41 @@ function enterEditMode(content) {
16961714
}
16971715
}
16981716

1717+
// ArrowDown at end of code block → exit to paragraph below
1718+
if (e.key === "ArrowDown" && !mod) {
1719+
const sel = window.getSelection();
1720+
if (sel && sel.rangeCount) {
1721+
let node = sel.anchorNode;
1722+
if (node?.nodeType === Node.TEXT_NODE) node = node.parentElement;
1723+
const pre = node?.closest("pre");
1724+
if (pre && pre.closest("#viewer-content")) {
1725+
const range = sel.getRangeAt(0);
1726+
if (range.collapsed) {
1727+
const code = pre.querySelector("code") || pre;
1728+
// Check if cursor is on the last line
1729+
const textContent = code.textContent || "";
1730+
const lastNewline = textContent.lastIndexOf("\n");
1731+
// Get cursor offset within code
1732+
let cursorOffset = 0;
1733+
const tw = document.createTreeWalker(code, NodeFilter.SHOW_TEXT);
1734+
let n;
1735+
while ((n = tw.nextNode())) {
1736+
if (n === range.startContainer) {
1737+
cursorOffset += range.startOffset;
1738+
break;
1739+
}
1740+
cursorOffset += n.textContent.length;
1741+
}
1742+
if (cursorOffset >= lastNewline) {
1743+
e.preventDefault();
1744+
_exitBlockBelow(pre, content);
1745+
return;
1746+
}
1747+
}
1748+
}
1749+
}
1750+
}
1751+
16991752
if (e.key === " ") {
17001753
handleMarkdownShortcutOnSpace(e, content);
17011754
return;
@@ -1738,6 +1791,39 @@ function enterEditMode(content) {
17381791
}
17391792

17401793
if (e.key === "Enter") {
1794+
// Shift+Enter on last line of code block → exit to paragraph below
1795+
if (e.shiftKey) {
1796+
const sel = window.getSelection();
1797+
if (sel && sel.rangeCount) {
1798+
let node = sel.anchorNode;
1799+
if (node?.nodeType === Node.TEXT_NODE) node = node.parentElement;
1800+
const pre = node?.closest("pre");
1801+
if (pre && pre.closest("#viewer-content")) {
1802+
const code = pre.querySelector("code") || pre;
1803+
const range = sel.getRangeAt(0);
1804+
if (range.collapsed) {
1805+
const textContent = code.textContent || "";
1806+
const lastNewline = textContent.lastIndexOf("\n");
1807+
let cursorOffset = 0;
1808+
const tw = document.createTreeWalker(code, NodeFilter.SHOW_TEXT);
1809+
let n;
1810+
while ((n = tw.nextNode())) {
1811+
if (n === range.startContainer) {
1812+
cursorOffset += range.startOffset;
1813+
break;
1814+
}
1815+
cursorOffset += n.textContent.length;
1816+
}
1817+
if (cursorOffset >= lastNewline) {
1818+
e.preventDefault();
1819+
_exitBlockBelow(pre, content);
1820+
return;
1821+
}
1822+
}
1823+
}
1824+
}
1825+
}
1826+
17411827
// Inside table: block Enter except on last cell or outside cells where it exits
17421828
if (isInsideTableOrWrapper()) {
17431829
e.preventDefault();

0 commit comments

Comments
 (0)