courrier refactor rebuild 2

This commit is contained in:
alma 2025-04-27 10:25:20 +02:00
parent b036530766
commit a51a4c303d
8 changed files with 436 additions and 28 deletions

View File

@ -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;
}

View File

@ -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 */}
<div className="flex-1 min-h-[200px] flex flex-col">
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
<div className="flex-1 flex flex-col border border-gray-300 rounded-md overflow-hidden">
<div
className="flex-1 w-full bg-white p-4 text-black overflow-y-auto email-content-display"
style={{
minHeight: '200px',
maxHeight: 'calc(100vh - 400px)'
}}
>
<div dangerouslySetInnerHTML={{ __html: emailContent }} />
</div>
<textarea
className="hidden"
value={emailContent}
onChange={(e) => setEmailContent(e.target.value)}
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden">
<RichEmailEditor
initialContent={emailContent}
onChange={setEmailContent}
minHeight="200px"
maxHeight="calc(100vh - 400px)"
/>
</div>
</div>
@ -626,20 +619,12 @@ function LegacyAdapter({
{/* Message Body */}
<div className="flex-1 min-h-[200px] flex flex-col">
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
<div className="flex-1 flex flex-col border border-gray-300 rounded-md overflow-hidden">
<div
className="flex-1 w-full bg-white p-4 text-black overflow-y-auto email-content-display"
style={{
minHeight: '200px',
maxHeight: 'calc(100vh - 400px)'
}}
>
<div dangerouslySetInnerHTML={{ __html: composeBody }} />
</div>
<textarea
className="hidden"
value={composeBody}
onChange={(e) => setComposeBody(e.target.value)}
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden">
<RichEmailEditor
initialContent={composeBody}
onChange={setComposeBody}
minHeight="200px"
maxHeight="calc(100vh - 400px)"
/>
</div>
</div>

View File

@ -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<RichEmailEditorProps> = ({
initialContent,
onChange,
placeholder = 'Write your message here...',
minHeight = '200px',
maxHeight = 'calc(100vh - 400px)',
}) => {
const editorRef = useRef<HTMLDivElement>(null);
const quillRef = useRef<any>(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 (
<div className="rich-email-editor-container">
{/* Quill container */}
<div
ref={editorRef}
className="quill-editor"
style={{
height: 'auto',
minHeight: minHeight,
maxHeight: maxHeight
}}
/>
{/* Loading indicator */}
{!isReady && (
<div className="flex items-center justify-center py-8">
<div className="h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent"></div>
</div>
)}
{/* Custom styles for email context */}
<style jsx>{`
.rich-email-editor-container {
display: flex;
flex-direction: column;
width: 100%;
border-radius: 6px;
overflow: hidden;
flex: 1;
}
.quill-editor {
width: 100%;
flex: 1;
}
/* Hide the editor until it's ready */
.quill-editor ${!isReady ? '{ display: none; }' : ''}
`}</style>
</div>
);
};
export default RichEmailEditor;

View File

@ -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 `
<p></p>
<p>On ${originalDate}, ${originalSender} wrote:</p>
<blockquote class="quoted-content">
${sanitizedBody}
</blockquote>
`.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 `
<p></p>
<p>---------- Forwarded message ---------</p>
<p><strong>From:</strong> ${originalSender}</p>
<p><strong>Date:</strong> ${originalDate}</p>
<p><strong>Subject:</strong> ${originalSubject}</p>
<p><strong>To:</strong> ${originalRecipients}</p>
<br>
<div class="email-original-content">
${sanitizedBody}
</div>
`.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}`;
}

66
node_modules/.package-lock.json generated vendored
View File

@ -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",

67
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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"