diff --git a/apps/meteor/app/ui-message/client/messageBox/createQuillComposerAPI.ts b/apps/meteor/app/ui-message/client/messageBox/createQuillComposerAPI.ts new file mode 100644 index 000000000000..1ace7c465aad --- /dev/null +++ b/apps/meteor/app/ui-message/client/messageBox/createQuillComposerAPI.ts @@ -0,0 +1,35 @@ +import type { QuillComposerAPI } from '../../../../client/lib/chats/ChatAPI'; + +const attributesMap = { + bold: '*', + italic: '_', + strike: '~', +}; + +export const createQuillComposerAPI = (): QuillComposerAPI => { + let text = ''; + + const setQuillDeltaToText = (getDelta: any) => { + const delta = getDelta(); + delta.ops.forEach((op: any) => { + const mdStack: string[] = []; + if (op.attributes) { + Object.keys(op.attributes).forEach((attribute: string) => { + mdStack.push(attribute); + text += attributesMap[attribute as keyof typeof attributesMap]; + }); + } + text += op.insert; + while (mdStack.length) { + text += attributesMap[mdStack.pop() as keyof typeof attributesMap]; + } + }); + }; + + return { + get text(): string { + return text; + }, + setQuillDeltaToText, + }; +}; diff --git a/apps/meteor/app/ui/client/lib/ChatMessages.ts b/apps/meteor/app/ui/client/lib/ChatMessages.ts index 59efeff49af1..b46c240877c2 100644 --- a/apps/meteor/app/ui/client/lib/ChatMessages.ts +++ b/apps/meteor/app/ui/client/lib/ChatMessages.ts @@ -3,7 +3,7 @@ import { isVideoConfMessage } from '@rocket.chat/core-typings'; import type { IActionManager } from '@rocket.chat/ui-contexts'; import type { UIEvent } from 'react'; -import type { ChatAPI, ComposerAPI, DataAPI, UploadsAPI } from '../../../../client/lib/chats/ChatAPI'; +import type { ChatAPI, ComposerAPI, DataAPI, QuillComposerAPI, UploadsAPI } from '../../../../client/lib/chats/ChatAPI'; import { createDataAPI } from '../../../../client/lib/chats/data'; import { processMessageEditing } from '../../../../client/lib/chats/flows/processMessageEditing'; import { processSetReaction } from '../../../../client/lib/chats/flows/processSetReaction'; @@ -32,11 +32,17 @@ export class ChatMessages implements ChatAPI { public composer: ComposerAPI | undefined; + public quillComposer?: QuillComposerAPI | undefined; + public setComposerAPI = (composer: ComposerAPI): void => { this.composer?.release(); this.composer = composer; }; + public setQuillComposerAPI = (quillComposer: QuillComposerAPI): void => { + this.quillComposer = quillComposer; + }; + public data: DataAPI; public readStateManager: ReadStateManager; diff --git a/apps/meteor/client/lib/chats/ChatAPI.ts b/apps/meteor/client/lib/chats/ChatAPI.ts index d2364cfcd72c..bfdf01ad5d4e 100644 --- a/apps/meteor/client/lib/chats/ChatAPI.ts +++ b/apps/meteor/client/lib/chats/ChatAPI.ts @@ -61,6 +61,11 @@ export type ComposerAPI = { readonly formatters: Subscribable; }; +export type QuillComposerAPI = { + text: string; + setQuillDeltaToText(getDelta: any): void; +}; + export type DataAPI = { composeMessage( text: string, @@ -107,7 +112,9 @@ export type UploadsAPI = { export type ChatAPI = { readonly uid: string | null; readonly composer?: ComposerAPI; + readonly quillComposer?: QuillComposerAPI; readonly setComposerAPI: (composer: ComposerAPI) => void; + readonly setQuillComposerAPI: (quillComposer: QuillComposerAPI) => void; readonly data: DataAPI; readonly uploads: UploadsAPI; readonly readStateManager: ReadStateManager; diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx index 043837f8c8c6..4158e9a4fbef 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx @@ -5,14 +5,15 @@ import { MessageComposerAction, MessageComposerToolbarActions, MessageComposer, - MessageComposerInput, + // MessageComposerInput, MessageComposerToolbar, MessageComposerActionsDivider, MessageComposerToolbarSubmit, MessageComposerHint, MessageComposerButton, + MessageComposerQuillInput, } from '@rocket.chat/ui-composer'; -import { useTranslation, useUserPreference, useLayout } from '@rocket.chat/ui-contexts'; +import { useTranslation, useUserPreference, useLayout, useQuill } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; import type { ReactElement, @@ -22,12 +23,14 @@ import type { KeyboardEvent, ClipboardEventHandler, MouseEvent, + Ref, } from 'react'; import React, { memo, useRef, useReducer, useCallback } from 'react'; import { Trans } from 'react-i18next'; import { useSubscription } from 'use-subscription'; import { createComposerAPI } from '../../../../../app/ui-message/client/messageBox/createComposerAPI'; +import { createQuillComposerAPI } from '../../../../../app/ui-message/client/messageBox/createQuillComposerAPI'; import type { FormattingButton } from '../../../../../app/ui-message/client/messageBox/messageBoxFormatting'; import { formattingButtons } from '../../../../../app/ui-message/client/messageBox/messageBoxFormatting'; import { getImageExtensionFromMime } from '../../../../../lib/getImageExtensionFromMime'; @@ -53,6 +56,7 @@ import MessageBoxFormattingToolbar from './MessageBoxFormattingToolbar'; import MessageBoxReplies from './MessageBoxReplies'; import { useMessageBoxAutoFocus } from './hooks/useMessageBoxAutoFocus'; import { useMessageBoxPlaceholder } from './hooks/useMessageBoxPlaceholder'; +import './MessageBoxQuill.css'; const reducer = (_: unknown, event: FormEvent): boolean => { const target = event.target as HTMLInputElement; @@ -139,6 +143,8 @@ const MessageBox = ({ const messageComposerRef = useRef(null); const shadowRef = useRef(null); + const { quillRef, getDelta } = useQuill(); + const storageID = `messagebox_${room._id}${tmid ? `-${tmid}` : ''}`; const callbackRef = useCallback( @@ -151,6 +157,16 @@ const MessageBox = ({ [chat, storageID], ); + const quillCallbackRef = useCallback( + (node: HTMLDivElement) => { + if (node === null || chat.quillComposer) { + return; + } + chat.setQuillComposerAPI(createQuillComposerAPI()); + }, + [chat], + ); + const autofocusRef = useMessageBoxAutoFocus(!isMobile); const useEmojis = useUserPreference('useEmojis'); @@ -168,7 +184,8 @@ const MessageBox = ({ }); const handleSendMessage = useMutableCallback(() => { - const text = chat.composer?.text ?? ''; + chat.quillComposer?.setQuillDeltaToText(getDelta); + const text = chat.quillComposer?.text ?? ''; chat.composer?.clear(); clearPopup(); @@ -353,7 +370,8 @@ const MessageBox = ({ configurations: composerPopupConfig, }); - const mergedRefs = useMessageComposerMergedRefs(c, textareaRef, callbackRef, autofocusRef); + // const mergedRefs = useMessageComposerMergedRefs(c, textareaRef, quillRef, callbackRef, autofocusRef, quillCallbackRef); + const qillMergedRefs = useMessageComposerMergedRefs(c, quillRef, callbackRef, autofocusRef, quillCallbackRef); const shouldPopupPreview = useEnablePopupPreview(filter, popup); @@ -399,7 +417,7 @@ const MessageBox = ({ {isRecordingVideo && } {isRecordingAudio && } - */} + } + aria-label={composerPlaceholder} + name='msg' + disabled={isRecording || !canSend} + onChange={setTyping} + style={textAreaStyle} + placeholder={composerPlaceholder} + onKeyDown={handler} + onPaste={handlePaste} + aria-activedescendant={ariaActiveDescendant} />
@@ -445,19 +475,19 @@ const MessageBox = ({ {t('Join')} )} - {canSend && ( - <> - {isEditing && {t('Cancel')}} - - - )} + {/* {canSend && ( */} + <> + {isEditing && {t('Cancel')}} + + + {/* )} */} diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx index afe8a58ec72a..10b1019b53bd 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx @@ -1,3 +1,4 @@ +import { Box } from '@rocket.chat/fuselage'; import { MessageComposerAction } from '@rocket.chat/ui-composer'; import { useTranslation } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; @@ -21,7 +22,7 @@ const MessageBoxFormattingToolbar = ({ items, variant = 'large', composer, disab const featuredFormatter = collapsedItems.splice(0, 1)[0]; return ( - <> + {'icon' in featuredFormatter && ( composer.wrapSelection(featuredFormatter.pattern)} @@ -30,12 +31,12 @@ const MessageBoxFormattingToolbar = ({ items, variant = 'large', composer, disab /> )} - + ); } return ( - <> + {items.map((formatter) => 'icon' in formatter ? ( ), )} - + ); }; diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxQuill.css b/apps/meteor/client/views/room/composer/messageBox/MessageBoxQuill.css new file mode 100644 index 000000000000..5fa9aeafa690 --- /dev/null +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxQuill.css @@ -0,0 +1,201 @@ +.ql-container { + box-sizing: border-box; + font-family: Helvetica, Arial, sans-serif; + font-size: 13px; + height: 100%; + margin: 0px; + position: relative; +} +.ql-clipboard { + left: -100000px; + height: 1px; + overflow-y: hidden; + position: absolute; + top: 50%; +} +.ql-clipboard p { + margin: 0; + padding: 0; +} +.ql-editor { + box-sizing: border-box; + line-height: 1.42; + height: 100%; + outline: none; + overflow-y: auto; + padding: 12px 15px; + tab-size: 4; + -moz-tab-size: 4; + text-align: left; + white-space: pre-wrap; + word-wrap: break-word; +} +.ql-editor > * { + cursor: text; +} +.ql-editor ol > li, +.ql-editor ul > li { + list-style-type: none; +} +.ql-editor ul > li::before { + content: '\2022'; +} +.ql-editor ul[data-checked=true], +.ql-editor ul[data-checked=false] { + pointer-events: none; +} +.ql-editor ul[data-checked=true] > li *, +.ql-editor ul[data-checked=false] > li * { + pointer-events: all; +} +.ql-editor ul[data-checked=true] > li::before, +.ql-editor ul[data-checked=false] > li::before { + color: #777; + cursor: pointer; + pointer-events: all; +} +.ql-editor ul[data-checked=true] > li::before { + content: '\2611'; +} +.ql-editor ul[data-checked=false] > li::before { + content: '\2610'; +} +.ql-editor li::before { + display: inline-block; + white-space: nowrap; + width: 1.2em; +} +.ql-editor li:not(.ql-direction-rtl)::before { + margin-left: -1.5em; + margin-right: 0.3em; + text-align: right; +} +.ql-editor li.ql-direction-rtl::before { + margin-left: 0.3em; + margin-right: -1.5em; +} +.ql-editor ol li:not(.ql-direction-rtl), +.ql-editor ul li:not(.ql-direction-rtl) { + padding-left: 1.5em; +} +.ql-editor ol li.ql-direction-rtl, +.ql-editor ul li.ql-direction-rtl { + padding-right: 1.5em; +} +.ql-editor ol li { + counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; + counter-increment: list-0; +} +.ql-editor ol li:before { + content: counter(list-0, decimal) '. '; +} +.ql-editor.ql-blank::before { + color: rgba(0,0,0,0.6); + content: attr(data-placeholder); + left: 15px; + pointer-events: none; + position: absolute; + right: 15px; +} +.ql-snow.ql-toolbar:after, +.ql-snow .ql-toolbar:after { + clear: both; + content: ''; + display: table; +} +.ql-snow.ql-toolbar button, +.ql-snow .ql-toolbar button { + background: none; + border: none; + cursor: pointer; + display: inline-block; + float: left; + height: 24px; + padding: 3px 5px; + width: 28px; +} +.ql-snow.ql-toolbar button svg, +.ql-snow .ql-toolbar button svg { + float: left; + height: 100%; +} +.ql-snow.ql-toolbar button:hover .ql-fill, +.ql-snow.ql-toolbar button:focus .ql-fill, +.ql-snow.ql-toolbar button.ql-active .ql-fill, +.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill, +.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill, +.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill, +.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill, +.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill, +.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill, +.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, +.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, +.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill { + fill: #06c; +} +.ql-snow.ql-toolbar button:hover .ql-stroke, +.ql-snow.ql-toolbar button:focus .ql-stroke, +.ql-snow.ql-toolbar button.ql-active .ql-stroke, +.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke, +.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke, +.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke, +.ql-snow.ql-toolbar button:hover .ql-stroke-miter, +.ql-snow.ql-toolbar button:focus .ql-stroke-miter, +.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter, +.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter, +.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, +.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter { + stroke: #06c; +} +.ql-snow { + box-sizing: border-box; +} +.ql-snow * { + box-sizing: border-box; +} +.ql-snow .ql-hidden { + display: none; +} +.ql-snow .ql-stroke { + fill: none; + stroke: #444; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 2; +} +.ql-snow .ql-stroke-miter { + fill: none; + stroke: #444; + stroke-miterlimit: 10; + stroke-width: 2; +} +.ql-snow .ql-fill, +.ql-snow .ql-stroke.ql-fill { + fill: #444; +} +.ql-snow .ql-editor code, +.ql-snow .ql-editor pre { + background-color: #f0f0f0; + border-radius: 3px; +} +.ql-snow .ql-editor pre { + white-space: pre-wrap; + margin-bottom: 5px; + margin-top: 5px; + padding: 5px 10px; +} +.ql-snow .ql-editor code { + font-size: 85%; + padding: 2px 4px; +} +.ql-snow .ql-editor pre.ql-syntax { + background-color: #E4E7EA; + overflow: visible; +} +.ql-container.ql-snow { + border: 1px solid #ccc; +} diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 35430565ce10..d040abade970 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -291,6 +291,7 @@ "@types/lodash.debounce": "^4.0.8", "@types/object-path": "^0.11.3", "@types/proxy-from-env": "^1.0.3", + "@types/quill": "^2.0.11", "@types/speakeasy": "^2.0.9", "@xmldom/xmldom": "^0.8.10", "adm-zip": "0.5.10", @@ -400,6 +401,7 @@ "psl": "^1.8.0", "query-string": "^7.1.3", "queue-fifo": "^0.2.6", + "quill": "^1.3.7", "rc-scrollbars": "^1.1.6", "react": "~17.0.2", "react-aria": "~3.23.1", diff --git a/packages/ui-composer/src/MessageComposer/MessageComposerQuillInput.tsx b/packages/ui-composer/src/MessageComposer/MessageComposerQuillInput.tsx new file mode 100644 index 000000000000..8d35bb20ce28 --- /dev/null +++ b/packages/ui-composer/src/MessageComposer/MessageComposerQuillInput.tsx @@ -0,0 +1,41 @@ +import { css } from '@rocket.chat/css-in-js'; +import { Box, Palette } from '@rocket.chat/fuselage'; +import type { ComponentProps, ReactElement, Ref } from 'react'; +import { forwardRef } from 'react'; + +const messageComposerInputStyle = css` + resize: none; + + &::placeholder { + color: ${Palette.text['font-annotation']}; + } +`; + +type MessageComposerQuillInputProps = ComponentProps; + +const MessageComposerQuillInput = forwardRef(function MessageComposerQuillInput( + props: MessageComposerQuillInputProps, + ref: Ref, +): ReactElement { + console.log(props); + return ( + + + + ); +}); + +export default MessageComposerQuillInput; diff --git a/packages/ui-composer/src/MessageComposer/index.ts b/packages/ui-composer/src/MessageComposer/index.ts index edf466ab2966..c38cd0d79e7a 100644 --- a/packages/ui-composer/src/MessageComposer/index.ts +++ b/packages/ui-composer/src/MessageComposer/index.ts @@ -5,6 +5,7 @@ import MessageComposerButton from './MessageComposerButton'; import MessageComposerHint from './MessageComposerHint'; import MessageComposerIcon from './MessageComposerIcon'; import MessageComposerInput from './MessageComposerInput'; +import MessageComposerQuillInput from './MessageComposerQuillInput'; import MessageComposerSkeleton from './MessageComposerSkeleton'; import MessageComposerToolbar from './MessageComposerToolbar'; import MessageComposerToolbarActions from './MessageComposerToolbarActions'; @@ -22,4 +23,5 @@ export { MessageComposerIcon, MessageComposerHint, MessageComposerButton, + MessageComposerQuillInput, }; diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 6c2e2fb1169a..1b4552904fa5 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -9,6 +9,7 @@ "@rocket.chat/i18n": "workspace:~", "@rocket.chat/rest-typings": "workspace:^", "@types/jest": "~29.5.7", + "@types/quill": "^2.0.11", "@types/react": "~17.0.69", "@types/react-dom": "~17.0.22", "@types/use-sync-external-store": "^0.0.5", @@ -16,6 +17,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "jest": "~29.6.4", "mongodb": "^4.17.2", + "quill": "^1.3.7", "react": "~17.0.2", "ts-jest": "~29.1.1", "typescript": "~5.3.2", diff --git a/packages/ui-contexts/src/hooks/useQuill.ts b/packages/ui-contexts/src/hooks/useQuill.ts new file mode 100644 index 000000000000..18da63c8697a --- /dev/null +++ b/packages/ui-contexts/src/hooks/useQuill.ts @@ -0,0 +1,62 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +/* eslint-disable prefer-rest-params */ +import type Quill from 'quill'; +import type { RefObject } from 'react'; +import { useRef, useState, useEffect } from 'react'; + +type QuillOptions = { + placeholder?: string; + customIcons?: { [key: string]: string }; +}; + +function assign(target: any, _varArgs: any) { + if (target === null || target === undefined) { + throw new TypeError('Cannot convert undefined or null to object'); + } + + const to = Object(target); + + for (let index = 1; index < arguments.length; index++) { + const nextSource = arguments[index]; + + if (nextSource !== null && nextSource !== undefined) { + for (const nextKey in nextSource) { + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; +} + +export const useQuill = (options?: QuillOptions) => { + const quillRef: RefObject = useRef(); + + const [isLoaded, setIsLoaded] = useState(false); + const [obj, setObj] = useState({ + Quill: undefined as any | undefined, + quillRef, + quill: undefined as Quill | undefined, + getDelta: () => {}, + }); + + useEffect(() => { + if (!obj.Quill) { + setObj((prev) => assign(prev, { Quill: require('quill') })); + } + if (obj.Quill && !obj.quill && quillRef && quillRef.current && isLoaded) { + const opts = { + modules: { toolbar: '#toolbar' }, + theme: 'snow', + }; + const quill = new obj.Quill(quillRef.current, opts); + const getDelta = () => quill.getContents(); + + setObj(assign(assign({}, obj), { quill, getDelta })); + } + setIsLoaded(true); + }, [isLoaded, options, obj]); + + return obj; +}; diff --git a/packages/ui-contexts/src/index.ts b/packages/ui-contexts/src/index.ts index 0870eb4417c8..ff685d086294 100644 --- a/packages/ui-contexts/src/index.ts +++ b/packages/ui-contexts/src/index.ts @@ -50,6 +50,7 @@ export { useMediaUrl } from './hooks/useMediaUrl'; export { useMethod } from './hooks/useMethod'; export { useModal } from './hooks/useModal'; export { usePermission } from './hooks/usePermission'; +export { useQuill } from './hooks/useQuill'; export { usePermissionWithScopedRoles } from './hooks/usePermissionWithScopedRoles'; export { useRole } from './hooks/useRole'; export { useRolesDescription } from './hooks/useRolesDescription'; diff --git a/yarn.lock b/yarn.lock index 7c88ca377d17..95f680e32c0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9862,6 +9862,7 @@ __metadata: "@types/proxy-from-env": ^1.0.3 "@types/proxyquire": ^1.3.30 "@types/psl": ^1.1.2 + "@types/quill": ^2.0.11 "@types/react": ~17.0.69 "@types/react-dom": ~17.0.22 "@types/rewire": ^2.5.29 @@ -10032,6 +10033,7 @@ __metadata: psl: ^1.8.0 query-string: ^7.1.3 queue-fifo: ^0.2.6 + quill: ^1.3.7 rc-scrollbars: ^1.1.6 react: ~17.0.2 react-aria: ~3.23.1 @@ -10723,6 +10725,7 @@ __metadata: "@rocket.chat/password-policies": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" "@types/jest": ~29.5.7 + "@types/quill": ^2.0.11 "@types/react": ~17.0.69 "@types/react-dom": ~17.0.22 "@types/use-sync-external-store": ^0.0.5 @@ -10730,6 +10733,7 @@ __metadata: eslint-plugin-react-hooks: ^4.6.0 jest: ~29.6.4 mongodb: ^4.17.2 + quill: ^1.3.7 react: ~17.0.2 ts-jest: ~29.1.1 typescript: ~5.3.2 @@ -14562,6 +14566,16 @@ __metadata: languageName: node linkType: hard +"@types/quill@npm:^2.0.11": + version: 2.0.14 + resolution: "@types/quill@npm:2.0.14" + dependencies: + parchment: ^1.1.2 + quill-delta: ^5.1.0 + checksum: 05167f590f8d2bd7ededa819aa6df4004967b2988c6dbd571b24d595142e37d663d4acc26b62576c81dd3db3587fcb69f54a424ecf56ee3fa51c57cb8c58d0bb + languageName: node + linkType: hard + "@types/range-parser@npm:*": version: 1.2.4 resolution: "@types/range-parser@npm:1.2.4" @@ -18975,7 +18989,7 @@ __metadata: languageName: node linkType: hard -"clone@npm:^2.1.2": +"clone@npm:^2.1.1, clone@npm:^2.1.2": version: 2.1.2 resolution: "clone@npm:2.1.2" checksum: aaf106e9bc025b21333e2f4c12da539b568db4925c0501a1bf4070836c9e848c892fa22c35548ce0d1132b08bbbfa17a00144fe58fccdab6fa900fec4250f67d @@ -20861,6 +20875,20 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^1.0.1": + version: 1.1.2 + resolution: "deep-equal@npm:1.1.2" + dependencies: + is-arguments: ^1.1.1 + is-date-object: ^1.0.5 + is-regex: ^1.1.4 + object-is: ^1.1.5 + object-keys: ^1.1.1 + regexp.prototype.flags: ^1.5.1 + checksum: 2d50f27fff785fb272cdef038ee5365ee5a30ab1aab053976e6a6add44cc60abd99b38179a46a01ac52c5e54ebb220e8f1a3a1954da20678b79c46ef4d97c9db + languageName: node + linkType: hard + "deep-equal@npm:^2.0.5": version: 2.2.2 resolution: "deep-equal@npm:2.2.2" @@ -22777,6 +22805,13 @@ __metadata: languageName: node linkType: hard +"eventemitter3@npm:^2.0.3": + version: 2.0.3 + resolution: "eventemitter3@npm:2.0.3" + checksum: dfbf4a07144afea0712d8e6a7f30ae91beb7c12c36c3d480818488aafa437d9a331327461f82c12dfd60a4fbad502efc97f684089cda02809988b84a23630752 + languageName: node + linkType: hard + "eventemitter3@npm:^3.1.0": version: 3.1.2 resolution: "eventemitter3@npm:3.1.2" @@ -23155,6 +23190,13 @@ __metadata: languageName: node linkType: hard +"fast-diff@npm:1.1.2": + version: 1.1.2 + resolution: "fast-diff@npm:1.1.2" + checksum: 2ef726603e22a89ef27225bfaef24c17e3aec188df24da4629d5f012b23a884e09a0c7299ff37a0aec7aa788755bd554f5801f698de4deeffce83308bd11405d + languageName: node + linkType: hard + "fast-diff@npm:^1.1.2": version: 1.2.0 resolution: "fast-diff@npm:1.2.0" @@ -23162,6 +23204,13 @@ __metadata: languageName: node linkType: hard +"fast-diff@npm:^1.3.0": + version: 1.3.0 + resolution: "fast-diff@npm:1.3.0" + checksum: d22d371b994fdc8cce9ff510d7b8dc4da70ac327bcba20df607dd5b9cae9f908f4d1028f5fe467650f058d1e7270235ae0b8230809a262b4df587a3b3aa216c3 + languageName: node + linkType: hard + "fast-fifo@npm:^1.1.0, fast-fifo@npm:^1.2.0": version: 1.3.2 resolution: "fast-fifo@npm:1.3.2" @@ -32667,6 +32716,13 @@ __metadata: languageName: node linkType: hard +"parchment@npm:^1.1.2, parchment@npm:^1.1.4": + version: 1.1.4 + resolution: "parchment@npm:1.1.4" + checksum: 47997567424d1ad8648046091a06b3a5423ed83f9dfa421d7fd93e0032e79aedd8db5499ff55327e08eebd89d8e927704a646f87d45d4bcfe63016aa5a88947d + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -34868,6 +34924,42 @@ __metadata: languageName: node linkType: hard +"quill-delta@npm:^3.6.2": + version: 3.6.3 + resolution: "quill-delta@npm:3.6.3" + dependencies: + deep-equal: ^1.0.1 + extend: ^3.0.2 + fast-diff: 1.1.2 + checksum: e62ed339838077841db401da3181bdf559c6667d014a671767788380c5be13a6205603bcdd27445260e6f6b2b5519161e1000023e521e3b2ff087270fa67fef6 + languageName: node + linkType: hard + +"quill-delta@npm:^5.1.0": + version: 5.1.0 + resolution: "quill-delta@npm:5.1.0" + dependencies: + fast-diff: ^1.3.0 + lodash.clonedeep: ^4.5.0 + lodash.isequal: ^4.5.0 + checksum: a9c8cfb6a76c95e8ebc2d8e2a5982a192500a8a8b851fb6b06311d701c8da78c07b332fa7d29d6f66933d32a4fd79b619ba5e27800528c5ae318667df22969d4 + languageName: node + linkType: hard + +"quill@npm:^1.3.7": + version: 1.3.7 + resolution: "quill@npm:1.3.7" + dependencies: + clone: ^2.1.1 + deep-equal: ^1.0.1 + eventemitter3: ^2.0.3 + extend: ^3.0.2 + parchment: ^1.1.4 + quill-delta: ^3.6.2 + checksum: db3e265a8410a4554e50a18cae4ebc0b43a996a776bcf03e26abcadbf617f4db329d49a0fa3ada6a70538a369bbbdc8fa7a66086f194b481914bf1adbab16f8f + languageName: node + linkType: hard + "raf-schd@npm:^4.0.2": version: 4.0.3 resolution: "raf-schd@npm:4.0.3"