From 3a50cbebdd8cf08755312f0ec60dc4d98d164d60 Mon Sep 17 00:00:00 2001 From: Joshua Ryder Date: Fri, 7 Nov 2025 09:47:23 -0500 Subject: [PATCH] feat: Add rich text formatting to sermon notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement comprehensive rich text editing capabilities using Tiptap editor with full formatting toolbar and functionality. Features: - Bold, italic, underline, strikethrough text formatting - Highlight text with yellow marker - Bullet and numbered lists - Heading styles (H2, H3) - Text alignment (left, center, right) - Undo/redo support - Clear formatting option - Intuitive toolbar with icons and tooltips - Responsive font size support Technical changes: - Added Tiptap dependencies to package.json (@tiptap/vue-3, starter-kit, extensions) - Created RichTextEditor component with full toolbar and formatting options - Integrated editor into sermon notes section replacing textarea - Updated download API to convert HTML to formatted plain text - Updated email API to handle HTML content properly - Notes now stored as HTML with rich formatting preserved The editor provides a professional writing experience with all standard formatting tools while maintaining automatic save functionality and seamless integration with email/download features. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- components/RichTextEditor.vue | 309 ++++++++++++++++++++ package.json | 5 + pages/[slug].vue | 10 +- server/api/notes/download/[sermonId].get.ts | 21 +- server/api/notes/email/[sermonId].post.ts | 5 +- 5 files changed, 340 insertions(+), 10 deletions(-) create mode 100644 components/RichTextEditor.vue diff --git a/components/RichTextEditor.vue b/components/RichTextEditor.vue new file mode 100644 index 0000000..d54af39 --- /dev/null +++ b/components/RichTextEditor.vue @@ -0,0 +1,309 @@ + + + + + diff --git a/package.json b/package.json index fc3c6c0..53eea20 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,11 @@ "postinstall": "nuxt prepare" }, "dependencies": { + "@tiptap/extension-highlight": "^2.10.3", + "@tiptap/extension-text-align": "^2.10.3", + "@tiptap/extension-underline": "^2.10.3", + "@tiptap/starter-kit": "^2.10.3", + "@tiptap/vue-3": "^2.10.3", "bcrypt": "^5.1.1", "better-sqlite3": "^11.3.0", "nodemailer": "^6.9.7", diff --git a/pages/[slug].vue b/pages/[slug].vue index ffcc24c..9264a9c 100644 --- a/pages/[slug].vue +++ b/pages/[slug].vue @@ -117,13 +117,11 @@

⚠️ Notes are deleted if a Sermon is deleted by an admin. Please email or download notes to have a copy sent to you.

- + @update:modelValue="handleNotesChange" + :editorClass="fontSizeClasses" + />

{{ saveStatus }} diff --git a/server/api/notes/download/[sermonId].get.ts b/server/api/notes/download/[sermonId].get.ts index aa1bcb0..1a222ee 100644 --- a/server/api/notes/download/[sermonId].get.ts +++ b/server/api/notes/download/[sermonId].get.ts @@ -42,7 +42,26 @@ export default defineEventHandler(async (event) => { // Get user's notes const noteRecord = getSermonNote(user.id!, sermonId) - const userNotes = noteRecord?.notes || 'No notes taken' + // Convert HTML to plain text for download + const htmlToText = (html: string) => { + if (!html) return 'No notes taken' + return html + .replace(//gi, '\n') + .replace(/<\/p>/gi, '\n') + .replace(/]*>/gi, '') + .replace(/<\/h[1-6]>/gi, '\n') + .replace(/]*>/gi, '') + .replace(/]*>/gi, '• ') + .replace(/<\/li>/gi, '\n') + .replace(/<[^>]+>/g, '') + .replace(/ /g, ' ') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .trim() + } + const userNotes = htmlToText(noteRecord?.notes || '') // Format bible references let bibleReferencesText = '' diff --git a/server/api/notes/email/[sermonId].post.ts b/server/api/notes/email/[sermonId].post.ts index 46bedb9..dce6812 100644 --- a/server/api/notes/email/[sermonId].post.ts +++ b/server/api/notes/email/[sermonId].post.ts @@ -41,10 +41,9 @@ export default defineEventHandler(async (event) => { }) } - // Get user's notes + // Get user's notes (already stored as HTML from rich text editor) const noteRecord = getSermonNote(user.id!, sermonId) - // Convert line breaks to HTML breaks for email display - const userNotes = noteRecord?.notes ? noteRecord.notes.replace(/\n/g, '
') : '' + const userNotes = noteRecord?.notes || '

No notes taken

' // Format bible references for HTML email let bibleReferencesText = ''