diff --git a/.prettierignore b/.prettierignore index 2924630..adefcf6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -98,4 +98,8 @@ docs/build/ # Temporary folders tmp/ -temp/ \ No newline at end of file +temp/ + +# Ignore Docker-related files for Prettier +motia/Dockerfile +motia/.dockerignore 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 = () => '/g, - '' - ) - // Code blocks - handle pre > code blocks first - .replace( - /') - // Inline code - only for code not inside pre blocks - .replace( - /<\/pre>/g, '/g, '') - .replace(/
/g, '
') - .replace(/
/g, '
') - .replace(/
- ') - // Tables - .replace(/
/g, '
') - .replace(//g, '') - .replace(/
/g, ' ') - .replace(/ /g, ' ') - // Paragraphs - .replace(/ /g, '