Compare commits

...

1 commit

Author SHA1 Message Date
Nithin David
bacab4d6a4 Makes editor work 2022-12-21 20:00:29 +05:30
10 changed files with 1386 additions and 3 deletions

View file

@ -0,0 +1,202 @@
<template>
<div>
<div class="editor-root">
<div ref="editor" />
</div>
</div>
</template>
<script>
import { EditorView } from 'prosemirror-view';
import { schema, defaultMarkdownSerializer } from 'prosemirror-markdown';
import { EditorState, Selection } from 'prosemirror-state';
import { defaultMarkdownParser } from 'prosemirror-markdown';
import { wootWriterSetup } from '@chatwoot/prosemirror-schema';
import { buildMenuItems } from './src/menu';
import marksFormattingRules from './src/marksInputRules';
import blockFormattingRules from './src/blockInputRules';
import '@chatwoot/prosemirror-schema/src/woot-editor.css';
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
const createState = (content, placeholder, plugins = []) => {
return EditorState.create({
doc: defaultMarkdownParser.parse(content),
plugins: wootWriterSetup({
schema,
placeholder,
plugins,
menuContent: buildMenuItems(schema).fullMenu,
}),
});
};
export default {
name: 'WootMessageEditor',
mixins: [eventListenerMixins, uiSettingsMixin],
props: {
value: { type: String, default: '' },
editorId: { type: String, default: '' },
placeholder: { type: String, default: '' },
isPrivate: { type: Boolean, default: false },
},
data() {
return {
showCannedMenu: false,
mentionSearchKey: '',
cannedSearchTerm: '',
editorView: null,
range: null,
state: undefined,
};
},
computed: {
contentFromEditor() {
return defaultMarkdownSerializer.serialize(this.editorView.state.doc);
},
plugins() {
return [blockFormattingRules(schema), marksFormattingRules(schema)];
},
},
watch: {
value(newValue = '') {
if (newValue !== this.contentFromEditor) {
this.reloadState();
}
},
editorId() {
this.reloadState();
},
},
created() {
this.state = createState(this.value, this.placeholder, this.plugins);
},
mounted() {
this.createEditorView();
this.editorView.updateState(this.state);
this.focusEditorInputField();
},
methods: {
addBold() {
const { state, dispatch } = this.editorView;
const { tr } = state;
tr.insertText('**bold**');
dispatch(tr);
},
reloadState() {
this.state = createState(this.value, this.placeholder, this.plugins);
this.editorView.updateState(this.state);
this.focusEditorInputField();
},
createEditorView() {
this.editorView = new EditorView(this.$refs.editor, {
state: this.state,
dispatchTransaction: tx => {
this.state = this.state.apply(tx);
this.emitOnChange();
},
handleDOMEvents: {
keyup: () => {
this.onKeyup();
},
keydown: (view, event) => {
this.onKeydown(event);
},
focus: () => {
this.onFocus();
},
blur: () => {
this.onBlur();
},
},
});
},
handleKeyEvents() {},
focusEditorInputField() {
const { tr } = this.editorView.state;
const selection = Selection.atEnd(tr.doc);
this.editorView.dispatch(tr.setSelection(selection));
this.editorView.focus();
},
emitOnChange() {
this.editorView.updateState(this.state);
this.$emit('input', this.contentFromEditor);
},
onKeyup() {},
onKeydown() {},
onBlur() {
this.$emit('blur');
},
onFocus() {
this.$emit('focus');
},
},
};
</script>
<style lang="scss">
.ProseMirror-menubar-wrapper {
display: flex;
flex-direction: column;
> .ProseMirror {
padding: 0;
word-break: break-word;
}
}
.editor-root {
width: 100%;
}
.ProseMirror-woot-style {
min-height: 8rem;
max-height: 12rem;
overflow: auto;
}
.ProseMirror-prompt {
z-index: var(--z-index-highest);
background: var(--color-background-light);
border-radius: var(--border-radius-normal);
border: 1px solid var(--color-border);
}
.is-private {
.prosemirror-mention-node {
font-weight: var(--font-weight-medium);
background: var(--s-50);
color: var(--s-900);
padding: 0 var(--space-smaller);
}
}
.editor-wrap {
margin-bottom: var(--space-normal);
}
.message-editor {
border: 1px solid var(--color-border);
border-radius: var(--border-radius-normal);
padding: 0 var(--space-slab);
margin-bottom: 0;
}
.editor_warning {
border: 1px solid var(--r-400);
}
.editor-warning__message {
color: var(--r-400);
font-weight: var(--font-weight-normal);
padding: var(--space-smaller) 0 0 0;
}
</style>

View file

@ -0,0 +1,176 @@
import {
textblockTypeInputRule,
wrappingInputRule,
inputRules,
} from 'prosemirror-inputrules';
import { leafNodeReplacementCharacter } from './utils';
import { createInputRule, defaultInputRuleHandler } from './utils';
import {
isConvertableToCodeBlock,
transformToCodeBlockAction,
insertBlock,
} from './commands';
import { safeInsert } from 'prosemirror-utils';
const MAX_HEADING_LEVEL = 5;
function getHeadingLevel(match) {
return {
level: match[1].length,
};
}
export function headingRule(nodeType, maxLevel) {
return textblockTypeInputRule(
new RegExp('^(#{1,' + maxLevel + '})\\s$'),
nodeType,
getHeadingLevel
);
}
export function blockQuoteRule(nodeType) {
return wrappingInputRule(/^\s*>\s$/, nodeType);
}
export function codeBlockRule(nodeType) {
return textblockTypeInputRule(/^```$/, nodeType);
}
/**
* Get heading rules
*
* @param {Schema} schema
* @returns {}
*/
function getHeadingRules(schema) {
// '# ' for h1, '## ' for h2 and etc
const hashRule = defaultInputRuleHandler(
headingRule(schema.nodes.heading, MAX_HEADING_LEVEL),
true
);
const leftNodeReplacementHashRule = createInputRule(
new RegExp(`${leafNodeReplacementCharacter}(#{1,6})\\s$`),
(state, match, start, end) => {
const level = match[1].length;
return insertBlock(
state,
schema.nodes.heading,
`heading${level}`,
start,
end,
{ level }
);
},
true
);
return [hashRule, leftNodeReplacementHashRule];
}
/**
* Get all block quote input rules
*
* @param {Schema} schema
* @returns {}
*/
function getBlockQuoteRules(schema) {
// '> ' for blockquote
const greatherThanRule = defaultInputRuleHandler(
blockQuoteRule(schema.nodes.blockquote),
true
);
const leftNodeReplacementGreatherRule = createInputRule(
new RegExp(`${leafNodeReplacementCharacter}\\s*>\\s$`),
(state, _match, start, end) => {
return insertBlock(
state,
schema.nodes.blockquote,
'blockquote',
start,
end
);
},
true
);
return [greatherThanRule, leftNodeReplacementGreatherRule];
}
/**
* Get all code block input rules
*
* @param {Schema} schema
* @returns {}
*/
function getCodeBlockRules(schema) {
const threeTildeRule = createInputRule(
/((^`{3,})|(\s`{3,}))(\S*)$/,
(state, match, start, end) => {
const attributes = {};
if (match[4]) {
attributes.language = match[4];
}
const newStart = match[0][0] === ' ' ? start + 1 : start;
if (isConvertableToCodeBlock(state)) {
const tr = transformToCodeBlockAction(state, attributes)
// remove markdown decorator ```
.delete(newStart, end)
.scrollIntoView();
return tr;
}
let { tr } = state;
tr = tr.delete(newStart, end);
const codeBlock = state.schema.nodes.code_block.createChecked();
return safeInsert(codeBlock)(tr);
},
true
);
const leftNodeReplacementThreeTildeRule = createInputRule(
new RegExp(`((${leafNodeReplacementCharacter}\`{3,})|(\\s\`{3,}))(\\S*)$`),
(state, match, start, end) => {
const attributes = {};
if (match[4]) {
attributes.language = match[4];
}
let tr = insertBlock(
state,
schema.nodes.code_block,
'codeblock',
start,
end,
attributes
);
return tr;
},
true
);
return [threeTildeRule, leftNodeReplacementThreeTildeRule];
}
export function blocksInputRule(schema) {
const rules = [];
if (schema.nodes.heading) {
rules.push(...getHeadingRules(schema));
}
if (schema.nodes.blockquote) {
rules.push(...getBlockQuoteRules(schema));
}
if (schema.nodes.code_block) {
rules.push(...getCodeBlockRules(schema));
}
if (rules.length !== 0) {
return inputRules({ rules });
}
return false;
}
export default blocksInputRule;

View file

@ -0,0 +1,157 @@
import { hasParentNodeOfType } from 'prosemirror-utils';
import { TextSelection, NodeSelection } from 'prosemirror-state';
import { mapSlice } from './utils';
export const applyMarkOnRange = (from, to, removeMark, mark, tr) => {
// const { schema } = tr.doc.type;
// const { code } = schema.marks;
// if (mark.type === code) {
// // When turning to code we need to flat some special characters
// import { transformSmartCharsMentionsAndEmojis } from '../plugins/text-formatting/commands/transform-to-code';
// transformSmartCharsMentionsAndEmojis(from, to, tr);
// }
tr.doc.nodesBetween(tr.mapping.map(from), tr.mapping.map(to), (node, pos) => {
if (!node.isText) {
return true;
}
// This is an issue when the user selects some text.
// We need to check if the current node position is less than the range selection from.
// If its true, that means we should apply the mark using the range selection,
// not the current node position.
const nodeBetweenFrom = Math.max(pos, tr.mapping.map(from));
const nodeBetweenTo = Math.min(pos + node.nodeSize, tr.mapping.map(to));
if (removeMark) {
tr.removeMark(nodeBetweenFrom, nodeBetweenTo, mark);
} else {
tr.addMark(nodeBetweenFrom, nodeBetweenTo, mark);
}
return true;
});
return tr;
};
export const insertBlock = (state, nodeType, nodeName, start, end, attrs) => {
// To ensure that match is done after HardBreak.
const { hardBreak, codeBlock, listItem } = state.schema.nodes;
const $pos = state.doc.resolve(start);
if ($pos.nodeAfter.type !== hardBreak) {
return null;
}
// To ensure no nesting is done. (unless we're inserting a codeBlock inside lists)
if (
$pos.depth > 1 &&
!(nodeType === codeBlock && hasParentNodeOfType(listItem)(state.selection))
) {
return null;
}
// Split at the start of autoformatting and delete formatting characters.
let tr = state.tr.delete(start, end).split(start);
let currentNode = tr.doc.nodeAt(start + 1);
// If node has more content split at the end of autoformatting.
let nodeHasMoreContent = false;
tr.doc.nodesBetween(start, start + currentNode.nodeSize, (node, pos) => {
if (!nodeHasMoreContent && node.type === hardBreak) {
nodeHasMoreContent = true;
tr = tr.split(pos + 1).delete(pos, pos + 1);
}
});
if (nodeHasMoreContent) {
currentNode = tr.doc.nodeAt(start + 1);
}
// Create new node and fill with content of current node.
const { blockquote, paragraph } = state.schema.nodes;
let content;
let depth;
if (nodeType === blockquote) {
depth = 3;
content = [paragraph.create({}, currentNode.content)];
} else {
depth = 2;
content = currentNode.content;
}
const newNode = nodeType.create(attrs, content);
// Add new node.
tr = tr
.setSelection(new NodeSelection(tr.doc.resolve(start + 1)))
.replaceSelectionWith(newNode)
.setSelection(new TextSelection(tr.doc.resolve(start + depth)));
return tr;
};
export function transformToCodeBlockAction(state, attrs) {
if (!state.selection.empty) {
// Don't do anything, if there is something selected
return state.tr;
}
const codeBlock = state.schema.nodes.code_block;
const startOfCodeBlockText = state.selection.$from;
const parentPos = startOfCodeBlockText.before();
const end = startOfCodeBlockText.end();
const codeBlockSlice = mapSlice(
state.doc.slice(startOfCodeBlockText.pos, end),
node => {
if (node.type === state.schema.nodes.hard_break) {
return state.schema.text('\n');
}
if (node.isText) {
return node.mark([]);
}
if (node.isInline) {
return node.attrs.text ? state.schema.text(node.attrs.text) : null;
}
return node.content.childCount ? node.content : null;
}
);
const tr = state.tr.replaceRange(
startOfCodeBlockText.pos,
end,
codeBlockSlice
);
// If our offset isnt at 3 (backticks) at the start of line, cater for content.
if (startOfCodeBlockText.parentOffset >= 3) {
return tr.split(startOfCodeBlockText.pos, undefined, [
{ type: codeBlock, attrs },
]);
}
// TODO: Check parent node for valid code block marks, ATM It's not necessary because code block doesn't have any valid mark.
const codeBlockMarks = [];
return tr.setNodeMarkup(parentPos, codeBlock, attrs, codeBlockMarks);
}
export function isConvertableToCodeBlock(state) {
// Before a document is loaded, there is no selection.
if (!state.selection) {
return false;
}
const { $from } = state.selection;
const node = $from.parent;
if (!node.isTextblock || node.type === state.schema.nodes.code_block) {
return false;
}
const parentDepth = $from.depth - 1;
const parentNode = $from.node(parentDepth);
const index = $from.index(parentDepth);
return parentNode.canReplaceWith(
index,
index + 1,
state.schema.nodes.code_block
);
}

View file

@ -0,0 +1,75 @@
const BaseIcon = {
width: 24,
height: 24,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
strokeWidth: 2,
strokeLinecap: 'round',
strokeLinejoin: 'round',
path: '',
};
export const BoldIcon = {
...BaseIcon,
path:
'M6.935 4.44A1.5 1.5 0 0 1 7.996 4h4.383C15.017 4 17 6.182 17 8.625a4.63 4.63 0 0 1-.865 2.682c1.077.827 1.866 2.12 1.866 3.813C18 18.232 15.3 20 13.12 20H8a1.5 1.5 0 0 1-1.5-1.5l-.004-13c0-.397.158-.779.44-1.06ZM9.5 10.25h2.88c.903 0 1.62-.76 1.62-1.625S13.281 7 12.38 7H9.498l.002 3.25Zm0 3V17h3.62c.874 0 1.88-.754 1.88-1.88 0-1.13-.974-1.87-1.88-1.87H9.5Z',
};
export const ItalicsIcon = {
...BaseIcon,
path:
'M9.75 4h8.504a.75.75 0 0 1 .102 1.493l-.102.006h-3.197L10.037 18.5h4.213a.75.75 0 0 1 .742.648l.007.102a.75.75 0 0 1-.648.743L14.25 20h-9.5a.747.747 0 0 1-.746-.75c0-.38.28-.694.645-.743l.101-.007h3.685l.021-.065L13.45 5.499h-3.7a.75.75 0 0 1-.742-.648L9 4.75a.75.75 0 0 1 .648-.743L9.751 4h8.503-8.503Z',
};
export const CodeIcon = {
...BaseIcon,
path:
'm8.066 18.943 6.5-14.5a.75.75 0 0 1 1.404.518l-.036.096-6.5 14.5a.75.75 0 0 1-1.404-.518l.036-.096 6.5-14.5-6.5 14.5ZM2.22 11.47l4.25-4.25a.75.75 0 0 1 1.133.976l-.073.085L3.81 12l3.72 3.719a.75.75 0 0 1-.976 1.133l-.084-.073-4.25-4.25a.75.75 0 0 1-.073-.976l.073-.084 4.25-4.25-4.25 4.25Zm14.25-4.25a.75.75 0 0 1 .976-.073l.084.073 4.25 4.25a.75.75 0 0 1 .073.976l-.073.085-4.25 4.25a.75.75 0 0 1-1.133-.977l.073-.084L20.19 12l-3.72-3.72a.75.75 0 0 1 0-1.06Z',
};
export const LinkIcon = {
...BaseIcon,
path:
'M9.25 7a.75.75 0 0 1 .11 1.492l-.11.008H7a3.5 3.5 0 0 0-.206 6.994L7 15.5h2.25a.75.75 0 0 1 .11 1.492L9.25 17H7a5 5 0 0 1-.25-9.994L7 7h2.25ZM17 7a5 5 0 0 1 .25 9.994L17 17h-2.25a.75.75 0 0 1-.11-1.492l.11-.008H17a3.5 3.5 0 0 0 .206-6.994L17 8.5h-2.25a.75.75 0 0 1-.11-1.492L14.75 7H17ZM7 11.25h10a.75.75 0 0 1 .102 1.493L17 12.75H7a.75.75 0 0 1-.102-1.493L7 11.25h10H7Z',
};
export const UndoIcon = {
...BaseIcon,
path:
'M4.75 2a.75.75 0 0 1 .743.648l.007.102v5.69l4.574-4.56a6.41 6.41 0 0 1 8.879-.179l.186.18a6.41 6.41 0 0 1 0 9.063l-8.846 8.84a.75.75 0 0 1-1.06-1.062l8.845-8.838a4.91 4.91 0 0 0-6.766-7.112l-.178.17L6.562 9.5h5.688a.75.75 0 0 1 .743.648l.007.102a.75.75 0 0 1-.648.743L12.25 11h-7.5a.75.75 0 0 1-.743-.648L4 10.25v-7.5A.75.75 0 0 1 4.75 2Z',
};
export const RedoIcon = {
...BaseIcon,
path:
'M19.25 2a.75.75 0 0 0-.743.648l-.007.102v5.69l-4.574-4.56a6.41 6.41 0 0 0-8.878-.179l-.186.18a6.41 6.41 0 0 0 0 9.063l8.845 8.84a.75.75 0 0 0 1.06-1.062l-8.845-8.838a4.91 4.91 0 0 1 6.766-7.112l.178.17L17.438 9.5H11.75a.75.75 0 0 0-.743.648L11 10.25c0 .38.282.694.648.743l.102.007h7.5a.75.75 0 0 0 .743-.648L20 10.25v-7.5a.75.75 0 0 0-.75-.75Z',
};
export const BulletListIcon = {
...BaseIcon,
path:
'M3.25 17.5a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5Zm3.5.5h14.5a.75.75 0 0 1 .102 1.494l-.102.006H6.75a.75.75 0 0 1-.102-1.493L6.75 18h14.5-14.5Zm-3.5-7a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5Zm3.5.5h14.5a.75.75 0 0 1 .102 1.494L21.25 13H6.75a.75.75 0 0 1-.102-1.493l.102-.007h14.5-14.5Zm-3.5-7A1.25 1.25 0 1 1 3.25 7a1.25 1.25 0 0 1 0-2.499Zm3.5.5h14.5a.75.75 0 0 1 .102 1.494l-.102.006H6.75a.75.75 0 0 1-.102-1.493L6.75 5h14.5-14.5Z',
};
export const TextNumberListIcon = {
...BaseIcon,
path:
'M6 2.75a.75.75 0 0 0-1.434-.307l-.002.003a1.45 1.45 0 0 1-.067.132 4.126 4.126 0 0 1-.238.384c-.217.313-.524.663-.906.902a.75.75 0 1 0 .794 1.272c.125-.078.243-.161.353-.248V7.25a.75.75 0 0 0 1.5 0v-4.5ZM20.5 18.75a.75.75 0 0 0-.75-.75h-9a.75.75 0 0 0 0 1.5h9a.75.75 0 0 0 .75-.75ZM20.5 12.244a.75.75 0 0 0-.75-.75h-9a.75.75 0 1 0 0 1.5h9a.75.75 0 0 0 .75-.75ZM20.5 5.75a.75.75 0 0 0-.75-.75h-9a.75.75 0 0 0 0 1.5h9a.75.75 0 0 0 .75-.75ZM5.15 10.52c-.3-.053-.676.066-.87.26a.75.75 0 1 1-1.06-1.06c.556-.556 1.43-.812 2.192-.677.397.07.805.254 1.115.605.316.358.473.825.473 1.352 0 .62-.271 1.08-.606 1.42-.278.283-.63.511-.906.689l-.08.051a5.88 5.88 0 0 0-.481.34H6.25a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75c0-1.314.984-1.953 1.575-2.337l.06-.04c.318-.205.533-.345.69-.504.134-.136.175-.238.175-.369 0-.223-.061-.318-.098-.36a.42.42 0 0 0-.251-.12ZM2.97 21.28s.093.084.004.005l.006.005.013.013a1.426 1.426 0 0 0 .15.125c.095.07.227.158.397.243.341.17.83.329 1.46.329.64 0 1.196-.181 1.601-.54.408-.36.61-.857.595-1.359A1.775 1.775 0 0 0 6.77 19c.259-.305.412-.685.426-1.101a1.73 1.73 0 0 0-.595-1.36C6.196 16.181 5.64 16 5 16c-.63 0-1.119.158-1.46.33a2.592 2.592 0 0 0-.51.334 1.426 1.426 0 0 0-.037.033l-.013.013-.006.005-.002.003H2.97l-.001.002a.75.75 0 0 0 1.048 1.072 1.1 1.1 0 0 1 .192-.121c.159-.08.42-.171.79-.171.36 0 .536.1.608.164.07.061.09.127.088.187a.325.325 0 0 1-.123.23c-.089.077-.263.169-.573.169a.75.75 0 0 0 0 1.5c.31 0 .484.092.573.168.091.08.121.166.123.231a.232.232 0 0 1-.088.187c-.072.064-.247.164-.608.164a1.75 1.75 0 0 1-.79-.17 1.1 1.1 0 0 1-.192-.122.75.75 0 0 0-1.048 1.072Zm.002-4.563-.001.002c.007-.006.2-.168 0-.002Z',
};
export const Heading1Icon = {
...BaseIcon,
path:
'M19.59 5.081a.746.746 0 0 0-.809.084.751.751 0 0 0-.249.367c-.69 2.051-2.057 3.409-3.168 4.075a.75.75 0 0 0 .772 1.286c.774-.464 1.623-1.18 2.364-2.146v9.503a.75.75 0 0 0 1.5 0V5.772a.75.75 0 0 0-.41-.69ZM3.5 5.75a.75.75 0 0 0-1.5 0v12.5a.75.75 0 0 0 1.5 0V12.5H10v5.75a.75.75 0 0 0 1.5 0V5.75a.75.75 0 0 0-1.5 0V11H3.5V5.75Z',
};
export const Heading2Icon = {
...BaseIcon,
path:
'M4.5 5.75a.75.75 0 0 0-1.5 0v12.5a.75.75 0 0 0 1.5 0V12.5H11v5.75a.75.75 0 0 0 1.5 0V5.75a.75.75 0 0 0-1.5 0V11H4.5V5.75Zm10.921 2.085c.23-.46.913-1.335 2.58-1.335.842 0 1.459.26 1.86.639.397.376.64.921.64 1.611 0 1.963-1.3 3.068-2.958 4.343l-.212.163C15.825 14.409 14 15.806 14 18.25a.75.75 0 0 0 .75.75h6.5a.75.75 0 0 0 0-1.5h-5.66c.315-1.252 1.427-2.11 2.866-3.218C20.05 13.057 22 11.537 22 8.75c0-1.06-.383-2.015-1.11-2.702C20.166 5.364 19.158 5 18 5c-2.333 0-3.484 1.291-3.92 2.165a.75.75 0 0 0 1.341.67Z',
};
export const Heading3Icon = {
...BaseIcon,
path:
'M3.5 5.75a.75.75 0 0 0-1.5 0v12.5a.75.75 0 0 0 1.5 0V12.5H10v5.75a.75.75 0 0 0 1.5 0V5.75a.75.75 0 0 0-1.5 0V11H3.5V5.75Zm11.92 2.085c.23-.46.914-1.335 2.58-1.335.843 0 1.46.26 1.86.639.398.376.64.921.64 1.611 0 .606-.161 1.026-.384 1.332-.228.314-.555.554-.953.735-.816.37-1.802.433-2.383.433a.75.75 0 0 0 0 1.5c.581 0 1.567.063 2.383.433.398.18.725.42.953.735.223.306.384.726.384 1.332 0 1.086-.914 2.25-2.5 2.25-1.727 0-2.348-.76-2.553-1.276a.75.75 0 1 0-1.394.552C14.508 17.926 15.727 19 18 19c2.414 0 4-1.836 4-3.75 0-.894-.245-1.63-.67-2.214A3.679 3.679 0 0 0 20.144 12a3.679 3.679 0 0 0 1.186-1.036c.425-.584.67-1.32.67-2.214 0-1.06-.383-2.015-1.11-2.702C20.165 5.364 19.157 5 18 5c-2.334 0-3.484 1.291-3.92 2.165a.75.75 0 1 0 1.34.67Z',
};

View file

@ -0,0 +1,248 @@
/* eslint-disable no-useless-escape */
import { inputRules } from 'prosemirror-inputrules';
import { applyMarkOnRange } from './commands';
import { createInputRule } from './utils';
const validCombos = {
'**': ['_', '~~'],
'*': ['__', '~~'],
__: ['*', '~~'],
_: ['**', '~~'],
'~~': ['__', '_', '**', '*'],
};
const validRegex = (char, str) => {
for (let i = 0; i < validCombos[char].length; i += 1) {
const ch = validCombos[char][i];
if (ch === str) {
return true;
}
const matchLength = str.length - ch.length;
if (str.substr(matchLength, str.length) === ch) {
return validRegex(ch, str.substr(0, matchLength));
}
}
return false;
};
function addMark(markType, schema, charSize, char) {
return (state, match, start, end) => {
const [, prefix, textWithCombo] = match;
const to = end;
// in case of *string* pattern it matches the text from beginning of the paragraph,
// because we want ** to work for strong text
// that's why "start" argument is wrong and we need to calculate it ourselves
const from = textWithCombo ? start + prefix.length : start;
const nodeBefore = state.doc.resolve(start + prefix.length).nodeBefore;
if (
prefix &&
prefix.length > 0 &&
!validRegex(char, prefix) &&
!(nodeBefore && nodeBefore.type === state.schema.nodes.hard_break)
) {
return null;
}
// fixes the following case: my `*name` is *
// expected result: should ignore special characters inside "code"
if (
state.schema.marks.code &&
state.schema.marks.code.isInSet(state.doc.resolve(from + 1).marks())
) {
return null;
}
// Prevent autoformatting across hardbreaks
let containsHardBreak;
state.doc.nodesBetween(from, to, node => {
if (node.type === schema.nodes.hard_break) {
containsHardBreak = true;
return false;
}
return !containsHardBreak;
});
if (containsHardBreak) {
return null;
}
// fixes autoformatting in heading nodes: # Heading *bold*
// expected result: should not autoformat *bold*; <h1>Heading *bold*</h1>
if (state.doc.resolve(from).sameParent(state.doc.resolve(to))) {
if (!state.doc.resolve(from).parent.type.allowsMarkType(markType)) {
return null;
}
}
// apply mark to the range (from, to)
let tr = state.tr.addMark(from, to, markType.create());
if (charSize > 1) {
// delete special characters after the text
// Prosemirror removes the last symbol by itself, so we need to remove "charSize - 1" symbols
tr = tr.delete(to - (charSize - 1), to);
}
return (
tr
// delete special characters before the text
.delete(from, from + charSize)
.removeStoredMark(markType)
);
};
}
function addCodeMark(markType, specialChar) {
return (state, match, start, end) => {
if (match[1] && match[1].length > 0) {
const allowedPrefixConditions = [
prefix => {
return prefix === '(';
},
prefix => {
const nodeBefore = state.doc.resolve(start + prefix.length)
.nodeBefore;
return (
(nodeBefore && nodeBefore.type === state.schema.nodes.hard_break) ||
false
);
},
];
if (allowedPrefixConditions.every(condition => !condition(match[1]))) {
return null;
}
}
// fixes autoformatting in heading nodes: # Heading `bold`
// expected result: should not autoformat *bold*; <h1>Heading `bold`</h1>
if (state.doc.resolve(start).sameParent(state.doc.resolve(end))) {
if (!state.doc.resolve(start).parent.type.allowsMarkType(markType)) {
return null;
}
}
let tr = state.tr;
// checks if a selection exists and needs to be removed
if (state.selection.from !== state.selection.to) {
tr.delete(state.selection.from, state.selection.to);
end -= state.selection.to - state.selection.from;
}
const regexStart = end - match[2].length + 1;
const codeMark = state.schema.marks.code.create();
return applyMarkOnRange(regexStart, end, false, codeMark, tr)
.setStoredMarks([codeMark])
.delete(regexStart, regexStart + specialChar.length)
.removeStoredMark(markType);
};
}
export const strongRegex1 = /(\S*)(\_\_([^\_\s](\_(?!\_)|[^\_])*[^\_\s]|[^\_\s])\_\_)$/;
export const strongRegex2 = /(\S*)(\*\*([^\*\s](\*(?!\*)|[^\*])*[^\*\s]|[^\*\s])\*\*)$/;
export const italicRegex1 = /(\S*[^\s\_]*)(\_([^\s\_][^\_]*[^\s\_]|[^\s\_])\_)$/;
export const italicRegex2 = /(\S*[^\s\*]*)(\*([^\s\*][^\*]*[^\s\*]|[^\s\*])\*)$/;
export const strikeRegex = /(\S*)(\~\~([^\s\~](\~(?!\~)|[^\~])*[^\s\~]|[^\s\~])\~\~)$/;
export const codeRegex = /(\S*)(`[^\s][^`]*`)$/;
/**
* Create input rules for strong mark
*
* @param {Schema} schema
* @returns {InputRule[]}
*/
function getStrongInputRules(schema) {
// **string** or __strong__ should bold the text
const markLength = 2;
const doubleUnderscoreRule = createInputRule(
strongRegex1,
addMark(schema.marks.strong, schema, markLength, '__')
);
const doubleAsterixRule = createInputRule(
strongRegex2,
addMark(schema.marks.strong, schema, markLength, '**')
);
return [doubleUnderscoreRule, doubleAsterixRule];
}
/**
* Create input rules for em mark
*
* @param {Schema} schema
* @returns {InputRule[]}
*/
function getItalicInputRules(schema) {
// *string* or _string_ should italic the text
const markLength = 1;
const underscoreRule = createInputRule(
italicRegex1,
addMark(schema.marks.em, schema, markLength, '_')
);
const asterixRule = createInputRule(
italicRegex2,
addMark(schema.marks.em, schema, markLength, '*')
);
return [underscoreRule, asterixRule];
}
/**
* Create input rules for strike mark
*
* @param {Schema} schema
* @returns {InputRule[]}
*/
function getStrikeInputRules(schema) {
const markLength = 2;
const doubleTildeRule = createInputRule(
strikeRegex,
addMark(schema.marks.strike, schema, markLength, '~~')
);
return [doubleTildeRule];
}
/**
* Create input rules for code mark
*
* @param {Schema} schema
* @returns {InputRule[]}
*/
function getCodeInputRules(schema) {
const backTickRule = createInputRule(
codeRegex,
addCodeMark(schema.marks.code, '`')
);
return [backTickRule];
}
export function textFormattingInputRules(schema) {
const rules = [];
if (schema.marks.strong) {
rules.push(...getStrongInputRules(schema));
}
if (schema.marks.em) {
rules.push(...getItalicInputRules(schema));
}
if (schema.marks.strike) {
rules.push(...getStrikeInputRules(schema));
}
if (schema.marks.code) {
rules.push(...getCodeInputRules(schema));
}
if (rules.length !== 0) {
return inputRules({ rules });
}
return false;
}
export default textFormattingInputRules;

View file

@ -0,0 +1,227 @@
/* eslint-disable no-cond-assign */
/* eslint-disable no-plusplus */
import { MenuItem } from 'prosemirror-menu';
import { toggleMark, setBlockType } from 'prosemirror-commands';
import { undo, redo } from 'prosemirror-history';
import { wrapInList } from 'prosemirror-schema-list';
import { openPrompt } from '@chatwoot/prosemirror-schema/src/prompt';
import { TextField } from '@chatwoot/prosemirror-schema/src/TextField';
import {
BoldIcon,
ItalicsIcon,
CodeIcon,
UndoIcon,
RedoIcon,
LinkIcon,
Heading3Icon,
Heading2Icon,
Heading1Icon,
TextNumberListIcon,
BulletListIcon,
} from './icons';
import { markActive } from './utils';
// Helpers to create specific types of items
function cmdItem(cmd, options) {
let passedOptions = {
label: options.title,
run: cmd,
};
Object.keys(options).reduce((acc, optionKey) => {
acc[optionKey] = options[optionKey];
return acc;
}, passedOptions);
if ((!options.enable || options.enable === true) && !options.select)
passedOptions[options.enable ? 'enable' : 'select'] = state => cmd(state);
return new MenuItem(passedOptions);
}
function blockTypeIsActive(state, type, attrs) {
const { $from } = state.selection;
let wrapperDepth;
let currentDepth = $from.depth;
while (currentDepth > 0) {
const currentNodeAtDepth = $from.node(currentDepth);
const comparisonAttrs = {
...attrs,
};
// debugger;
if (currentNodeAtDepth.attrs.level) {
comparisonAttrs.level = currentNodeAtDepth.attrs.level;
}
const isType = type.name === currentNodeAtDepth.type.name;
const hasAttrs = Object.keys(attrs).reduce((prev, curr) => {
if (attrs[curr] !== currentNodeAtDepth.attrs[curr]) {
return false;
}
return prev;
}, true);
if (isType && hasAttrs) {
wrapperDepth = currentDepth;
}
currentDepth -= 1;
}
// return wrapperDepth !== undefined;
return wrapperDepth;
}
const toggleBlockType = (type, attrs) => (state, dispatch) => {
const isActive = blockTypeIsActive(state, type, attrs);
const newNodeType = isActive ? state.schema.nodes.paragraph : type;
const setBlockFunction = setBlockType(newNodeType, attrs);
return setBlockFunction(state, dispatch);
};
function markItem(markType, options) {
let passedOptions = {
active(state) {
return markActive(state, markType);
},
enable: true,
};
Object.keys(options).reduce((acc, optionKey) => {
acc[optionKey] = options[optionKey];
return acc;
}, passedOptions);
return cmdItem(toggleMark(markType), passedOptions);
}
function linkItem(markType) {
return new MenuItem({
title: 'Add or remove link',
icon: LinkIcon,
active(state) {
return markActive(state, markType);
},
enable(state) {
return !state.selection.empty;
},
run(state, dispatch, view) {
if (markActive(state, markType)) {
toggleMark(markType)(state, dispatch);
return true;
}
openPrompt({
title: 'Create a link',
fields: {
href: new TextField({
label: 'https://example.com',
class: 'small',
required: true,
}),
},
callback(attrs) {
toggleMark(markType, attrs)(view.state, view.dispatch);
view.focus();
},
});
return false;
},
});
}
function headerItem(nodeType, options) {
const { level = 1 } = options;
return new MenuItem({
title: `Heading ${level}`,
icon: options.icon,
active(state) {
return blockTypeIsActive(state, nodeType, { level });
},
enable() {
return true;
},
run(state, dispatch, view) {
if (blockTypeIsActive(state, nodeType, { level })) {
toggleBlockType(nodeType, { level })(state, dispatch);
return true;
}
toggleBlockType(nodeType, { level })(view.state, view.dispatch);
view.focus();
return false;
},
});
}
function wrapListItem(nodeType, options) {
return cmdItem(wrapInList(nodeType, options.attrs), options);
}
export function buildMenuItems(schema) {
let r = {
toggleStrong: markItem(schema.marks.strong, {
title: 'Toggle strong style',
icon: BoldIcon,
}),
toggleEm: markItem(schema.marks.em, {
title: 'Toggle emphasis',
icon: ItalicsIcon,
}),
toggleCode: markItem(schema.marks.code, {
title: 'Toggle code font',
icon: CodeIcon,
}),
toggleLink: linkItem(schema.marks.link),
wrapBulletList: wrapListItem(schema.nodes.bullet_list, {
title: 'Wrap in bullet list',
icon: BulletListIcon,
}),
wrapOrderedList: wrapListItem(schema.nodes.ordered_list, {
title: 'Wrap in ordered list',
icon: TextNumberListIcon,
}),
toggleH1: headerItem(schema.nodes.heading, {
level: 1,
title: 'Toggle code font',
icon: Heading1Icon,
}),
toggleH2: headerItem(schema.nodes.heading, {
level: 2,
title: 'Toggle code font',
icon: Heading2Icon,
}),
toggleH3: headerItem(schema.nodes.heading, {
level: 3,
title: 'Toggle code font',
icon: Heading3Icon,
}),
undoItem: new MenuItem({
title: 'Undo last change',
run: undo,
enable: state => undo(state),
icon: UndoIcon,
}),
redoItem: new MenuItem({
title: 'Redo last undone change',
run: redo,
enable: state => redo(state),
icon: RedoIcon,
}),
};
let cut = arr => arr.filter(x => x);
r.inlineMenu = [
cut([r.toggleStrong, r.toggleEm, r.toggleCode, r.toggleLink]),
];
r.blockMenu = [
cut([
r.toggleH1,
r.toggleH2,
r.toggleH3,
r.wrapBulletList,
r.wrapOrderedList,
]),
];
r.fullMenu = r.inlineMenu.concat([[r.undoItem, r.redoItem]], r.blockMenu);
return r;
}

View file

@ -0,0 +1,225 @@
import { InputRule } from 'prosemirror-inputrules';
import { Fragment, Slice } from 'prosemirror-model';
import { TextSelection } from 'prosemirror-state';
/**
* Determine if a mark (with specific attribute values) exists anywhere in the selection.
*/
export const markActive = (state, mark) => {
let { from, $from, to, empty } = state.selection;
if (empty) return mark.isInSet(state.storedMarks || $from.marks());
return state.doc.rangeHasMark(from, to, mark);
};
export const hasCode = (state, pos) => {
const { code } = state.schema.marks;
const node = pos >= 0 && state.doc.nodeAt(pos);
if (node) {
return !!node.marks.filter(mark => mark.type === code).length;
}
return false;
};
const hasUnsupportedMarkForBlockInputRule = (state, start, end) => {
const {
doc,
schema: { marks },
} = state;
let unsupportedMarksPresent = false;
const isUnsupportedMark = node =>
node.type === marks.code || node.type === marks.link;
doc.nodesBetween(start, end, node => {
unsupportedMarksPresent =
unsupportedMarksPresent ||
node.marks.filter(isUnsupportedMark).length > 0;
});
return unsupportedMarksPresent;
};
const hasUnsupportedMarkForInputRule = (state, start, end) => {
const {
doc,
schema: { marks },
} = state;
let unsupportedMarksPresent = false;
const isCodemark = mark => mark.type === marks.code;
doc.nodesBetween(start, end, node => {
unsupportedMarksPresent =
unsupportedMarksPresent || node.marks.filter(isCodemark).length > 0;
});
return unsupportedMarksPresent;
};
export function defaultInputRuleHandler(inputRule, isBlockNodeRule = false) {
const originalHandler = inputRule.handler;
inputRule.handler = (state, match, start, end) => {
const unsupportedMarks = isBlockNodeRule
? hasUnsupportedMarkForBlockInputRule(state, start, end)
: hasUnsupportedMarkForInputRule(state, start, end);
if (state.selection.$from.parent.type.spec.code || unsupportedMarks) {
return false;
}
return originalHandler(state, match, start, end);
};
return inputRule;
}
export const createInputRule = (match, handler, isBlockNodeRule = false) =>
defaultInputRuleHandler(new InputRule(match, handler), isBlockNodeRule);
// ProseMirror uses the Unicode Character 'OBJECT REPLACEMENT CHARACTER' (U+FFFC) as text representation for
// leaf nodes, i.e. nodes that don't have any content or text property (e.g. hardBreak, emoji, mention, rule)
// It was introduced because of https://github.com/ProseMirror/prosemirror/issues/262
// This can be used in an input rule regex to be able to include or exclude such nodes.
export const leafNodeReplacementCharacter = '\ufffc';
/**
* Returns false if node contains only empty inline nodes and hardBreaks.
*/
export function hasVisibleContent(node) {
const isInlineNodeHasVisibleContent = inlineNode => {
return inlineNode.isText
? !!inlineNode.textContent.trim()
: inlineNode.type.name !== 'hardBreak';
};
if (node.isInline) {
return isInlineNodeHasVisibleContent(node);
}
if (node.isBlock && (node.isLeaf || node.isAtom)) {
return true;
}
if (!node.childCount) {
return false;
}
for (let index = 0; index < node.childCount; index += 1) {
const child = node.child(index);
if (hasVisibleContent(child)) {
return true;
}
}
return false;
}
/**
* Checks if node is an empty paragraph.
*/
export function isEmptyParagraph(node) {
return (
!node ||
(node.type.name === 'paragraph' && !node.textContent && !node.childCount)
);
}
/**
* Checks if a node has any content. Ignores node that only contain empty block nodes.
*/
export function isNodeEmpty(node) {
if (node && node.textContent) {
return false;
}
if (
!node ||
!node.childCount ||
(node.childCount === 1 && isEmptyParagraph(node.firstChild))
) {
return true;
}
const block = [];
const nonBlock = [];
node.forEach(child =>
child.isInline ? nonBlock.push(child) : block.push(child)
);
return (
!nonBlock.length &&
!block.filter(
childNode =>
(!!childNode.childCount &&
!(
childNode.childCount === 1 && isEmptyParagraph(childNode.firstChild)
)) ||
childNode.isAtom
).length
);
}
export const compose = (...functions) => args =>
functions.reduceRight((arg, fn) => fn(arg), args);
/**
* A helper to get the underlying array of a fragment.
*/
export function getFragmentBackingArray(fragment) {
return fragment.content;
}
export function mapFragment(content, callback, parent) {
const children = [];
for (let i = 0, size = content.childCount; i < size; i += 1) {
const node = content.child(i);
const transformed = node.isLeaf
? callback(node, parent, i)
: callback(
node.copy(mapFragment(node.content, callback, node)),
parent,
i
);
if (transformed) {
if (transformed) {
children.push(...getFragmentBackingArray(transformed));
} else if (Array.isArray(transformed)) {
children.push(...transformed);
} else {
children.push(transformed);
}
}
}
return Fragment.fromArray(children);
}
export function mapSlice(slice, callback) {
const fragment = mapFragment(slice.content, callback);
return new Slice(fragment, slice.openStart, slice.openEnd);
}
export function atTheEndOfDoc(state) {
const { selection, doc } = state;
return doc.nodeSize - selection.$to.pos - 2 === selection.$to.depth;
}
export function canMoveDown(state) {
const { selection } = state;
if (selection instanceof TextSelection) {
if (!selection.empty) {
return true;
}
}
return !atTheEndOfDoc(state);
}
export function atTheBeginningOfDoc(state) {
const { selection } = state;
return selection.$from.pos === selection.$from.depth;
}
export function canMoveUp(state) {
const { selection } = state;
if (selection instanceof TextSelection) {
if (!selection.empty) {
return true;
}
}
return !atTheBeginningOfDoc(state);
}

View file

@ -14,8 +14,6 @@
v-model="articleContent" v-model="articleContent"
class="article-content" class="article-content"
:placeholder="$t('HELP_CENTER.EDIT_ARTICLE.CONTENT_PLACEHOLDER')" :placeholder="$t('HELP_CENTER.EDIT_ARTICLE.CONTENT_PLACEHOLDER')"
:is-format-mode="true"
:override-line-breaks="true"
@focus="onFocus" @focus="onFocus"
@blur="onBlur" @blur="onBlur"
@input="onContentInput" @input="onContentInput"
@ -26,7 +24,8 @@
<script> <script>
import { debounce } from '@chatwoot/utils'; import { debounce } from '@chatwoot/utils';
import ResizableTextArea from 'shared/components/ResizableTextArea'; import ResizableTextArea from 'shared/components/ResizableTextArea';
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/FullEditor.vue';
export default { export default {
components: { components: {

View file

@ -48,7 +48,10 @@
"ninja-keys": "^1.1.9", "ninja-keys": "^1.1.9",
"opus-recorder": "^8.0.5", "opus-recorder": "^8.0.5",
"prosemirror-markdown": "1.5.1", "prosemirror-markdown": "1.5.1",
"prosemirror-menu": "^1.2.1",
"prosemirror-state": "1.3.4", "prosemirror-state": "1.3.4",
"prosemirror-tables": "^1.3.0",
"prosemirror-utils": "^0.9.6",
"prosemirror-view": "1.18.4", "prosemirror-view": "1.18.4",
"semver": "7.3.5", "semver": "7.3.5",
"spinkit": "~1.2.5", "spinkit": "~1.2.5",

View file

@ -12228,6 +12228,11 @@ orderedmap@^1.1.0:
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.1.tgz#c618e77611b3b21d0fe3edc92586265e0059c789" resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.1.tgz#c618e77611b3b21d0fe3edc92586265e0059c789"
integrity sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ== integrity sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ==
orderedmap@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.0.tgz#819457082fa3a06abd316d83a281a1ca467437cd"
integrity sha512-/pIFexOm6S70EPdznemIz3BQZoJ4VTFrhqzu0ACBqBgeLsLxq8e6Jim63ImIfwW/zAD1AlXpRMlOv3aghmo4dA==
original@^1.0.0: original@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
@ -13645,6 +13650,14 @@ prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.4:
prosemirror-state "^1.0.0" prosemirror-state "^1.0.0"
w3c-keyname "^2.2.0" w3c-keyname "^2.2.0"
prosemirror-keymap@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz#d5cc9da9b712020690a994b50b92a0e448a60bf5"
integrity sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==
dependencies:
prosemirror-state "^1.0.0"
w3c-keyname "^2.2.0"
prosemirror-markdown@1.5.1: prosemirror-markdown@1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.5.1.tgz#877c7faea2225d3c52e988599bbe4457bcb3190f" resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.5.1.tgz#877c7faea2225d3c52e988599bbe4457bcb3190f"
@ -13663,6 +13676,16 @@ prosemirror-menu@^1.1.4:
prosemirror-history "^1.0.0" prosemirror-history "^1.0.0"
prosemirror-state "^1.0.0" prosemirror-state "^1.0.0"
prosemirror-menu@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prosemirror-menu/-/prosemirror-menu-1.2.1.tgz#94d99a8547b7ba5680c20e9c497ce19846ce3b2c"
integrity sha512-sBirXxVfHalZO4f1ZS63WzewINK4182+7dOmoMeBkqYO8wqMBvBS7wQuwVOHnkMWPEh0+N0LJ856KYUN+vFkmQ==
dependencies:
crelt "^1.0.0"
prosemirror-commands "^1.0.0"
prosemirror-history "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-model@^1.0.0, prosemirror-model@^1.1.0: prosemirror-model@^1.0.0, prosemirror-model@^1.1.0:
version "1.14.1" version "1.14.1"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.14.1.tgz#d784c67f95a5d66b853e82ff9a87a50353ef9cd5" resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.14.1.tgz#d784c67f95a5d66b853e82ff9a87a50353ef9cd5"
@ -13670,6 +13693,13 @@ prosemirror-model@^1.0.0, prosemirror-model@^1.1.0:
dependencies: dependencies:
orderedmap "^1.1.0" orderedmap "^1.1.0"
prosemirror-model@^1.16.0, prosemirror-model@^1.8.1:
version "1.18.3"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.3.tgz#d1026a78cff928fd600e90d87cf7d162e0a4e3fd"
integrity sha512-yUVejauEY3F1r7PDy4UJKEGeIU+KFc71JQl5sNvG66CLVdKXRjhWpBW6KMeduGsmGOsw85f6EGrs6QxIKOVILA==
dependencies:
orderedmap "^2.0.0"
prosemirror-schema-list@^1.1.4: prosemirror-schema-list@^1.1.4:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.4.tgz#471f9caf2d2bed93641d2e490434c0d2d4330df1" resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.4.tgz#471f9caf2d2bed93641d2e490434c0d2d4330df1"
@ -13686,6 +13716,26 @@ prosemirror-state@1.3.4, prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, pro
prosemirror-model "^1.0.0" prosemirror-model "^1.0.0"
prosemirror-transform "^1.0.0" prosemirror-transform "^1.0.0"
prosemirror-state@^1.3.1:
version "1.4.2"
resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.2.tgz#f93bd8a33a4454efab917ba9b738259d828db7e5"
integrity sha512-puuzLD2mz/oTdfgd8msFbe0A42j5eNudKAAPDB0+QJRw8cO1ygjLmhLrg9RvDpf87Dkd6D4t93qdef00KKNacQ==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-view "^1.27.0"
prosemirror-tables@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.3.0.tgz#262fa7d030f7bebef7b5fd9a045bce9a786c198c"
integrity sha512-ujzOb37O2ahmqI626Y0N0V/SZxuA9OGNYnsIMWdfecwkc8S8OShOqeD4kKUxpD0JcP81Z8qy/ulrXQuKhS4WUg==
dependencies:
prosemirror-keymap "^1.1.2"
prosemirror-model "^1.8.1"
prosemirror-state "^1.3.1"
prosemirror-transform "^1.2.1"
prosemirror-view "^1.13.3"
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0: prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0:
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.3.2.tgz#5620ebe7379e6fae4f34ecc881886cb22ce96579" resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.3.2.tgz#5620ebe7379e6fae4f34ecc881886cb22ce96579"
@ -13693,6 +13743,18 @@ prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0:
dependencies: dependencies:
prosemirror-model "^1.0.0" prosemirror-model "^1.0.0"
prosemirror-transform@^1.2.1:
version "1.7.0"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.7.0.tgz#a8a0768f3ee6418d26ebef435beda9d43c65e472"
integrity sha512-O4T697Cqilw06Zvc3Wm+e237R6eZtJL/xGMliCi+Uo8VL6qHk6afz1qq0zNjT3eZMuYwnP8ZS0+YxX/tfcE9TQ==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-utils@^0.9.6:
version "0.9.6"
resolved "https://registry.yarnpkg.com/prosemirror-utils/-/prosemirror-utils-0.9.6.tgz#3d97bd85897e3b535555867dc95a51399116a973"
integrity sha512-UC+j9hQQ1POYfMc5p7UFxBTptRiGPR7Kkmbl3jVvU8VgQbkI89tR/GK+3QYC8n+VvBZrtAoCrJItNhWSxX3slA==
prosemirror-view@1.18.4, prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.17.2: prosemirror-view@1.18.4, prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.17.2:
version "1.18.4" version "1.18.4"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.18.4.tgz#179141df117cf414434ade08115f2e233d135f6d" resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.18.4.tgz#179141df117cf414434ade08115f2e233d135f6d"
@ -13702,6 +13764,15 @@ prosemirror-view@1.18.4, prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prose
prosemirror-state "^1.0.0" prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0" prosemirror-transform "^1.1.0"
prosemirror-view@^1.13.3, prosemirror-view@^1.27.0:
version "1.29.1"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.29.1.tgz#9a4938d1a863ca76e23c6573d30e3ece2b17d9a0"
integrity sha512-OhujVZSDsh0l0PyHNdfaBj6DBkbhYaCfbaxmTeFrMKd/eWS+G6IC+OAbmR9IsLC8Se1HSbphMaXnsXjupHL3UQ==
dependencies:
prosemirror-model "^1.16.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0"
proto-list@~1.2.1: proto-list@~1.2.1:
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"