chore: Replace plain editor with advanced editor (#13071)

# Pull Request Template

## Description

This PR reverts the plain text editor back to the **advanced editor**,
which was previously removed in
[https://github.com/chatwoot/chatwoot/pull/13058](https://github.com/chatwoot/chatwoot/pull/13058).
All channels now use the **ProseMirror editor**, with formatting applied
based on each channel’s configuration.

This PR also fixes issues where **new lines were not properly preserved
during Markdown serialization**, for both:

* `Enter or CMD/Ctrl+enter` (new paragraph)
* `Shift+Enter` (`hard_break`)

Additionally, it resolves related **[Sentry
issue](https://chatwoot-p3.sentry.io/issues/?environment=production&project=4507182691975168&query=is%3Aunresolved%20markdown&referrer=issue-list&statsPeriod=7d)**.

With these changes:

* Line breaks and spacing are now preserved correctly when saving canned
responses.
* When editing a canned response, the content retains the exact spacing
and formatting as saved in editor.
* Canned responses are now correctly converted to plain text where
required and displayed consistently in the canned response list.

### https://github.com/chatwoot/prosemirror-schema/pull/38

---

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?



## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Sivin Varghese 2026-01-08 15:17:54 +05:30 committed by GitHub
parent dcc72ee63c
commit 92422f979d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 73 additions and 218 deletions

View File

@ -55,6 +55,7 @@ import {
getSelectionCoords, getSelectionCoords,
calculateMenuPosition, calculateMenuPosition,
getEffectiveChannelType, getEffectiveChannelType,
stripUnsupportedFormatting,
} from 'dashboard/helper/editorHelper'; } from 'dashboard/helper/editorHelper';
import { import {
hasPressedEnterAndNotCmdOrShift, hasPressedEnterAndNotCmdOrShift,
@ -131,8 +132,10 @@ const editorMenuOptions = computed(() => {
const createState = (content, placeholder, plugins = [], methods = {}) => { const createState = (content, placeholder, plugins = [], methods = {}) => {
const schema = editorSchema.value; const schema = editorSchema.value;
// Strip unsupported formatting before parsing to prevent "Token type not supported" errors
const sanitizedContent = stripUnsupportedFormatting(content, schema);
return EditorState.create({ return EditorState.create({
doc: new MessageMarkdownTransformer(schema).parse(content), doc: new MessageMarkdownTransformer(schema).parse(sanitizedContent),
plugins: buildEditor({ plugins: buildEditor({
schema, schema,
placeholder, placeholder,

View File

@ -7,9 +7,7 @@ import { useTrack } from 'dashboard/composables';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins'; import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import { FEATURE_FLAGS } from 'dashboard/featureFlags'; import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import CannedResponse from './CannedResponse.vue';
import ReplyToMessage from './ReplyToMessage.vue'; import ReplyToMessage from './ReplyToMessage.vue';
import ResizableTextArea from 'shared/components/ResizableTextArea.vue';
import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview.vue'; import AttachmentPreview from 'dashboard/components/widgets/AttachmentsPreview.vue';
import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel.vue'; import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel.vue';
import ReplyEmailHead from './ReplyEmailHead.vue'; import ReplyEmailHead from './ReplyEmailHead.vue';
@ -46,7 +44,6 @@ import {
appendSignature, appendSignature,
removeSignature, removeSignature,
getEffectiveChannelType, getEffectiveChannelType,
extractTextFromMarkdown,
} from 'dashboard/helper/editorHelper'; } from 'dashboard/helper/editorHelper';
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage'; import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
@ -72,8 +69,6 @@ export default {
WhatsappTemplates, WhatsappTemplates,
WootMessageEditor, WootMessageEditor,
QuotedEmailPreview, QuotedEmailPreview,
ResizableTextArea,
CannedResponse,
}, },
mixins: [inboxMixin, fileUploadMixin, keyboardEventListenerMixins], mixins: [inboxMixin, fileUploadMixin, keyboardEventListenerMixins],
props: { props: {
@ -114,8 +109,6 @@ export default {
recordingAudioState: '', recordingAudioState: '',
recordingAudioDurationText: '', recordingAudioDurationText: '',
replyType: REPLY_EDITOR_MODES.REPLY, replyType: REPLY_EDITOR_MODES.REPLY,
mentionSearchKey: '',
hasSlashCommand: false,
bccEmails: '', bccEmails: '',
ccEmails: '', ccEmails: '',
toEmails: '', toEmails: '',
@ -148,9 +141,6 @@ export default {
if (!senderId) return {}; if (!senderId) return {};
return this.$store.getters['contacts/getContact'](senderId); return this.$store.getters['contacts/getContact'](senderId);
}, },
isRichEditorEnabled() {
return this.isAWebWidgetInbox || this.isAnEmailChannel || this.isAPIInbox;
},
shouldShowReplyToMessage() { shouldShowReplyToMessage() {
return ( return (
this.inReplyTo?.id && this.inReplyTo?.id &&
@ -409,19 +399,6 @@ export default {
!!this.quotedEmailText !!this.quotedEmailText
); );
}, },
showRichContentEditor() {
if (this.isOnPrivateNote || this.isRichEditorEnabled) {
return true;
}
return false;
},
// ensure that the signature is plain text depending on `showRichContentEditor`
signatureToApply() {
return this.showRichContentEditor
? this.messageSignature
: extractTextFromMarkdown(this.messageSignature);
},
}, },
watch: { watch: {
currentChat(conversation, oldConversation) { currentChat(conversation, oldConversation) {
@ -464,25 +441,7 @@ export default {
this.resetRecorderAndClearAttachments(); this.resetRecorderAndClearAttachments();
} }
}, },
message(updatedMessage) { message() {
// Check if the message starts with a slash.
const bodyWithoutSignature = removeSignature(
updatedMessage,
this.signatureToApply
);
const startsWithSlash = bodyWithoutSignature.startsWith('/');
// Determine if the user is potentially typing a slash command.
// This is true if the message starts with a slash and the rich content editor is not active.
this.hasSlashCommand = startsWithSlash && !this.showRichContentEditor;
this.showMentions = this.hasSlashCommand;
// If a slash command is active, extract the command text after the slash.
// If not, reset the mentionSearchKey.
this.mentionSearchKey = this.hasSlashCommand
? bodyWithoutSignature.substring(1)
: '';
// Autosave the current message draft. // Autosave the current message draft.
this.doAutoSaveDraft(); this.doAutoSaveDraft();
}, },
@ -532,7 +491,6 @@ export default {
methods: { methods: {
handleInsert(article) { handleInsert(article) {
const { url, title } = article; const { url, title } = article;
if (this.isRichEditorEnabled) {
// Removing empty lines from the title // Removing empty lines from the title
const lines = title.split('\n'); const lines = title.split('\n');
const nonEmptyLines = lines.filter(line => line.trim() !== ''); const nonEmptyLines = lines.filter(line => line.trim() !== '');
@ -541,11 +499,6 @@ export default {
BUS_EVENTS.INSERT_INTO_RICH_EDITOR, BUS_EVENTS.INSERT_INTO_RICH_EDITOR,
`[${filteredMarkdown}](${url})` `[${filteredMarkdown}](${url})`
); );
} else {
this.addIntoEditor(
`${this.$t('CONVERSATION.REPLYBOX.INSERT_READ_MORE')} ${url}`
);
}
useTrack(CONVERSATION_EVENTS.INSERT_ARTICLE_LINK); useTrack(CONVERSATION_EVENTS.INSERT_ARTICLE_LINK);
}, },
@ -614,26 +567,14 @@ export default {
if (this.isPrivate) { if (this.isPrivate) {
return message; return message;
} }
if (this.showRichContentEditor) {
const effectiveChannelType = getEffectiveChannelType( const effectiveChannelType = getEffectiveChannelType(
this.channelType, this.channelType,
this.inbox?.medium || '' this.inbox?.medium || ''
); );
return this.sendWithSignature return this.sendWithSignature
? appendSignature( ? appendSignature(message, this.messageSignature, effectiveChannelType)
message, : removeSignature(message, this.messageSignature, effectiveChannelType);
this.messageSignature,
effectiveChannelType
)
: removeSignature(
message,
this.messageSignature,
effectiveChannelType
);
}
return this.sendWithSignature
? appendSignature(message, this.signatureToApply)
: removeSignature(message, this.signatureToApply);
}, },
removeFromDraft() { removeFromDraft() {
if (this.conversationIdByRoute) { if (this.conversationIdByRoute) {
@ -649,7 +590,6 @@ export default {
Escape: { Escape: {
action: () => { action: () => {
this.hideEmojiPicker(); this.hideEmojiPicker();
this.hideMentions();
}, },
allowOnFocusedInput: true, allowOnFocusedInput: true,
}, },
@ -694,11 +634,6 @@ export default {
// Don't handle paste if compose new conversation modal is open // Don't handle paste if compose new conversation modal is open
if (this.newConversationModalActive) return; if (this.newConversationModalActive) return;
const data = e.clipboardData.files;
if (!this.showRichContentEditor && data.length !== 0) {
this.$refs.messageInput?.$el?.blur();
}
// Filter valid files (non-zero size) // Filter valid files (non-zero size)
Array.from(e.clipboardData.files) Array.from(e.clipboardData.files)
.filter(file => file.size > 0) .filter(file => file.size > 0)
@ -832,7 +767,6 @@ export default {
// if signature is enabled, append it to the message // if signature is enabled, append it to the message
// appendSignature ensures that the signature is not duplicated // appendSignature ensures that the signature is not duplicated
// so we don't need to check if the signature is already present // so we don't need to check if the signature is already present
if (this.showRichContentEditor) {
const effectiveChannelType = getEffectiveChannelType( const effectiveChannelType = getEffectiveChannelType(
this.channelType, this.channelType,
this.inbox?.medium || '' this.inbox?.medium || ''
@ -842,9 +776,6 @@ export default {
this.messageSignature, this.messageSignature,
effectiveChannelType effectiveChannelType
); );
} else {
message = appendSignature(message, this.signatureToApply);
}
} }
const updatedMessage = replaceVariablesInMessage({ const updatedMessage = replaceVariablesInMessage({
@ -868,40 +799,21 @@ export default {
}); });
if (canReply || this.isAWhatsAppChannel || this.isAPIInbox) if (canReply || this.isAWhatsAppChannel || this.isAPIInbox)
this.replyType = mode; this.replyType = mode;
if (this.showRichContentEditor) {
if (this.isRecordingAudio) { if (this.isRecordingAudio) {
this.toggleAudioRecorder(); this.toggleAudioRecorder();
} }
return;
}
this.$nextTick(() => this.$refs.messageInput.focus());
}, },
clearEditorSelection() { clearEditorSelection() {
this.updateEditorSelectionWith = ''; this.updateEditorSelectionWith = '';
}, },
insertIntoTextEditor(text, selectionStart, selectionEnd) {
const { message } = this;
const newMessage =
message.slice(0, selectionStart) +
text +
message.slice(selectionEnd, message.length);
this.message = newMessage;
},
addIntoEditor(content) { addIntoEditor(content) {
if (this.showRichContentEditor) {
this.updateEditorSelectionWith = content; this.updateEditorSelectionWith = content;
this.onFocus(); this.onFocus();
}
if (!this.showRichContentEditor) {
const { selectionStart, selectionEnd } = this.$refs.messageInput.$el;
this.insertIntoTextEditor(content, selectionStart, selectionEnd);
}
}, },
clearMessage() { clearMessage() {
this.message = ''; this.message = '';
if (this.sendWithSignature && !this.isPrivate) { if (this.sendWithSignature && !this.isPrivate) {
// if signature is enabled, append it to the message // if signature is enabled, append it to the message
if (this.showRichContentEditor) {
const effectiveChannelType = getEffectiveChannelType( const effectiveChannelType = getEffectiveChannelType(
this.channelType, this.channelType,
this.inbox?.medium || '' this.inbox?.medium || ''
@ -911,9 +823,6 @@ export default {
this.messageSignature, this.messageSignature,
effectiveChannelType effectiveChannelType
); );
} else {
this.message = appendSignature(this.message, this.signatureToApply);
}
} }
this.attachedFiles = []; this.attachedFiles = [];
this.isRecordingAudio = false; this.isRecordingAudio = false;
@ -948,9 +857,6 @@ export default {
this.toggleEmojiPicker(); this.toggleEmojiPicker();
} }
}, },
hideMentions() {
this.showMentions = false;
},
onTypingOn() { onTypingOn() {
this.toggleTyping('on'); this.toggleTyping('on');
}, },
@ -1197,13 +1103,6 @@ export default {
:message="inReplyTo" :message="inReplyTo"
@dismiss="resetReplyToMessage" @dismiss="resetReplyToMessage"
/> />
<CannedResponse
v-if="showMentions && hasSlashCommand"
v-on-clickaway="hideMentions"
class="normal-editor__canned-box"
:search-key="mentionSearchKey"
@replace="replaceText"
/>
<EmojiInput <EmojiInput
v-if="showEmojiPicker" v-if="showEmojiPicker"
v-on-clickaway="hideEmojiPicker" v-on-clickaway="hideEmojiPicker"
@ -1227,23 +1126,7 @@ export default {
@play="recordingAudioState = 'playing'" @play="recordingAudioState = 'playing'"
@pause="recordingAudioState = 'paused'" @pause="recordingAudioState = 'paused'"
/> />
<ResizableTextArea
v-else-if="!showRichContentEditor"
ref="messageInput"
v-model="message"
class="rounded-none input"
:placeholder="messagePlaceHolder"
:min-height="4"
:signature="signatureToApply"
allow-signature
:send-with-signature="sendWithSignature"
@typing-off="onTypingOff"
@typing-on="onTypingOn"
@focus="onFocus"
@blur="onBlur"
/>
<WootMessageEditor <WootMessageEditor
v-else
v-model="message" v-model="message"
:editor-id="editorStateId" :editor-id="editorStateId"
class="input popover-prosemirror-menu" class="input popover-prosemirror-menu"
@ -1369,10 +1252,6 @@ export default {
.reply-box__top { .reply-box__top {
@apply relative py-0 px-4 -mt-px; @apply relative py-0 px-4 -mt-px;
textarea {
@apply shadow-none outline-none border-transparent bg-transparent m-0 max-h-60 min-h-[3rem] pt-4 pb-0 px-0 resize-none;
}
} }
.emoji-dialog { .emoji-dialog {
@ -1392,9 +1271,4 @@ export default {
@apply ltr:left-1 rtl:right-1 -bottom-2; @apply ltr:left-1 rtl:right-1 -bottom-2;
} }
} }
.normal-editor__canned-box {
width: calc(100% - 2 * 1rem);
left: 1rem;
}
</style> </style>

View File

@ -1,6 +1,7 @@
<script setup> <script setup>
import { ref, watch, computed, nextTick } from 'vue'; import { ref, watch, computed, nextTick } from 'vue';
import { useKeyboardNavigableList } from 'dashboard/composables/useKeyboardNavigableList'; import { useKeyboardNavigableList } from 'dashboard/composables/useKeyboardNavigableList';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
const props = defineProps({ const props = defineProps({
items: { items: {
@ -15,6 +16,8 @@ const props = defineProps({
const emit = defineEmits(['mentionSelect']); const emit = defineEmits(['mentionSelect']);
const { getPlainText } = useMessageFormatter();
const mentionsListContainerRef = ref(null); const mentionsListContainerRef = ref(null);
const selectedIndex = ref(0); const selectedIndex = ref(0);
@ -94,7 +97,7 @@ const variableKey = (item = {}) => {
'text-n-slate-12': index === selectedIndex, 'text-n-slate-12': index === selectedIndex,
}" }"
> >
{{ item.description }} {{ getPlainText(item.description) }}
</p> </p>
<p <p
class="max-w-full min-w-0 mb-0 overflow-hidden text-xs text-n-slate-11 group-hover:text-n-slate-12 text-ellipsis whitespace-nowrap" class="max-w-full min-w-0 mb-0 overflow-hidden text-xs text-n-slate-11 group-hover:text-n-slate-12 text-ellipsis whitespace-nowrap"

View File

@ -34,6 +34,7 @@ export function extractTextFromMarkdown(markdown) {
/** /**
* Strip unsupported markdown formatting based on channel capabilities. * Strip unsupported markdown formatting based on channel capabilities.
* Uses MARKDOWN_PATTERNS from editor constants.
* *
* @param {string} markdown - markdown text to process * @param {string} markdown - markdown text to process
* @param {string} channelType - The channel type to check supported formatting * @param {string} channelType - The channel type to check supported formatting
@ -43,35 +44,16 @@ export function stripUnsupportedSignatureMarkdown(markdown, channelType) {
if (!markdown) return ''; if (!markdown) return '';
const { marks = [], nodes = [] } = FORMATTING[channelType] || {}; const { marks = [], nodes = [] } = FORMATTING[channelType] || {};
const has = (arr, key) => arr.includes(key); const supported = [...marks, ...nodes];
// Define stripping rules: [condition, pattern, replacement] // Apply patterns from MARKDOWN_PATTERNS for unsupported types
const rules = [ const result = MARKDOWN_PATTERNS.reduce((text, { type, patterns }) => {
[!has(nodes, 'image'), /!\[.*?\]\(.*?\)/g, ''], if (supported.includes(type)) return text;
[!has(marks, 'link'), /\[([^\]]+)\]\([^)]+\)/g, '$1'], return patterns.reduce(
[!has(nodes, 'codeBlock'), /```[\s\S]*?```/g, ''], (t, { pattern, replacement }) => t.replace(pattern, replacement),
[!has(marks, 'code'), /`([^`]+)`/g, '$1'], text
[!has(marks, 'strong'), /\*\*([^*]+)\*\*/g, '$1'],
[!has(marks, 'strong'), /__([^_]+)__/g, '$1'],
[!has(marks, 'em'), /\*([^*]+)\*/g, '$1'],
// Match _text_ only at word boundaries (whitespace/string start/end)
// Preserves underscores in URLs (e.g., https://example.com/path_name) and variable names
[
!has(marks, 'em'),
/(?<=^|[\s])_([^_\s][^_]*[^_\s]|[^_\s])_(?=$|[\s])/g,
'$1',
],
[!has(marks, 'strike'), /~~([^~]+)~~/g, '$1'],
[!has(nodes, 'blockquote'), /^>\s?/gm, ''],
[!has(nodes, 'bulletList'), /^[-*+]\s+/gm, ''],
[!has(nodes, 'orderedList'), /^\d+\.\s+/gm, ''],
];
const result = rules.reduce(
(text, [shouldStrip, pattern, replacement]) =>
shouldStrip ? text.replace(pattern, replacement) : text,
markdown
); );
}, markdown);
return result return result
.split('\n') .split('\n')
@ -186,27 +168,22 @@ export function appendSignature(body, signature, channelType) {
/** /**
* Removes the signature from the body, along with the signature delimiter. * Removes the signature from the body, along with the signature delimiter.
* Tries to find both the original signature and the stripped version. * Tries multiple signature variants: original, channel-stripped, and fully stripped.
* *
* @param {string} body - The body to remove the signature from. * @param {string} body - The body to remove the signature from.
* @param {string} signature - The signature to remove. * @param {string} signature - The signature to remove.
* @param {string} channelType - Optional. The effective channel type for channel-specific stripping. * @param {string} channelType - Optional. The effective channel type for channel-specific stripping.
* For Twilio channels, pass the result of getEffectiveChannelType().
* @returns {string} - The body with the signature removed. * @returns {string} - The body with the signature removed.
*/ */
export function removeSignature(body, signature, channelType) { export function removeSignature(body, signature, channelType) {
// Build list of signatures to try: original, channel-stripped, and fully stripped // Build unique list of signature variants to try
const cleanedSignature = cleanSignature(signature);
const channelStripped = channelType const channelStripped = channelType
? cleanSignature(stripUnsupportedSignatureMarkdown(signature, channelType)) ? cleanSignature(stripUnsupportedSignatureMarkdown(signature, channelType))
: null; : null;
const fullyStripped = cleanSignature(extractTextFromMarkdown(signature));
// Try signatures in order: original → channel-specific → fully stripped
const signaturesToTry = [ const signaturesToTry = [
cleanedSignature, cleanSignature(signature),
channelStripped, channelStripped,
fullyStripped, cleanSignature(extractTextFromMarkdown(signature)),
].filter((sig, i, arr) => sig && arr.indexOf(sig) === i); // Remove nulls and duplicates ].filter((sig, i, arr) => sig && arr.indexOf(sig) === i); // Remove nulls and duplicates
// Find the first matching signature // Find the first matching signature
@ -225,17 +202,12 @@ export function removeSignature(body, signature, channelType) {
newBody = newBody.substring(0, signatureIndex).trimEnd(); newBody = newBody.substring(0, signatureIndex).trimEnd();
} }
// let's find the delimiter and remove it // Remove delimiter if it's at the end
const delimiterIndex = newBody.lastIndexOf(SIGNATURE_DELIMITER); if (newBody.endsWith(SIGNATURE_DELIMITER)) {
if (
delimiterIndex !== -1 &&
delimiterIndex === newBody.length - SIGNATURE_DELIMITER.length // this will ensure the delimiter is at the end
) {
// if the delimiter is at the end, remove it // if the delimiter is at the end, remove it
newBody = newBody.substring(0, delimiterIndex); newBody = newBody.slice(0, -SIGNATURE_DELIMITER.length);
} }
// return the value
return newBody; return newBody;
} }

View File

@ -6,6 +6,7 @@ import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
import { computed, onMounted, ref, defineOptions } from 'vue'; import { computed, onMounted, ref, defineOptions } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useStoreGetters, useStore } from 'dashboard/composables/store'; import { useStoreGetters, useStore } from 'dashboard/composables/store';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import Button from 'dashboard/components-next/button/Button.vue'; import Button from 'dashboard/components-next/button/Button.vue';
@ -17,6 +18,8 @@ const getters = useStoreGetters();
const store = useStore(); const store = useStore();
const { t } = useI18n(); const { t } = useI18n();
const { getPlainText } = useMessageFormatter();
const showAddPopup = ref(false); const showAddPopup = ref(false);
const loading = ref({}); const loading = ref({});
const showEditPopup = ref(false); const showEditPopup = ref(false);
@ -182,7 +185,7 @@ const tableHeaders = computed(() => {
{{ cannedItem.short_code }} {{ cannedItem.short_code }}
</td> </td>
<td class="py-4 ltr:pr-4 rtl:pl-4 md:break-all whitespace-normal"> <td class="py-4 ltr:pr-4 rtl:pl-4 md:break-all whitespace-normal">
{{ cannedItem.content }} {{ getPlainText(cannedItem.content) }}
</td> </td>
<td class="py-4 flex justify-end gap-1"> <td class="py-4 flex justify-end gap-1">
<Button <Button

View File

@ -33,7 +33,7 @@
"dependencies": { "dependencies": {
"@breezystack/lamejs": "^1.2.7", "@breezystack/lamejs": "^1.2.7",
"@chatwoot/ninja-keys": "1.2.3", "@chatwoot/ninja-keys": "1.2.3",
"@chatwoot/prosemirror-schema": "1.2.6", "@chatwoot/prosemirror-schema": "1.3.4",
"@chatwoot/utils": "^0.0.51", "@chatwoot/utils": "^0.0.51",
"@formkit/core": "^1.6.7", "@formkit/core": "^1.6.7",
"@formkit/vue": "^1.6.7", "@formkit/vue": "^1.6.7",

10
pnpm-lock.yaml generated
View File

@ -20,8 +20,8 @@ importers:
specifier: 1.2.3 specifier: 1.2.3
version: 1.2.3 version: 1.2.3
'@chatwoot/prosemirror-schema': '@chatwoot/prosemirror-schema':
specifier: 1.2.6 specifier: 1.3.4
version: 1.2.6 version: 1.3.4
'@chatwoot/utils': '@chatwoot/utils':
specifier: ^0.0.51 specifier: ^0.0.51
version: 0.0.51 version: 0.0.51
@ -430,8 +430,8 @@ packages:
'@chatwoot/ninja-keys@1.2.3': '@chatwoot/ninja-keys@1.2.3':
resolution: {integrity: sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==} resolution: {integrity: sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==}
'@chatwoot/prosemirror-schema@1.2.6': '@chatwoot/prosemirror-schema@1.3.4':
resolution: {integrity: sha512-ej60kU3m/tP0VoGkOJhj0X+Mxt7fEX5DSEE4IibCZnTM4kMewkMxSYyPu0AXqaA4nKX1hTMrwcbv1t7gVQttWQ==} resolution: {integrity: sha512-XY1UyG9topVy7fNCt45YgiU/QzlJJRhrcrSWcvAIIcDYKomnEcGOyeAfM3WeFD6GgOuLzRUyIHrW+WQGpHaZvA==}
'@chatwoot/utils@0.0.51': '@chatwoot/utils@0.0.51':
resolution: {integrity: sha512-WlEmWfOTzR7YZRUWzn5Wpm15/BRudpwqoNckph8TohyDbiim1CP4UZGa+qjajxTbNGLLhtKlm0Xl+X16+5Wceg==} resolution: {integrity: sha512-WlEmWfOTzR7YZRUWzn5Wpm15/BRudpwqoNckph8TohyDbiim1CP4UZGa+qjajxTbNGLLhtKlm0Xl+X16+5Wceg==}
@ -4909,7 +4909,7 @@ snapshots:
hotkeys-js: 3.8.7 hotkeys-js: 3.8.7
lit: 2.2.6 lit: 2.2.6
'@chatwoot/prosemirror-schema@1.2.6': '@chatwoot/prosemirror-schema@1.3.4':
dependencies: dependencies:
markdown-it-sup: 2.0.0 markdown-it-sup: 2.0.0
prosemirror-commands: 1.6.0 prosemirror-commands: 1.6.0