Cybrkyd's Git Repositories

jottings - commit: 9573c1e

commit 9573c1e73cd08febe51a10d36cf8c8c15996adfd05273da7ffca0fc9117853d1
author Cybrkyd <git@cybrkyd.com> 2026-06-20 10:50:52 +0100
committer Cybrkyd <git@cybrkyd.com> 2026-06-20 10:50:52 +0100

Commit Message

Double and triple-Enter fix

All docs now get an empty "phantom" last line, a la Unix text file style. This works around Chrome's contenteditable + white-space:pre-wrap quirk where new \n does not render until a new line break is pushed in below it. A friendly ghost, we strip it out, so it is never exported or spellchecked. Let's call him Casper.

📊 Diffstat

resources/js/app.js 42
1 files changed, 39 insertions(+), 3 deletions(-)

Diff

diff --git a/resources/js/app.js b/resources/js/app.js
index b01675a..f1f0cd6 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -30,14 +30,40 @@ const contextCopy = document.getElementById('contextCopy');
const contextPaste = document.getElementById('contextPaste');
// Editor text helpers
-
- // innerText is out; textContent is a little more sane for flat run of text nodes
function getEditorText() {
- return editor.textContent.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
+ return editor.textContent
+ .replace(/\r\n/g, '\n')
+ .replace(/\r/g, '\n')
+ .replace(/\n$/, '');
+ }
+
+ // End editor.textContent with exactly one "\n"
+ function ensureTrailingNewline() {
+ if (!editor.textContent.endsWith('\n')) {
+ editor.appendChild(document.createTextNode('\n'));
+ }
+ }
+
+ // Toggle the placeholder on/off based on real content
+ function updatePlaceholderVisibility() {
+ editor.classList.toggle('editor-empty', getEditorText() === '');
}
function setEditorText(text) {
editor.textContent = text;
+ ensureTrailingNewline();
+ updatePlaceholderVisibility();
+
+ // Caret always starts at line 1 x column 1
+ if (editor.firstChild) {
+ const range = document.createRange();
+ range.setStart(editor.firstChild, 0);
+ range.collapse(true);
+ const selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
clearTimeout(spellDebounce);
runSpellcheck();
}
@@ -59,6 +85,10 @@ function insertTextAtCursor(text) {
selection.removeAllRanges();
selection.addRange(range);
+ // Permanently maintain the bottom empty line
+ ensureTrailingNewline();
+ updatePlaceholderVisibility();
+
// Let input handler run for normal typing
editor.dispatchEvent(new Event('input', { bubbles: true }));
}
@@ -858,6 +888,9 @@ toggleBtn.addEventListener("click", function() {
// Editor input events
editor.addEventListener("input", function() {
+ ensureTrailingNewline();
+ updatePlaceholderVisibility();
+
const text = getEditorText();
updateCounts(text);
scheduleSpellcheck();
@@ -973,6 +1006,9 @@ resizeObserver.observe(editor);
resizeCanvas();
+ // bottom empty line on first load
+ setEditorText('');
+
Neutralino.init();
loadDocuments();
updateStatus();