fix: prevent -- signature delimiter rendering as \ in bubble (#14134)
# Pull Request Template ## Description Fixes https://linear.app/chatwoot/issue/CW-6903/signature-delimiter-renders-as-h2-when-using-enter-line-before **1**. Fixes an issue where the signature delimiter `--` gets parsed as an H2 when using **Enter** (new paragraph) before or after it, causing it to render as a bold `\` in the message bubble. * Ensures `--` renders as plain text * Aligns renderer with parser behavior (both disable `lheading`) * Prevents stray `\` from appearing as heading text **2**. Also fixes a related editor issue where toggling signature **off** leaves behind a stray `\` or `-- \`. * Strips blank paragraph markers (`\`) and dangling hard breaks (`\<newline>`) from ProseMirror serializer * Applied in both `appendSignature` and `removeSignature` * Replaces `trimEnd()` with shared helpers (`trimTrailingBlanks` / `stripTrailingBlankMarkers`) ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? #### Screenshots **Before** <img width="194" height="204" alt="image" src="https://github.com/user-attachments/assets/b286ab50-7f89-4910-a552-1568902b93b3" /> **After** <img width="194" height="220" alt="image" src="https://github.com/user-attachments/assets/658cd543-bce2-46e2-a319-35e5374f1aef" /> **Editor** https://linear.app/chatwoot/issue/CW-6903/signature-delimiter-renders-as-in-h2-when-using-enter-line#comment-5814b882 ### Steps #### Editor 1. Enable agent signature 2. Add and remove new lines around the signature using Enter/shift enter 3. Toggle signature off 4. Notice stray `\` or `-- \` remains #### Bubble 1. Enable agent signature 2. Send a message using Enter between lines 3. Verify `--` renders correctly (no H2, no bold `\`) ## 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:
co-authored by
Muhsin Keloth
parent
6386eec5e7
commit
c1d167bd64
@@ -123,6 +123,13 @@ export function cleanSignature(signature) {
|
||||
}
|
||||
}
|
||||
|
||||
// Strip `\<newline>` hardbreak markers trailing `--` after a signature slice
|
||||
const stripDelimiterHardbreaks = body =>
|
||||
body.replace(/(--)\s*(?:\\\s*)+$/, '$1');
|
||||
|
||||
// Strip standalone blank-paragraph markers (`\` on their own lines).
|
||||
const stripTrailingBlankLine = body => body.replace(/\n(?:\s*\\\n)+$/, '');
|
||||
|
||||
/**
|
||||
* Adds the signature delimiter to the beginning of the signature.
|
||||
*
|
||||
@@ -227,13 +234,19 @@ export function removeSignature(body, signature, channelType) {
|
||||
// trimming will ensure any spaces or new lines before the signature are removed
|
||||
// This means we will have the delimiter at the end
|
||||
if (signatureIndex > -1) {
|
||||
newBody = newBody.substring(0, signatureIndex).trimEnd();
|
||||
newBody = stripDelimiterHardbreaks(
|
||||
newBody.substring(0, signatureIndex)
|
||||
).trimEnd();
|
||||
}
|
||||
|
||||
// Remove delimiter if it's at the end
|
||||
if (newBody.endsWith(SIGNATURE_DELIMITER)) {
|
||||
// if the delimiter is at the end, remove it
|
||||
newBody = newBody.slice(0, -SIGNATURE_DELIMITER.length);
|
||||
// strip any trailing blank-line markers
|
||||
if (signatureIndex > -1) {
|
||||
newBody = stripTrailingBlankLine(newBody);
|
||||
}
|
||||
}
|
||||
|
||||
return newBody;
|
||||
|
||||
@@ -336,6 +336,38 @@ describe('removeSignature', () => {
|
||||
'This is a test\n\n'
|
||||
);
|
||||
});
|
||||
it('strips blank-paragraph marker before the delimiter', () => {
|
||||
expect(removeSignature('hey\n\n\\\n--\n\nHello there', 'Hello there')).toBe(
|
||||
'hey'
|
||||
);
|
||||
});
|
||||
it('strips multiple consecutive blank-paragraph markers before the delimiter', () => {
|
||||
expect(
|
||||
removeSignature('wewe\n\n\\\n\\\n\\\n--\n\nHello there', 'Hello there')
|
||||
).toBe('wewe');
|
||||
});
|
||||
it('strips dangling hardbreak when signature shared a paragraph with "--"', () => {
|
||||
expect(removeSignature('hey\n\n--\\\nHello there', 'Hello there')).toBe(
|
||||
'hey\n\n'
|
||||
);
|
||||
});
|
||||
it('preserves trailing backslash in user text when appending', () => {
|
||||
expect(appendSignature('The path is C:\\', 'Best\nAgent')).toContain(
|
||||
'C:\\'
|
||||
);
|
||||
expect(appendSignature('C:\\\n', 'Best\nAgent')).toContain('C:\\');
|
||||
expect(appendSignature('C:\\\n\n', 'Best\nAgent')).toContain('C:\\');
|
||||
});
|
||||
it('preserves trailing backslash in user text when removing', () => {
|
||||
expect(removeSignature('C:\\\n--\n\nBest\nAgent', 'Best\nAgent')).toContain(
|
||||
'C:\\'
|
||||
);
|
||||
expect(removeSignature('C:\\\n--', 'no matching sig')).toContain('C:\\');
|
||||
expect(removeSignature('C:\\\nBest\\\nAgent', 'Best\nAgent')).toContain(
|
||||
'C:\\'
|
||||
);
|
||||
expect(removeSignature('notes\n\\\n--', 'no matching sig')).toContain('\\');
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeSignature with stripped signature', () => {
|
||||
|
||||
@@ -42,6 +42,7 @@ const createMarkdownInstance = (linkify = true) => {
|
||||
quotes: '\u201c\u201d\u2018\u2019',
|
||||
maxNesting: 20,
|
||||
})
|
||||
.disable(['lheading'])
|
||||
.use(mentionPlugin)
|
||||
.use(imgResizeManager)
|
||||
.use(mila, {
|
||||
|
||||
@@ -33,6 +33,13 @@ describe('#MessageFormatter', () => {
|
||||
<h2>tool</h2>`
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render a setext heading when text is followed by "--"', () => {
|
||||
const message = 'hy\n\n\\\n\\-\\-\n\nHello there';
|
||||
const result = new MessageFormatter(message).formattedMessage;
|
||||
expect(result).not.toMatch('<h2>');
|
||||
expect(result).not.toMatch('<h1>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('content with image and has "cw_image_height" query at the end of URL', () => {
|
||||
|
||||
Reference in New Issue
Block a user