diff --git a/app/globals.css b/app/globals.css
index 3590765a..45ac427b 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -216,3 +216,42 @@
word-break: break-word;
}
+/* Quill editor customizations for email composition */
+.ql-editor {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+/* Quote formatting for forwarded/replied emails */
+.ql-editor blockquote {
+ border-left: 3px solid #ddd;
+ padding-left: 10px;
+ margin: 8px 0;
+ color: #555;
+}
+
+/* Forward message formatting */
+.ql-editor .forward-header {
+ margin-bottom: 10px;
+ color: #333;
+ font-family: Arial, sans-serif;
+}
+
+/* Make sure the quoted content is properly indented */
+.ql-editor .email-original-content {
+ margin-top: 10px;
+}
+
+/* Fix toolbar button styling */
+.ql-toolbar.ql-snow {
+ border-top: none;
+ border-left: none;
+ border-right: none;
+ border-bottom: 1px solid #e5e7eb;
+}
+
+.ql-container.ql-snow {
+ border: none;
+}
+
diff --git a/components/email/ComposeEmail.tsx b/components/email/ComposeEmail.tsx
index 8811e806..3d4757b2 100644
--- a/components/email/ComposeEmail.tsx
+++ b/components/email/ComposeEmail.tsx
@@ -15,6 +15,7 @@ import { Label } from '@/components/ui/label';
import ComposeEmailHeader from './ComposeEmailHeader';
import ComposeEmailForm from './ComposeEmailForm';
import ComposeEmailFooter from './ComposeEmailFooter';
+import RichEmailEditor from './RichEmailEditor';
// Import ONLY from the centralized formatter
import {
@@ -332,20 +333,12 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
{/* Message Body */}
-
@@ -626,20 +619,12 @@ function LegacyAdapter({
{/* Message Body */}
-
diff --git a/components/email/RichEmailEditor.tsx b/components/email/RichEmailEditor.tsx
new file mode 100644
index 00000000..04f8c464
--- /dev/null
+++ b/components/email/RichEmailEditor.tsx
@@ -0,0 +1,132 @@
+'use client';
+
+import React, { useEffect, useRef, useState } from 'react';
+import 'quill/dist/quill.snow.css';
+import { sanitizeHtml } from '@/lib/utils/email-formatter';
+
+interface RichEmailEditorProps {
+ initialContent: string;
+ onChange: (content: string) => void;
+ placeholder?: string;
+ minHeight?: string;
+ maxHeight?: string;
+}
+
+const RichEmailEditor: React.FC
= ({
+ initialContent,
+ onChange,
+ placeholder = 'Write your message here...',
+ minHeight = '200px',
+ maxHeight = 'calc(100vh - 400px)',
+}) => {
+ const editorRef = useRef(null);
+ const quillRef = useRef(null);
+ const [isReady, setIsReady] = useState(false);
+
+ // Initialize Quill editor when component mounts
+ useEffect(() => {
+ // Import Quill dynamically (client-side only)
+ const initializeQuill = async () => {
+ if (!editorRef.current) return;
+
+ const Quill = (await import('quill')).default;
+
+ // Define custom formats/modules as needed for email
+ const emailToolbarOptions = [
+ ['bold', 'italic', 'underline', 'strike'],
+ [{ 'color': [] }, { 'background': [] }],
+ [{ 'list': 'ordered'}, { 'list': 'bullet' }],
+ [{ 'indent': '-1'}, { 'indent': '+1' }],
+ [{ 'align': [] }],
+ ['link'],
+ ['clean'],
+ ];
+
+ // Create new Quill instance with the DOM element
+ const editorElement = editorRef.current;
+ quillRef.current = new Quill(editorElement, {
+ modules: {
+ toolbar: emailToolbarOptions
+ },
+ placeholder: placeholder,
+ theme: 'snow',
+ });
+
+ // Set initial content (sanitized)
+ if (initialContent) {
+ quillRef.current.clipboard.dangerouslyPasteHTML(sanitizeHtml(initialContent));
+ }
+
+ // Add change listener
+ quillRef.current.on('text-change', () => {
+ const html = quillRef.current.root.innerHTML;
+ onChange(html);
+ });
+
+ setIsReady(true);
+ };
+
+ initializeQuill();
+
+ // Clean up on unmount
+ return () => {
+ if (quillRef.current) {
+ // Clean up any event listeners or resources
+ quillRef.current.off('text-change');
+ }
+ };
+ }, []);
+
+ // Update content from props if changed externally
+ useEffect(() => {
+ if (quillRef.current && isReady) {
+ const currentContent = quillRef.current.root.innerHTML;
+ if (initialContent !== currentContent) {
+ quillRef.current.clipboard.dangerouslyPasteHTML(sanitizeHtml(initialContent));
+ }
+ }
+ }, [initialContent, isReady]);
+
+ return (
+
+ {/* Quill container */}
+
+
+ {/* Loading indicator */}
+ {!isReady && (
+
+ )}
+
+ {/* Custom styles for email context */}
+
+
+ );
+};
+
+export default RichEmailEditor;
\ No newline at end of file
diff --git a/lib/utils/email-formatter.ts b/lib/utils/email-formatter.ts
index de951d64..f5840be2 100644
--- a/lib/utils/email-formatter.ts
+++ b/lib/utils/email-formatter.ts
@@ -9,6 +9,8 @@
*/
import DOMPurify from 'isomorphic-dompurify';
+import sanitizeHtml from 'sanitize-html';
+import { formatDateRelative } from './date-formatter';
// Reset any existing hooks to start clean
DOMPurify.removeAllHooks();
@@ -364,4 +366,71 @@ export function encodeComposeContent(content: string): string {
return Object.entries(mimeHeaders)
.map(([key, value]) => `${key}: ${value}`)
.join('\n') + '\n\n' + content;
+}
+
+// Email formatter functions for various email actions
+export function formatReplyEmail(email: any): string {
+ const originalSender = email.sender?.name || email.sender?.email || 'Unknown Sender';
+ const originalDate = formatDateRelative(new Date(email.date));
+
+ const sanitizedBody = sanitizeHtml(email.content || '', {
+ allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
+ allowedAttributes: {
+ ...sanitizeHtml.defaults.allowedAttributes,
+ img: ['src', 'alt', 'width', 'height']
+ }
+ });
+
+ return `
+
+On ${originalDate}, ${originalSender} wrote:
+
+ ${sanitizedBody}
+
+ `.trim();
+}
+
+export function formatForwardedEmail(email: any): string {
+ const originalSender = email.sender?.name || email.sender?.email || 'Unknown Sender';
+ const originalRecipients = email.to?.map((recipient: any) =>
+ recipient.name || recipient.email
+ ).join(', ') || 'Unknown Recipients';
+ const originalDate = formatDateRelative(new Date(email.date));
+ const originalSubject = email.subject || 'No Subject';
+
+ const sanitizedBody = sanitizeHtml(email.content || '', {
+ allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
+ allowedAttributes: {
+ ...sanitizeHtml.defaults.allowedAttributes,
+ img: ['src', 'alt', 'width', 'height']
+ }
+ });
+
+ return `
+
+---------- Forwarded message ---------
+From: ${originalSender}
+Date: ${originalDate}
+Subject: ${originalSubject}
+To: ${originalRecipients}
+
+
+ ${sanitizedBody}
+
+ `.trim();
+}
+
+export function formatReplyToAllEmail(email: any): string {
+ // For reply all, we use the same format as regular reply
+ return formatReplyEmail(email);
+}
+
+// Utility function to get the reply subject line
+export function getReplySubject(subject: string): string {
+ return subject.startsWith('Re:') ? subject : `Re: ${subject}`;
+}
+
+// Utility function to get the forward subject line
+export function getForwardSubject(subject: string): string {
+ return subject.startsWith('Fwd:') ? subject : `Fwd: ${subject}`;
}
\ No newline at end of file
diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json
index 406d87d7..6be0d58e 100644
--- a/node_modules/.package-lock.json
+++ b/node_modules/.package-lock.json
@@ -3584,6 +3584,12 @@
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "license": "Apache-2.0"
+ },
"node_modules/fast-equals": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
@@ -4472,6 +4478,18 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+ "license": "MIT"
+ },
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -4479,6 +4497,13 @@
"deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
"license": "MIT"
},
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
+ "license": "MIT"
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -4955,6 +4980,12 @@
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
},
+ "node_modules/parchment": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
+ "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/parse5": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
@@ -5576,6 +5607,41 @@
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
"license": "MIT"
},
+ "node_modules/quill": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
+ "integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "eventemitter3": "^5.0.1",
+ "lodash-es": "^4.17.21",
+ "parchment": "^3.0.0",
+ "quill-delta": "^5.1.0"
+ },
+ "engines": {
+ "npm": ">=8.2.3"
+ }
+ },
+ "node_modules/quill-delta": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
+ "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-diff": "^1.3.0",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.isequal": "^4.5.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/quill/node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "license": "MIT"
+ },
"node_modules/quoted-printable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/quoted-printable/-/quoted-printable-1.0.1.tgz",
diff --git a/package-lock.json b/package-lock.json
index 9478000d..4073f4e5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -70,6 +70,7 @@
"next-themes": "^0.4.4",
"nodemailer": "^6.10.1",
"pg": "^8.14.1",
+ "quill": "^2.0.3",
"react": "^18",
"react-datepicker": "^8.3.0",
"react-day-picker": "8.10.1",
@@ -4552,6 +4553,12 @@
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "license": "Apache-2.0"
+ },
"node_modules/fast-equals": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
@@ -5440,6 +5447,18 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+ "license": "MIT"
+ },
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -5447,6 +5466,13 @@
"deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
"license": "MIT"
},
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
+ "license": "MIT"
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -5923,6 +5949,12 @@
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
},
+ "node_modules/parchment": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
+ "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/parse5": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
@@ -6544,6 +6576,41 @@
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
"license": "MIT"
},
+ "node_modules/quill": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
+ "integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "eventemitter3": "^5.0.1",
+ "lodash-es": "^4.17.21",
+ "parchment": "^3.0.0",
+ "quill-delta": "^5.1.0"
+ },
+ "engines": {
+ "npm": ">=8.2.3"
+ }
+ },
+ "node_modules/quill-delta": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
+ "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-diff": "^1.3.0",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.isequal": "^4.5.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/quill/node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "license": "MIT"
+ },
"node_modules/quoted-printable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/quoted-printable/-/quoted-printable-1.0.1.tgz",
diff --git a/package.json b/package.json
index fdd10a65..3e4e28aa 100644
--- a/package.json
+++ b/package.json
@@ -71,6 +71,7 @@
"next-themes": "^0.4.4",
"nodemailer": "^6.10.1",
"pg": "^8.14.1",
+ "quill": "^2.0.3",
"react": "^18",
"react-datepicker": "^8.3.0",
"react-day-picker": "8.10.1",
diff --git a/yarn.lock b/yarn.lock
index e46b137a..cb3a9d19 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1835,6 +1835,16 @@ eventemitter3@^4.0.1:
resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+eventemitter3@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz"
+ integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+
+fast-diff@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz"
+ integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
+
fast-equals@^5.0.1:
version "5.2.2"
resolved "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz"
@@ -2355,11 +2365,26 @@ linkify-it@5.0.0:
dependencies:
uc.micro "^2.0.0"
+lodash-es@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz"
+ integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+
+lodash.clonedeep@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz"
+ integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz"
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
+lodash.isequal@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz"
+ integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
+
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
@@ -2647,6 +2672,11 @@ package-json-from-dist@^1.0.0:
resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz"
integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
+parchment@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz"
+ integrity sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==
+
parse5@^7.0.0, parse5@^7.2.1:
version "7.2.1"
resolved "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz"
@@ -3001,6 +3031,25 @@ quick-format-unescaped@^4.0.3:
resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz"
integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==
+quill-delta@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz"
+ integrity sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==
+ dependencies:
+ fast-diff "^1.3.0"
+ lodash.clonedeep "^4.5.0"
+ lodash.isequal "^4.5.0"
+
+quill@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz"
+ integrity sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==
+ dependencies:
+ eventemitter3 "^5.0.1"
+ lodash-es "^4.17.21"
+ parchment "^3.0.0"
+ quill-delta "^5.1.0"
+
quoted-printable@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/quoted-printable/-/quoted-printable-1.0.1.tgz"