From 9f7a2edf50e71bf5dcb02b5829a6c9a2cd62815f Mon Sep 17 00:00:00 2001 From: Bhavani Saw Date: Thu, 30 Oct 2025 23:31:51 +0530 Subject: [PATCH 1/3] fix: auto bullet continuation and removed stray '>' before list items --- frontend/package-lock.json | 7 + frontend/package.json | 1 + frontend/src/components/dashboard/Note.jsx | 96 +++++++---- frontend/src/components/home/ExampleNote.jsx | 10 +- frontend/src/utils/markdownRenderer.js | 161 +++++++++---------- 5 files changed, 154 insertions(+), 121 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 101c931..5e199cd 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,6 +19,7 @@ "lucide-react": "^0.545.0", "markdown-it": "^14.1.0", "markdown-it-deflist": "^3.0.0", + "markdown-it-link-attributes": "^4.0.1", "markdown-it-task-lists": "^2.1.1", "prop-types": "^15.8.1", "react": "^18.3.1", @@ -4233,6 +4234,12 @@ "integrity": "sha512-OxPmQ/keJZwbubjiQWOvKLHwpV2wZ5I3Smc81OjhwbfJsjdRrvD5aLTQxmZzzePeO0kbGzAo3Krk4QLgA8PWLg==", "license": "MIT" }, + "node_modules/markdown-it-link-attributes": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz", + "integrity": "sha512-pg5OK0jPLg62H4k7M9mRJLT61gUp9nvG0XveKYHMOOluASo9OEF13WlXrpAp2aj35LbedAy3QOCgQCw0tkLKAQ==", + "license": "MIT" + }, "node_modules/markdown-it-task-lists": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index d2c96c6..cf76e6a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "lucide-react": "^0.545.0", "markdown-it": "^14.1.0", "markdown-it-deflist": "^3.0.0", + "markdown-it-link-attributes": "^4.0.1", "markdown-it-task-lists": "^2.1.1", "prop-types": "^15.8.1", "react": "^18.3.1", diff --git a/frontend/src/components/dashboard/Note.jsx b/frontend/src/components/dashboard/Note.jsx index 885973b..8937de2 100644 --- a/frontend/src/components/dashboard/Note.jsx +++ b/frontend/src/components/dashboard/Note.jsx @@ -188,41 +188,75 @@ const Note = ({ activePage, onContentChange, content = '', onSave }) => { }; const handleKeyDown = (e) => { - if (e.ctrlKey || e.metaKey) { - switch (e.key.toLowerCase()) { - case 'z': - if (e.shiftKey) { - e.preventDefault(); - handleRedo(); - } else { - e.preventDefault(); - handleUndo(); - } - break; - case 'y': + // 🔹 Auto-insert bullet or number on new line for lists + if (e.key === 'Enter') { + const textarea = e.target; + const { selectionStart, selectionEnd, value } = textarea; + + // Get current line text before cursor + const beforeCursor = value.substring(0, selectionStart); + const currentLine = beforeCursor.split('\n').pop(); + + // Match bullet (-, *, +) or numbered list (1.) + const match = currentLine.match(/^(\s*[-*+]|\s*\d+\.)\s+/); + + if (match) { + e.preventDefault(); + const bullet = match[0]; + const newValue = + value.substring(0, selectionStart) + + `\n${bullet}` + + value.substring(selectionEnd); + + setEditorContent(newValue); + addToHistory(newValue); + + // Move cursor to the right position after the bullet + requestAnimationFrame(() => { + const newPos = selectionStart + bullet.length + 1; + textarea.selectionStart = textarea.selectionEnd = newPos; + }); + return; // Stop further key handling + } + } + + // 🔹 Handle keyboard shortcuts (undo, redo, bold, italic, save) + if (e.ctrlKey || e.metaKey) { + switch (e.key.toLowerCase()) { + case 'z': + if (e.shiftKey) { e.preventDefault(); handleRedo(); - break; - case 'b': - e.preventDefault(); - wrapSelectedText('**', '**', 'bold text'); - break; - case 'i': - e.preventDefault(); - wrapSelectedText('*', '*', 'italic text'); - break; - case 's': + } else { e.preventDefault(); - if (onSave) { - onSave(); - toast.success('Note saved!'); - } - break; - default: - break; - } + handleUndo(); + } + break; + case 'y': + e.preventDefault(); + handleRedo(); + break; + case 'b': + e.preventDefault(); + wrapSelectedText('**', '**', 'bold text'); + break; + case 'i': + e.preventDefault(); + wrapSelectedText('*', '*', 'italic text'); + break; + case 's': + e.preventDefault(); + if (onSave) { + onSave(); + toast.success('Note saved!'); + } + break; + default: + break; } - }; + } +}; + const handleContentChange = (e) => { const newContent = e.target.value; diff --git a/frontend/src/components/home/ExampleNote.jsx b/frontend/src/components/home/ExampleNote.jsx index c5988e8..16bc9a8 100644 --- a/frontend/src/components/home/ExampleNote.jsx +++ b/frontend/src/components/home/ExampleNote.jsx @@ -59,11 +59,11 @@ const ExampleNote = () => { Private • Edited {timeAgo} -

+

Quick summary: Decide migration plan for the frontend — start with a Vite parallel folder, migrate auth pages first, then dashboard. Public share will be read-only links.

diff --git a/frontend/src/utils/markdownRenderer.js b/frontend/src/utils/markdownRenderer.js index 7316f54..800ffa3 100644 --- a/frontend/src/utils/markdownRenderer.js +++ b/frontend/src/utils/markdownRenderer.js @@ -21,7 +21,6 @@ const md = new MarkdownIt({ return hljs.highlight(code, { language: lang, ignoreIllegals: true }).value; } return hljs.highlightAuto(code).value; - // eslint-disable-next-line no-unused-vars } catch (err) { return code; } @@ -31,7 +30,6 @@ const md = new MarkdownIt({ enabled: true, label: false, labelAfter: false, - // Let the plugin handle its own classes itemClass: 'task-list-item', containerClass: 'contains-task-list', }) @@ -51,24 +49,22 @@ md.inline.ruler.push('strikethrough', (state) => { let pos = start + 2; while (pos < state.posMax) { - if (state.src.charCodeAt(pos) === marker) { - if (state.src.charCodeAt(pos + 1) === marker) { - const token = state.push('strikethrough_open', 'del', 1); - token.markup = '~~'; + if (state.src.charCodeAt(pos) === marker && state.src.charCodeAt(pos + 1) === marker) { + const token = state.push('strikethrough_open', 'del', 1); + token.markup = '~~'; - state.pos = start + 2; - const oldPos = state.pos; - state.pos = pos; + state.pos = start + 2; + const oldPos = state.pos; + state.pos = pos; - const content = state.src.slice(oldPos, pos); - state.push('text', content, 0); + const content = state.src.slice(oldPos, pos); + state.push('text', content, 0); - const closeToken = state.push('strikethrough_close', 'del', -1); - closeToken.markup = '~~'; + const closeToken = state.push('strikethrough_close', 'del', -1); + closeToken.markup = '~~'; - state.pos = pos + 2; - return true; - } + state.pos = pos + 2; + return true; } pos++; } @@ -101,7 +97,6 @@ md.inline.ruler.push('highlight', (state) => { return true; }); -// renderer rules for strikethrough and highlight md.renderer.rules.strikethrough_open = () => ''; md.renderer.rules.strikethrough_close = () => ''; md.renderer.rules.highlight_open = () => ''; @@ -109,6 +104,7 @@ md.renderer.rules.highlight_close = () => ''; /** * Custom rule for inline math using $formula$ + * Uses KaTeX to render inline math expressions * @param {Object} state - markdown-it state object * @returns {boolean} True if formula was rendered */ @@ -119,7 +115,7 @@ md.inline.ruler.push('math_inline', (state) => { let end = start + 1; while (end < state.src.length && state.src[end] !== '$') { if (state.src[end] === '\\' && end + 1 < state.src.length) { - end += 2; // Skip escaped characters + end += 2; continue; } end++; @@ -132,17 +128,15 @@ md.inline.ruler.push('math_inline', (state) => { try { const rendered = katex.renderToString(content, { - // Render the math content using KaTeX displayMode: false, throwOnError: false, }); - const token = state.push('math_inline', 'span', 0); // new token for the rendered math + const token = state.push('math_inline', 'span', 0); token.content = rendered; state.pos = end + 1; return true; - // eslint-disable-next-line no-unused-vars } catch (err) { return false; } @@ -157,65 +151,63 @@ md.renderer.rules.math_inline = (tokens, idx) => tokens[idx].content; * @returns {string} HTML string with Tailwind classes added */ const addTailwindClasses = (html) => { - return ( - html - // Headings - .replace(/

/g, '

') - .replace(/

/g, '

') - .replace(/

/g, '

') - .replace(/

/g, '

') - .replace(/

/g, '
') - .replace(/
/g, '
') - // Text formatting - .replace(//g, '') - .replace(//g, '') - // Blockquotes - .replace( - /
/g, - '
' - ) - // Code blocks - handle pre > code blocks first - .replace( - /
<\/pre>/g, '
') - // Inline code - only for code not inside pre blocks - .replace( - //g, '