diff --git a/documentation/FEATURES.md b/documentation/FEATURES.md
index e453aa1..7e53e1e 100644
--- a/documentation/FEATURES.md
+++ b/documentation/FEATURES.md
@@ -32,6 +32,7 @@
- **Visual tree view** - Expandable/collapsible navigation
- **Hide system folders** - Toggle to hide `_attachments`, `_templates` and other underscore-prefixed folders from sidebar
- **Tab inserts tab** - Toggle to make Tab key insert a tab character in the editor instead of changing focus
+- **List & quote continuation (Enter)** - At the end of a line, Enter continues blockquotes (`>`), bullets (`-` / `*` / `+`), and task lists (`- [ ]` / `- [x]`); pressing Enter again on an empty continued line exits the list or quote. Disabled inside fenced code blocks and when using Shift+Enter (plain newline)
### Export & Print
- **HTML Export** - Download notes as standalone HTML files with all styling, images, diagrams, and math embedded
diff --git a/frontend/app.js b/frontend/app.js
index 7fced06..ae22d18 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -857,6 +857,31 @@ function noteApp() {
this.autoSave();
},
+ /**
+ * Enter: continue blockquote / bullet / task list; second Enter on empty item exits (see editor-markdown-continue.js).
+ */
+ handleEditorEnterKey(event) {
+ if (typeof EditorMarkdownContinue === 'undefined') return;
+ const textarea = event.target;
+ if (!textarea || textarea.id !== 'note-editor') return;
+
+ const result = EditorMarkdownContinue.tryEnter(
+ this.noteContent,
+ textarea.selectionStart,
+ textarea.selectionEnd,
+ event
+ );
+ if (!result.handled) return;
+
+ event.preventDefault();
+ this.noteContent = result.text;
+ this.$nextTick(() => {
+ textarea.selectionStart = textarea.selectionEnd = result.cursor;
+ });
+ this.autoSave();
+ this.updateSyntaxHighlight();
+ },
+
// Sort mode configuration
sortModes: ['a-z', 'z-a', 'newest', 'oldest', 'largest', 'smallest'],
sortModeIcons: {
diff --git a/frontend/editor-markdown-continue.js b/frontend/editor-markdown-continue.js
new file mode 100644
index 0000000..eac57c5
--- /dev/null
+++ b/frontend/editor-markdown-continue.js
@@ -0,0 +1,124 @@
+/**
+ * Markdown editor helpers: Enter continues blockquotes, bullets, and task lists.
+ * Plain functions only; no DOM. Used by app.js handleEditorEnterKey.
+ */
+(function (global) {
+ 'use strict';
+
+ /**
+ * True if cursor position lies inside a ``` fenced code block (toggle per line).
+ */
+ function cursorInMarkdownCodeFence(text, pos) {
+ const before = text.slice(0, pos);
+ let lineStart = 0;
+ let inFence = false;
+ for (;;) {
+ const nl = before.indexOf('\n', lineStart);
+ const end = nl === -1 ? before.length : nl;
+ const line = before.slice(lineStart, end);
+ const trimmed = line.trimStart();
+ if (trimmed.startsWith('```')) {
+ inFence = !inFence;
+ }
+ if (nl === -1) break;
+ lineStart = nl + 1;
+ }
+ return inFence;
+ }
+
+ function getLineBounds(text, pos) {
+ const lineStart = text.lastIndexOf('\n', Math.max(0, pos - 1)) + 1;
+ let lineEnd = text.indexOf('\n', pos);
+ if (lineEnd === -1) lineEnd = text.length;
+ return {
+ lineStart,
+ lineEnd,
+ line: text.slice(lineStart, lineEnd),
+ };
+ }
+
+ /**
+ * @returns {{ continuePrefix: string, isEmpty: boolean } | null}
+ */
+ function parseContinuationContext(line) {
+ const quoteMatch = line.match(/^(\s*)((?:>\s*)+)/);
+ let quoteFull = '';
+ let rest = line;
+ if (quoteMatch) {
+ quoteFull = quoteMatch[1] + quoteMatch[2];
+ rest = line.slice(quoteMatch[0].length);
+ }
+
+ const taskMatch = rest.match(/^(\s*)([-*+])\s+\[([xX ])\]\s*(.*)$/);
+ if (taskMatch) {
+ const inner = taskMatch[1];
+ const ch = taskMatch[2];
+ const mark = taskMatch[3];
+ const cont = taskMatch[4];
+ const marker = ch + ' [' + mark + '] ';
+ const isEmpty = cont.trim() === '';
+ return { continuePrefix: quoteFull + inner + marker, isEmpty };
+ }
+
+ const bulletMatch = rest.match(/^(\s*)([-*+])\s+(.*)$/);
+ if (bulletMatch) {
+ const inner = bulletMatch[1];
+ const ch = bulletMatch[2];
+ const cont = bulletMatch[3];
+ const marker = ch + ' ';
+ const isEmpty = cont.trim() === '';
+ return { continuePrefix: quoteFull + inner + marker, isEmpty };
+ }
+
+ if (quoteFull) {
+ const isEmpty = rest.trim() === '';
+ return { continuePrefix: quoteFull, isEmpty };
+ }
+
+ return null;
+ }
+
+ /**
+ * @param {string} text
+ * @param {number} selStart
+ * @param {number} selEnd
+ * @param {KeyboardEvent} event
+ * @returns {{ handled: false } | { handled: true, text: string, cursor: number }}
+ */
+ function tryEnter(text, selStart, selEnd, event) {
+ if (event.key !== 'Enter') return { handled: false };
+ if (event.defaultPrevented) return { handled: false };
+ if (event.isComposing) return { handled: false };
+ if (event.shiftKey) return { handled: false };
+ if (event.ctrlKey || event.metaKey || event.altKey) return { handled: false };
+ if (selStart !== selEnd) return { handled: false };
+ if (cursorInMarkdownCodeFence(text, selStart)) return { handled: false };
+
+ const { lineStart, lineEnd, line } = getLineBounds(text, selStart);
+ if (selStart !== lineEnd) return { handled: false };
+
+ const lineForParse = line.replace(/\r/g, '');
+ const ctx = parseContinuationContext(lineForParse);
+ if (!ctx) return { handled: false };
+
+ if (ctx.isEmpty) {
+ let delEnd = lineEnd;
+ if (delEnd < text.length && text.charAt(delEnd) === '\n') {
+ delEnd += 1;
+ }
+ const newText = text.slice(0, lineStart) + text.slice(delEnd);
+ return { handled: true, text: newText, cursor: lineStart };
+ }
+
+ const insert = '\n' + ctx.continuePrefix;
+ const newText = text.slice(0, lineEnd) + insert + text.slice(lineEnd);
+ return { handled: true, text: newText, cursor: lineEnd + insert.length };
+ }
+
+ global.EditorMarkdownContinue = {
+ tryEnter,
+ parseContinuationContext,
+ getLineBounds,
+ cursorInMarkdownCodeFence,
+ };
+})(typeof window !== 'undefined' ? window : globalThis);
diff --git a/frontend/index.html b/frontend/index.html
index 1e7aa1f..609f662 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2682,6 +2682,7 @@
x-model="noteContent"
@input="autoSave(); updateSyntaxHighlight()"
@scroll="syncOverlayScroll()"
+ @keydown.enter="handleEditorEnterKey($event)"
@keydown.tab="handleTabKey($event)"
@drop="onEditorDrop($event)"
@dragover.prevent="onEditorDragOver($event)"
@@ -3433,6 +3434,7 @@
+