Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions web/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ function makeEmoteImg(name) {

// Plain-text source; images contribute their literal names.
function editorSource(root = editor) {
// A bare contenteditable can leave a single <br> or <div><br></div>
// as a placeholder. Treat that as empty source so the user can delete
// back to an empty editor.
if (root === editor && root.childNodes.length === 1) {
const only = root.firstChild;
if (only.nodeName === "BR" ||
(only.nodeName === "DIV" && only.childNodes.length === 1 &&
only.firstChild.nodeName === "BR")) {
return "";
}
}

let src = "";
for (const node of root.childNodes) {
if (node.nodeType === Node.TEXT_NODE) {
Expand Down Expand Up @@ -212,6 +224,14 @@ function getCaretOffset() {
return count;
}

function sourceOffsetAt(node, offset) {
if (node !== editor && !editor.contains(node)) return null;
const range = document.createRange();
range.setStart(editor, 0);
range.setEnd(node, offset);
return editorSource(range.cloneContents()).length;
}

function setCaretOffset(offset) {
const boundaries = []; // {at, make}
const walk = (node, base) => {
Expand Down Expand Up @@ -619,6 +639,25 @@ editor.addEventListener("keydown", (e) => {
return;
}

if (e.key === "Enter" && !e.ctrlKey && !e.metaKey) {
if (e.isComposing) return; // let IME confirm composition
e.preventDefault();
const sel = getSelection();
if (!sel.rangeCount) return;
const range = sel.getRangeAt(0);
const start = sourceOffsetAt(range.startContainer, range.startOffset);
const end = sourceOffsetAt(range.endContainer, range.endOffset);
if (start === null || end === null) return;
const src = editorSource(editor);
const next = src.slice(0, start) + "\n" + src.slice(end);
editor.replaceChildren(buildEditorFragment(next));
setCaretOffset(start + 1);
renderGutter(next);
scheduleUndoCommit();
updateAutocomplete();
return;
}

if (e.key === "Tab") {
// No popup yet: complete directly when there is exactly one
// candidate, otherwise open the popup for the word at the caret.
Expand Down
Loading