# Pull Request Template ## Description 1. This PR is an enhancement to https://github.com/chatwoot/chatwoot/pull/13045 It strips unsupported formatting from **message signatures** based on each channel’s formatting capabilities defined in the `FORMATTING` config 2. Remove usage of plain editor in Compose new conversation modal Only the following signature elements are considered: <strong>bold (<code inline="">strong</code>), italic (<code inline="">em</code>), links (<code inline="">link</code>), images (<code inline="">image</code>)</strong>.</p> Any formatting not supported by the target channel is automatically removed before the signature is appended. <h3>Channel-wise Signature Formatting Support</h3> Channel | Keeps in Signature | Strips from Signature -- | -- | -- Email | bold, italic, links, images | — WebWidget | bold, italic, links, images | — API | bold, italic | links, images WhatsApp | bold, italic | links, images Telegram | bold, italic, links | images Facebook | bold, italic | links, images Instagram | bold, italic | links, images Line | bold, italic | links, images SMS | — | everything Twilio SMS | — | everything Twitter/X | — | everything <hr> <h3>📝 Note</h3> <blockquote> <p>Message signatures only support <strong>bold, italic, links, and images</strong>.<br> Other formatting options available in the editor (lists, code blocks, strike-through, etc.) do <strong>not apply</strong> to signatures and are ignored.</p> </blockquote> ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? ### Loom video https://www.loom.com/share/d325ab86ca514c6d8f90dfe72a8928dd ## 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>
254 lines
6.0 KiB
JavaScript
254 lines
6.0 KiB
JavaScript
// Formatting rules for different contexts (channels and special contexts)
|
|
// marks: inline formatting (strong, em, code, link, strike)
|
|
// nodes: block structures (bulletList, orderedList, codeBlock, blockquote)
|
|
export const FORMATTING = {
|
|
// Channel formatting
|
|
'Channel::Email': {
|
|
marks: ['strong', 'em', 'code', 'link'],
|
|
nodes: ['bulletList', 'orderedList', 'codeBlock', 'blockquote', 'image'],
|
|
menu: [
|
|
'strong',
|
|
'em',
|
|
'code',
|
|
'link',
|
|
'bulletList',
|
|
'orderedList',
|
|
'undo',
|
|
'redo',
|
|
],
|
|
},
|
|
'Channel::WebWidget': {
|
|
marks: ['strong', 'em', 'code', 'link', 'strike'],
|
|
nodes: ['bulletList', 'orderedList', 'codeBlock', 'blockquote', 'image'],
|
|
menu: [
|
|
'strong',
|
|
'em',
|
|
'code',
|
|
'link',
|
|
'strike',
|
|
'bulletList',
|
|
'orderedList',
|
|
'undo',
|
|
'redo',
|
|
],
|
|
},
|
|
'Channel::Api': {
|
|
marks: ['strong', 'em'],
|
|
nodes: [],
|
|
menu: ['strong', 'em', 'undo', 'redo'],
|
|
},
|
|
'Channel::FacebookPage': {
|
|
marks: ['strong', 'em', 'code', 'strike'],
|
|
nodes: ['bulletList', 'orderedList', 'codeBlock'],
|
|
menu: [
|
|
'strong',
|
|
'em',
|
|
'code',
|
|
'strike',
|
|
'bulletList',
|
|
'orderedList',
|
|
'undo',
|
|
'redo',
|
|
],
|
|
},
|
|
'Channel::TwitterProfile': {
|
|
marks: [],
|
|
nodes: [],
|
|
menu: [],
|
|
},
|
|
'Channel::TwilioSms': {
|
|
marks: [],
|
|
nodes: [],
|
|
menu: [],
|
|
},
|
|
'Channel::Sms': {
|
|
marks: [],
|
|
nodes: [],
|
|
menu: [],
|
|
},
|
|
'Channel::Whatsapp': {
|
|
marks: ['strong', 'em', 'code', 'strike'],
|
|
nodes: ['bulletList', 'orderedList', 'codeBlock'],
|
|
menu: [
|
|
'strong',
|
|
'em',
|
|
'code',
|
|
'strike',
|
|
'bulletList',
|
|
'orderedList',
|
|
'undo',
|
|
'redo',
|
|
],
|
|
},
|
|
'Channel::Line': {
|
|
marks: ['strong', 'em', 'code', 'strike'],
|
|
nodes: ['codeBlock'],
|
|
menu: ['strong', 'em', 'code', 'strike', 'undo', 'redo'],
|
|
},
|
|
'Channel::Telegram': {
|
|
marks: ['strong', 'em', 'link', 'code'],
|
|
nodes: [],
|
|
menu: ['strong', 'em', 'link', 'code', 'undo', 'redo'],
|
|
},
|
|
'Channel::Instagram': {
|
|
marks: ['strong', 'em', 'code', 'strike'],
|
|
nodes: ['bulletList', 'orderedList'],
|
|
menu: [
|
|
'strong',
|
|
'em',
|
|
'code',
|
|
'bulletList',
|
|
'orderedList',
|
|
'strike',
|
|
'undo',
|
|
'redo',
|
|
],
|
|
},
|
|
'Channel::Voice': {
|
|
marks: [],
|
|
nodes: [],
|
|
menu: [],
|
|
},
|
|
// Special contexts (not actual channels)
|
|
'Context::Default': {
|
|
marks: ['strong', 'em', 'code', 'link', 'strike'],
|
|
nodes: ['bulletList', 'orderedList', 'codeBlock', 'blockquote'],
|
|
menu: [
|
|
'strong',
|
|
'em',
|
|
'code',
|
|
'link',
|
|
'strike',
|
|
'bulletList',
|
|
'orderedList',
|
|
'undo',
|
|
'redo',
|
|
],
|
|
},
|
|
'Context::MessageSignature': {
|
|
marks: ['strong', 'em', 'link'],
|
|
nodes: ['image'],
|
|
menu: ['strong', 'em', 'link', 'undo', 'redo', 'imageUpload'],
|
|
},
|
|
'Context::InboxSettings': {
|
|
marks: ['strong', 'em', 'link'],
|
|
nodes: [],
|
|
menu: ['strong', 'em', 'link', 'undo', 'redo'],
|
|
},
|
|
};
|
|
|
|
// Editor menu options for Full Editor
|
|
export const ARTICLE_EDITOR_MENU_OPTIONS = [
|
|
'strong',
|
|
'em',
|
|
'link',
|
|
'undo',
|
|
'redo',
|
|
'bulletList',
|
|
'orderedList',
|
|
'h1',
|
|
'h2',
|
|
'h3',
|
|
'imageUpload',
|
|
'code',
|
|
];
|
|
|
|
/**
|
|
* Markdown formatting patterns for stripping unsupported formatting.
|
|
*
|
|
* Maps camelCase type names to ProseMirror snake_case schema names.
|
|
* Order matters: codeBlock before code to avoid partial matches.
|
|
*/
|
|
export const MARKDOWN_PATTERNS = [
|
|
// --- BLOCK NODES ---
|
|
{
|
|
type: 'codeBlock', // PM: code_block, eg: ```js\ncode\n```
|
|
patterns: [
|
|
{ pattern: /`{3}(?:\w+)?\n?([\s\S]*?)`{3}/g, replacement: '$1' },
|
|
],
|
|
},
|
|
{
|
|
type: 'blockquote', // PM: blockquote, eg: > quote
|
|
patterns: [{ pattern: /^> ?/gm, replacement: '' }],
|
|
},
|
|
{
|
|
type: 'bulletList', // PM: bullet_list, eg: - item
|
|
patterns: [{ pattern: /^[\t ]*[-*+]\s+/gm, replacement: '' }],
|
|
},
|
|
{
|
|
type: 'orderedList', // PM: ordered_list, eg: 1. item
|
|
patterns: [{ pattern: /^[\t ]*\d+\.\s+/gm, replacement: '' }],
|
|
},
|
|
{
|
|
type: 'heading', // PM: heading, eg: ## Heading
|
|
patterns: [{ pattern: /^#{1,6}\s+/gm, replacement: '' }],
|
|
},
|
|
{
|
|
type: 'horizontalRule', // PM: horizontal_rule, eg: ---
|
|
patterns: [{ pattern: /^(?:---|___|\*\*\*)\s*$/gm, replacement: '' }],
|
|
},
|
|
{
|
|
type: 'image', // PM: image, eg: 
|
|
patterns: [{ pattern: /!\[([^\]]*)\]\([^)]+\)/g, replacement: '$1' }],
|
|
},
|
|
{
|
|
type: 'hardBreak', // PM: hard_break, eg: line\\\n or line \n
|
|
patterns: [
|
|
{ pattern: /\\\n/g, replacement: '\n' },
|
|
{ pattern: / {2,}\n/g, replacement: '\n' },
|
|
],
|
|
},
|
|
// --- INLINE MARKS ---
|
|
{
|
|
type: 'strong', // PM: strong, eg: **bold** or __bold__
|
|
patterns: [
|
|
{ pattern: /\*\*(.+?)\*\*/g, replacement: '$1' },
|
|
{ pattern: /__(.+?)__/g, replacement: '$1' },
|
|
],
|
|
},
|
|
{
|
|
type: 'em', // PM: em, eg: *italic* or _italic_
|
|
patterns: [
|
|
{ pattern: /(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, replacement: '$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
|
|
{
|
|
pattern: /(?<=^|[\s])_([^_\s][^_]*[^_\s]|[^_\s])_(?=$|[\s])/g,
|
|
replacement: '$1',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: 'strike', // PM: strike, eg: ~~strikethrough~~
|
|
patterns: [{ pattern: /~~(.+?)~~/g, replacement: '$1' }],
|
|
},
|
|
{
|
|
type: 'code', // PM: code, eg: `inline code`
|
|
patterns: [{ pattern: /`([^`]+)`/g, replacement: '$1' }],
|
|
},
|
|
{
|
|
type: 'link', // PM: link, eg: [text](url)
|
|
patterns: [{ pattern: /\[([^\]]+)\]\([^)]+\)/g, replacement: '$1' }],
|
|
},
|
|
];
|
|
|
|
// Editor image resize options for Message Editor
|
|
export const MESSAGE_EDITOR_IMAGE_RESIZES = [
|
|
{
|
|
name: 'Small',
|
|
height: '24px',
|
|
},
|
|
{
|
|
name: 'Medium',
|
|
height: '48px',
|
|
},
|
|
{
|
|
name: 'Large',
|
|
height: '72px',
|
|
},
|
|
{
|
|
name: 'Original Size',
|
|
height: 'auto',
|
|
},
|
|
];
|