feat: add slash command menu to article editor (#14035)
This commit is contained in:
@@ -8,13 +8,22 @@ import {
|
|||||||
EditorState,
|
EditorState,
|
||||||
Selection,
|
Selection,
|
||||||
} from '@chatwoot/prosemirror-schema';
|
} from '@chatwoot/prosemirror-schema';
|
||||||
|
import {
|
||||||
|
suggestionsPlugin,
|
||||||
|
triggerCharacters,
|
||||||
|
} from '@chatwoot/prosemirror-schema/src/mentions/plugin';
|
||||||
import imagePastePlugin from '@chatwoot/prosemirror-schema/src/plugins/image';
|
import imagePastePlugin from '@chatwoot/prosemirror-schema/src/plugins/image';
|
||||||
|
import { toggleMark } from 'prosemirror-commands';
|
||||||
|
import { wrapInList } from 'prosemirror-schema-list';
|
||||||
|
import { toggleBlockType } from '@chatwoot/prosemirror-schema/src/menu/common';
|
||||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||||
|
import SlashCommandMenu from './SlashCommandMenu.vue';
|
||||||
|
|
||||||
const MAXIMUM_FILE_UPLOAD_SIZE = 4; // in MB
|
const MAXIMUM_FILE_UPLOAD_SIZE = 4; // in MB
|
||||||
|
const SLASH_MENU_OFFSET = 4;
|
||||||
const createState = (
|
const createState = (
|
||||||
content,
|
content,
|
||||||
placeholder,
|
placeholder,
|
||||||
@@ -40,6 +49,7 @@ let editorView = null;
|
|||||||
let state;
|
let state;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { SlashCommandMenu },
|
||||||
mixins: [keyboardEventListenerMixins],
|
mixins: [keyboardEventListenerMixins],
|
||||||
props: {
|
props: {
|
||||||
modelValue: { type: String, default: '' },
|
modelValue: { type: String, default: '' },
|
||||||
@@ -62,8 +72,15 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
plugins: [imagePastePlugin(this.handleImageUpload)],
|
plugins: [
|
||||||
|
imagePastePlugin(this.handleImageUpload),
|
||||||
|
this.createSlashPlugin(),
|
||||||
|
],
|
||||||
isTextSelected: false, // Tracks text selection and prevents unnecessary re-renders on mouse selection
|
isTextSelected: false, // Tracks text selection and prevents unnecessary re-renders on mouse selection
|
||||||
|
showSlashMenu: false,
|
||||||
|
slashSearchTerm: '',
|
||||||
|
slashRange: null,
|
||||||
|
slashMenuPosition: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -95,6 +112,126 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
createSlashPlugin() {
|
||||||
|
return suggestionsPlugin({
|
||||||
|
matcher: triggerCharacters('/', 0),
|
||||||
|
suggestionClass: '',
|
||||||
|
onEnter: args => {
|
||||||
|
this.showSlashMenu = true;
|
||||||
|
this.slashRange = args.range;
|
||||||
|
this.slashSearchTerm = args.text || '';
|
||||||
|
this.updateSlashMenuPosition(args.range.from);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onChange: args => {
|
||||||
|
this.slashRange = args.range;
|
||||||
|
this.slashSearchTerm = args.text;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onExit: () => {
|
||||||
|
this.slashSearchTerm = '';
|
||||||
|
this.showSlashMenu = false;
|
||||||
|
this.slashMenuPosition = null;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onKeyDown: ({ event }) => {
|
||||||
|
return (
|
||||||
|
event.keyCode === 13 &&
|
||||||
|
this.showSlashMenu &&
|
||||||
|
this.$refs.slashMenu?.hasItems
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateSlashMenuPosition(pos) {
|
||||||
|
if (!editorView) return;
|
||||||
|
const coords = editorView.coordsAtPos(pos);
|
||||||
|
const editorRect = this.$refs.editor.getBoundingClientRect();
|
||||||
|
const isRtl = getComputedStyle(this.$refs.editor).direction === 'rtl';
|
||||||
|
this.slashMenuPosition = {
|
||||||
|
top: coords.bottom - editorRect.top + SLASH_MENU_OFFSET,
|
||||||
|
...(isRtl
|
||||||
|
? { right: editorRect.right - coords.right }
|
||||||
|
: { left: coords.left - editorRect.left }),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
removeSlashTriggerText() {
|
||||||
|
if (!editorView || !this.slashRange) return;
|
||||||
|
const { from, to } = this.slashRange;
|
||||||
|
editorView.dispatch(editorView.state.tr.delete(from, to));
|
||||||
|
state = editorView.state;
|
||||||
|
},
|
||||||
|
executeSlashCommand(actionKey) {
|
||||||
|
if (!editorView) return;
|
||||||
|
|
||||||
|
this.removeSlashTriggerText();
|
||||||
|
|
||||||
|
const { schema } = editorView.state;
|
||||||
|
const commandMap = {
|
||||||
|
strong: () =>
|
||||||
|
toggleMark(schema.marks.strong)(
|
||||||
|
editorView.state,
|
||||||
|
editorView.dispatch
|
||||||
|
),
|
||||||
|
em: () =>
|
||||||
|
toggleMark(schema.marks.em)(editorView.state, editorView.dispatch),
|
||||||
|
strike: () =>
|
||||||
|
toggleMark(schema.marks.strike)(
|
||||||
|
editorView.state,
|
||||||
|
editorView.dispatch
|
||||||
|
),
|
||||||
|
code: () =>
|
||||||
|
toggleMark(schema.marks.code)(editorView.state, editorView.dispatch),
|
||||||
|
h1: () =>
|
||||||
|
toggleBlockType(schema.nodes.heading, { level: 1 })(
|
||||||
|
editorView.state,
|
||||||
|
editorView.dispatch
|
||||||
|
),
|
||||||
|
h2: () =>
|
||||||
|
toggleBlockType(schema.nodes.heading, { level: 2 })(
|
||||||
|
editorView.state,
|
||||||
|
editorView.dispatch
|
||||||
|
),
|
||||||
|
h3: () =>
|
||||||
|
toggleBlockType(schema.nodes.heading, { level: 3 })(
|
||||||
|
editorView.state,
|
||||||
|
editorView.dispatch
|
||||||
|
),
|
||||||
|
bulletList: () =>
|
||||||
|
wrapInList(schema.nodes.bullet_list)(
|
||||||
|
editorView.state,
|
||||||
|
editorView.dispatch
|
||||||
|
),
|
||||||
|
orderedList: () =>
|
||||||
|
wrapInList(schema.nodes.ordered_list)(
|
||||||
|
editorView.state,
|
||||||
|
editorView.dispatch
|
||||||
|
),
|
||||||
|
insertTable: () => {
|
||||||
|
const { table, table_row, table_header, table_cell, paragraph } =
|
||||||
|
schema.nodes;
|
||||||
|
const headerCells = [0, 1, 2].map(() =>
|
||||||
|
table_header.createAndFill(null, paragraph.create())
|
||||||
|
);
|
||||||
|
const dataCells = [0, 1, 2].map(() =>
|
||||||
|
table_cell.createAndFill(null, paragraph.create())
|
||||||
|
);
|
||||||
|
const headerRow = table_row.create(null, headerCells);
|
||||||
|
const dataRow = table_row.create(null, dataCells);
|
||||||
|
const tableNode = table.create(null, [headerRow, dataRow]);
|
||||||
|
const tr = editorView.state.tr.replaceSelectionWith(tableNode);
|
||||||
|
editorView.dispatch(tr.scrollIntoView());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const command = commandMap[actionKey];
|
||||||
|
if (command) {
|
||||||
|
command();
|
||||||
|
state = editorView.state;
|
||||||
|
this.emitOnChange();
|
||||||
|
editorView.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
contentFromEditor() {
|
contentFromEditor() {
|
||||||
if (editorView) {
|
if (editorView) {
|
||||||
return ArticleMarkdownSerializer.serialize(editorView.state.doc);
|
return ArticleMarkdownSerializer.serialize(editorView.state.doc);
|
||||||
@@ -291,7 +428,15 @@ export default {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="editor-root editor--article">
|
<div class="editor-root editor--article relative">
|
||||||
|
<SlashCommandMenu
|
||||||
|
v-if="showSlashMenu"
|
||||||
|
ref="slashMenu"
|
||||||
|
:search-key="slashSearchTerm"
|
||||||
|
:enabled-menu-options="enabledMenuOptions"
|
||||||
|
:position="slashMenuPosition"
|
||||||
|
@select-action="executeSlashCommand"
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
ref="imageUploadInput"
|
ref="imageUploadInput"
|
||||||
type="file"
|
type="file"
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch, nextTick } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useKeyboardNavigableList } from 'dashboard/composables/useKeyboardNavigableList';
|
||||||
|
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
searchKey: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
enabledMenuOptions: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['selectAction']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const EDITOR_ACTIONS = [
|
||||||
|
{
|
||||||
|
value: 'h1',
|
||||||
|
labelKey: 'SLASH_COMMANDS.HEADING_1',
|
||||||
|
icon: 'i-lucide-heading-1',
|
||||||
|
menuKey: 'h1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'h2',
|
||||||
|
labelKey: 'SLASH_COMMANDS.HEADING_2',
|
||||||
|
icon: 'i-lucide-heading-2',
|
||||||
|
menuKey: 'h2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'h3',
|
||||||
|
labelKey: 'SLASH_COMMANDS.HEADING_3',
|
||||||
|
icon: 'i-lucide-heading-3',
|
||||||
|
menuKey: 'h3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'strong',
|
||||||
|
labelKey: 'SLASH_COMMANDS.BOLD',
|
||||||
|
icon: 'i-lucide-bold',
|
||||||
|
menuKey: 'strong',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'em',
|
||||||
|
labelKey: 'SLASH_COMMANDS.ITALIC',
|
||||||
|
icon: 'i-lucide-italic',
|
||||||
|
menuKey: 'em',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'insertTable',
|
||||||
|
labelKey: 'SLASH_COMMANDS.TABLE',
|
||||||
|
icon: 'i-lucide-table',
|
||||||
|
menuKey: 'insertTable',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'strike',
|
||||||
|
labelKey: 'SLASH_COMMANDS.STRIKETHROUGH',
|
||||||
|
icon: 'i-lucide-strikethrough',
|
||||||
|
menuKey: 'strike',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'code',
|
||||||
|
labelKey: 'SLASH_COMMANDS.CODE',
|
||||||
|
icon: 'i-lucide-code',
|
||||||
|
menuKey: 'code',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bulletList',
|
||||||
|
labelKey: 'SLASH_COMMANDS.BULLET_LIST',
|
||||||
|
icon: 'i-lucide-list',
|
||||||
|
menuKey: 'bulletList',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'orderedList',
|
||||||
|
labelKey: 'SLASH_COMMANDS.ORDERED_LIST',
|
||||||
|
icon: 'i-lucide-list-ordered',
|
||||||
|
menuKey: 'orderedList',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const listContainerRef = ref(null);
|
||||||
|
const selectedIndex = ref(0);
|
||||||
|
|
||||||
|
const items = computed(() => {
|
||||||
|
const search = props.searchKey.toLowerCase();
|
||||||
|
return EDITOR_ACTIONS.filter(action => {
|
||||||
|
if (!props.enabledMenuOptions.includes(action.menuKey)) return false;
|
||||||
|
if (!search) return true;
|
||||||
|
return t(action.labelKey).toLowerCase().includes(search);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasItems = computed(() => items.value.length > 0);
|
||||||
|
|
||||||
|
const menuStyle = computed(() => {
|
||||||
|
if (!props.position) return {};
|
||||||
|
const style = { top: `${props.position.top}px` };
|
||||||
|
if (props.position.right != null) {
|
||||||
|
style.right = `${props.position.right}px`;
|
||||||
|
} else {
|
||||||
|
style.left = `${props.position.left}px`;
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
});
|
||||||
|
|
||||||
|
const adjustScroll = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
const container = listContainerRef.value;
|
||||||
|
if (!container) return;
|
||||||
|
const el = container.querySelector(`#slash-item-${selectedIndex.value}`);
|
||||||
|
if (el) {
|
||||||
|
el.scrollIntoView({ block: 'nearest', behavior: 'auto' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = () => {
|
||||||
|
const item = items.value[selectedIndex.value];
|
||||||
|
if (item) emit('selectAction', item.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
useKeyboardNavigableList({
|
||||||
|
items,
|
||||||
|
onSelect,
|
||||||
|
adjustScroll,
|
||||||
|
selectedIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset selection when filtered items change
|
||||||
|
watch(items, () => {
|
||||||
|
selectedIndex.value = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const onHover = index => {
|
||||||
|
selectedIndex.value = index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onItemClick = index => {
|
||||||
|
selectedIndex.value = index;
|
||||||
|
onSelect();
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ hasItems });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- eslint-disable-next-line vue/no-root-v-if -->
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="hasItems"
|
||||||
|
ref="listContainerRef"
|
||||||
|
class="bg-n-alpha-3 backdrop-blur-[100px] outline outline-1 outline-n-container absolute rounded-xl z-50 flex flex-col min-w-[10rem] shadow-lg p-2 overflow-auto max-h-[15rem]"
|
||||||
|
:style="menuStyle"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="(item, index) in items"
|
||||||
|
:id="`slash-item-${index}`"
|
||||||
|
:key="item.value"
|
||||||
|
type="button"
|
||||||
|
class="inline-flex items-center justify-start w-full h-8 min-w-0 gap-2 px-2 py-1.5 border-0 rounded-lg text-n-slate-12 hover:bg-n-alpha-1 dark:hover:bg-n-alpha-2"
|
||||||
|
:class="{
|
||||||
|
'bg-n-alpha-1 dark:bg-n-alpha-2': index === selectedIndex,
|
||||||
|
}"
|
||||||
|
@mouseover="onHover(index)"
|
||||||
|
@click="onItemClick(index)"
|
||||||
|
>
|
||||||
|
<Icon :icon="item.icon" class="flex-shrink-0 size-3.5" />
|
||||||
|
<span class="min-w-0 text-sm truncate">
|
||||||
|
{{ t(item.labelKey) }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -12,11 +12,14 @@ import { useKeyboardEvents } from './useKeyboardEvents';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap the action in a function that calls the action and prevents the default event behavior.
|
* Wrap the action in a function that calls the action and prevents the default event behavior.
|
||||||
|
* Only prevents default when items are available to navigate.
|
||||||
* @param {Function} action - The action to be called.
|
* @param {Function} action - The action to be called.
|
||||||
|
* @param {import('vue').Ref<Array>} items - A ref to the array of selectable items.
|
||||||
* @returns {{action: Function, allowOnFocusedInput: boolean}} An object containing the action and a flag to allow the event on focused input.
|
* @returns {{action: Function, allowOnFocusedInput: boolean}} An object containing the action and a flag to allow the event on focused input.
|
||||||
*/
|
*/
|
||||||
const createAction = action => ({
|
const createAction = (action, items) => ({
|
||||||
action: e => {
|
action: e => {
|
||||||
|
if (!items.value?.length) return;
|
||||||
action();
|
action();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
},
|
},
|
||||||
@@ -38,15 +41,14 @@ const createKeyboardEvents = (
|
|||||||
items
|
items
|
||||||
) => {
|
) => {
|
||||||
const events = {
|
const events = {
|
||||||
ArrowUp: createAction(moveSelectionUp),
|
ArrowUp: createAction(moveSelectionUp, items),
|
||||||
'Control+KeyP': createAction(moveSelectionUp),
|
'Control+KeyP': createAction(moveSelectionUp, items),
|
||||||
ArrowDown: createAction(moveSelectionDown),
|
ArrowDown: createAction(moveSelectionDown, items),
|
||||||
'Control+KeyN': createAction(moveSelectionDown),
|
'Control+KeyN': createAction(moveSelectionDown, items),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Adds an event handler for the Enter key if the onSelect function is provided.
|
|
||||||
if (typeof onSelect === 'function') {
|
if (typeof onSelect === 'function') {
|
||||||
events.Enter = createAction(() => items.value?.length > 0 && onSelect());
|
events.Enter = createAction(onSelect, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
return events;
|
return events;
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ export const FORMATTING = {
|
|||||||
export const ARTICLE_EDITOR_MENU_OPTIONS = [
|
export const ARTICLE_EDITOR_MENU_OPTIONS = [
|
||||||
'strong',
|
'strong',
|
||||||
'em',
|
'em',
|
||||||
|
'strike',
|
||||||
'link',
|
'link',
|
||||||
'undo',
|
'undo',
|
||||||
'redo',
|
'redo',
|
||||||
|
|||||||
@@ -52,5 +52,17 @@
|
|||||||
},
|
},
|
||||||
"CHANNEL_SELECTOR": {
|
"CHANNEL_SELECTOR": {
|
||||||
"COMING_SOON": "Coming Soon!"
|
"COMING_SOON": "Coming Soon!"
|
||||||
|
},
|
||||||
|
"SLASH_COMMANDS": {
|
||||||
|
"HEADING_1": "Heading 1",
|
||||||
|
"HEADING_2": "Heading 2",
|
||||||
|
"HEADING_3": "Heading 3",
|
||||||
|
"BOLD": "Bold",
|
||||||
|
"ITALIC": "Italic",
|
||||||
|
"STRIKETHROUGH": "Strikethrough",
|
||||||
|
"CODE": "Code",
|
||||||
|
"BULLET_LIST": "Bullet List",
|
||||||
|
"ORDERED_LIST": "Ordered List",
|
||||||
|
"TABLE": "Table"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -698,7 +698,7 @@
|
|||||||
"EDIT_ARTICLE": {
|
"EDIT_ARTICLE": {
|
||||||
"MORE_PROPERTIES": "More properties",
|
"MORE_PROPERTIES": "More properties",
|
||||||
"UNCATEGORIZED": "Uncategorized",
|
"UNCATEGORIZED": "Uncategorized",
|
||||||
"EDITOR_PLACEHOLDER": "Write something..."
|
"EDITOR_PLACEHOLDER": "Write your content here. Type '/' for formatting options."
|
||||||
},
|
},
|
||||||
"ARTICLE_PROPERTIES": {
|
"ARTICLE_PROPERTIES": {
|
||||||
"ARTICLE_PROPERTIES": "Article properties",
|
"ARTICLE_PROPERTIES": "Article properties",
|
||||||
|
|||||||
@@ -85,6 +85,8 @@
|
|||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"opus-recorder": "^8.0.5",
|
"opus-recorder": "^8.0.5",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
|
"prosemirror-commands": "^1.7.1",
|
||||||
|
"prosemirror-schema-list": "^1.5.1",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"semver": "7.6.3",
|
"semver": "7.6.3",
|
||||||
"snakecase-keys": "^8.0.1",
|
"snakecase-keys": "^8.0.1",
|
||||||
|
|||||||
33
pnpm-lock.yaml
generated
33
pnpm-lock.yaml
generated
@@ -178,6 +178,12 @@ importers:
|
|||||||
pinia:
|
pinia:
|
||||||
specifier: ^3.0.4
|
specifier: ^3.0.4
|
||||||
version: 3.0.4(typescript@5.6.2)(vue@3.5.12(typescript@5.6.2))
|
version: 3.0.4(typescript@5.6.2)(vue@3.5.12(typescript@5.6.2))
|
||||||
|
prosemirror-commands:
|
||||||
|
specifier: ^1.7.1
|
||||||
|
version: 1.7.1
|
||||||
|
prosemirror-schema-list:
|
||||||
|
specifier: ^1.5.1
|
||||||
|
version: 1.5.1
|
||||||
qrcode:
|
qrcode:
|
||||||
specifier: ^1.5.4
|
specifier: ^1.5.4
|
||||||
version: 1.5.4
|
version: 1.5.4
|
||||||
@@ -3814,8 +3820,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||||
engines: {node: '>= 0.6.0'}
|
engines: {node: '>= 0.6.0'}
|
||||||
|
|
||||||
prosemirror-commands@1.6.0:
|
prosemirror-commands@1.7.1:
|
||||||
resolution: {integrity: sha512-xn1U/g36OqXn2tn5nGmvnnimAj/g1pUx2ypJJIe8WkVX83WyJVC5LTARaxZa2AtQRwntu9Jc5zXs9gL9svp/mg==}
|
resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
|
||||||
|
|
||||||
prosemirror-dropcursor@1.8.1:
|
prosemirror-dropcursor@1.8.1:
|
||||||
resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==}
|
resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==}
|
||||||
@@ -3841,8 +3847,8 @@ packages:
|
|||||||
prosemirror-model@1.22.3:
|
prosemirror-model@1.22.3:
|
||||||
resolution: {integrity: sha512-V4XCysitErI+i0rKFILGt/xClnFJaohe/wrrlT2NSZ+zk8ggQfDH4x2wNK7Gm0Hp4CIoWizvXFP7L9KMaCuI0Q==}
|
resolution: {integrity: sha512-V4XCysitErI+i0rKFILGt/xClnFJaohe/wrrlT2NSZ+zk8ggQfDH4x2wNK7Gm0Hp4CIoWizvXFP7L9KMaCuI0Q==}
|
||||||
|
|
||||||
prosemirror-schema-list@1.4.1:
|
prosemirror-schema-list@1.5.1:
|
||||||
resolution: {integrity: sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==}
|
resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
|
||||||
|
|
||||||
prosemirror-state@1.4.3:
|
prosemirror-state@1.4.3:
|
||||||
resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
|
resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
|
||||||
@@ -3853,6 +3859,9 @@ packages:
|
|||||||
prosemirror-transform@1.10.0:
|
prosemirror-transform@1.10.0:
|
||||||
resolution: {integrity: sha512-9UOgFSgN6Gj2ekQH5CTDJ8Rp/fnKR2IkYfGdzzp5zQMFsS4zDllLVx/+jGcX86YlACpG7UR5fwAXiWzxqWtBTg==}
|
resolution: {integrity: sha512-9UOgFSgN6Gj2ekQH5CTDJ8Rp/fnKR2IkYfGdzzp5zQMFsS4zDllLVx/+jGcX86YlACpG7UR5fwAXiWzxqWtBTg==}
|
||||||
|
|
||||||
|
prosemirror-transform@1.12.0:
|
||||||
|
resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==}
|
||||||
|
|
||||||
prosemirror-utils@1.2.2:
|
prosemirror-utils@1.2.2:
|
||||||
resolution: {integrity: sha512-7a2MPf99oCW8/587rQYI1/snX71Ban40+apr1hLkY8TmU9YXd7JeR6QsmktcTisJURO3WRjxIia4lTMsYgZVOw==}
|
resolution: {integrity: sha512-7a2MPf99oCW8/587rQYI1/snX71Ban40+apr1hLkY8TmU9YXd7JeR6QsmktcTisJURO3WRjxIia4lTMsYgZVOw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -4970,7 +4979,7 @@ snapshots:
|
|||||||
'@chatwoot/prosemirror-schema@1.3.10':
|
'@chatwoot/prosemirror-schema@1.3.10':
|
||||||
dependencies:
|
dependencies:
|
||||||
markdown-it-sup: 2.0.0
|
markdown-it-sup: 2.0.0
|
||||||
prosemirror-commands: 1.6.0
|
prosemirror-commands: 1.7.1
|
||||||
prosemirror-dropcursor: 1.8.1
|
prosemirror-dropcursor: 1.8.1
|
||||||
prosemirror-gapcursor: 1.3.2
|
prosemirror-gapcursor: 1.3.2
|
||||||
prosemirror-history: 1.4.1
|
prosemirror-history: 1.4.1
|
||||||
@@ -4979,7 +4988,7 @@ snapshots:
|
|||||||
prosemirror-markdown: 1.13.0
|
prosemirror-markdown: 1.13.0
|
||||||
prosemirror-menu: 1.2.4
|
prosemirror-menu: 1.2.4
|
||||||
prosemirror-model: 1.22.3
|
prosemirror-model: 1.22.3
|
||||||
prosemirror-schema-list: 1.4.1
|
prosemirror-schema-list: 1.5.1
|
||||||
prosemirror-state: 1.4.3
|
prosemirror-state: 1.4.3
|
||||||
prosemirror-tables: 1.5.0
|
prosemirror-tables: 1.5.0
|
||||||
prosemirror-utils: 1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)
|
prosemirror-utils: 1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)
|
||||||
@@ -8705,11 +8714,11 @@ snapshots:
|
|||||||
|
|
||||||
process@0.11.10: {}
|
process@0.11.10: {}
|
||||||
|
|
||||||
prosemirror-commands@1.6.0:
|
prosemirror-commands@1.7.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-model: 1.22.3
|
prosemirror-model: 1.22.3
|
||||||
prosemirror-state: 1.4.3
|
prosemirror-state: 1.4.3
|
||||||
prosemirror-transform: 1.10.0
|
prosemirror-transform: 1.12.0
|
||||||
|
|
||||||
prosemirror-dropcursor@1.8.1:
|
prosemirror-dropcursor@1.8.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8749,7 +8758,7 @@ snapshots:
|
|||||||
prosemirror-menu@1.2.4:
|
prosemirror-menu@1.2.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
crelt: 1.0.5
|
crelt: 1.0.5
|
||||||
prosemirror-commands: 1.6.0
|
prosemirror-commands: 1.7.1
|
||||||
prosemirror-history: 1.4.1
|
prosemirror-history: 1.4.1
|
||||||
prosemirror-state: 1.4.3
|
prosemirror-state: 1.4.3
|
||||||
|
|
||||||
@@ -8757,7 +8766,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
orderedmap: 2.1.0
|
orderedmap: 2.1.0
|
||||||
|
|
||||||
prosemirror-schema-list@1.4.1:
|
prosemirror-schema-list@1.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-model: 1.22.3
|
prosemirror-model: 1.22.3
|
||||||
prosemirror-state: 1.4.3
|
prosemirror-state: 1.4.3
|
||||||
@@ -8781,6 +8790,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-model: 1.22.3
|
prosemirror-model: 1.22.3
|
||||||
|
|
||||||
|
prosemirror-transform@1.12.0:
|
||||||
|
dependencies:
|
||||||
|
prosemirror-model: 1.22.3
|
||||||
|
|
||||||
prosemirror-utils@1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3):
|
prosemirror-utils@1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
prosemirror-model: 1.22.3
|
prosemirror-model: 1.22.3
|
||||||
|
|||||||
Reference in New Issue
Block a user