Skip to content

Commit 7f7c365

Browse files
committed
fix: strip trailing whitespace from sent messages
1 parent 1761bf1 commit 7f7c365

1 file changed

Lines changed: 47 additions & 28 deletions

File tree

src/lib/components/common/RichTextInput.svelte

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@
3737
});
3838
turndownService.escape = (string) => string;
3939
40+
// Turndown renders <br> as a Markdown " \n" hard break, leaving invisible
41+
// trailing spaces on every line. Strip it, but only after real content and
42+
// never inside fenced code blocks where trailing whitespace can be significant.
43+
const stripTrailingWhitespace = (md: string): string =>
44+
md
45+
.split(/(```[\s\S]*?```|~~~[\s\S]*?~~~)/g)
46+
.map((part, i) =>
47+
i % 2
48+
? part
49+
: part.replace(/([^\n \t])[ \t]+\n/g, '$1\n').replace(/([^\n \t])[ \t]+$/g, '$1')
50+
)
51+
.join('');
52+
4053
// Produce single newlines between paragraphs instead of double.
4154
// TipTap wraps every line in <p> tags; the default Turndown rule emits
4255
// \n\n around each paragraph which then required a destructive
@@ -711,7 +724,7 @@
711724
try {
712725
// Try parsing the value
713726
return marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
714-
breaks: false
727+
breaks: true
715728
});
716729
} catch (error) {
717730
// If no attempts remain, fallback to plain text
@@ -885,26 +898,30 @@
885898
jsonValue = editor.getJSON();
886899
887900
if (richText) {
888-
mdValue = turndownService
889-
.turndown(
890-
htmlValue
891-
.replace(/<p><\/p>/g, '<br/>')
892-
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
893-
)
894-
.replace(/\u00a0/g, ' ');
901+
mdValue = stripTrailingWhitespace(
902+
turndownService
903+
.turndown(
904+
htmlValue
905+
.replace(/<p><\/p>/g, '<br/>')
906+
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
907+
)
908+
.replace(/\u00a0/g, ' ')
909+
);
895910
} else {
896-
mdValue = turndownService
897-
.turndown(
898-
htmlValue
899-
// Replace empty paragraphs with line breaks
900-
.replace(/<p><\/p>/g, '<br/>')
901-
// Replace multiple spaces with non-breaking spaces
902-
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
903-
// Replace tabs with non-breaking spaces (preserve indentation)
904-
.replace(/\t/g, '\u00a0\u00a0\u00a0\u00a0') // 1 tab = 4 spaces
905-
)
906-
// Convert non-breaking spaces back to regular spaces for markdown
907-
.replace(/\u00a0/g, ' ');
911+
mdValue = stripTrailingWhitespace(
912+
turndownService
913+
.turndown(
914+
htmlValue
915+
// Replace empty paragraphs with line breaks
916+
.replace(/<p><\/p>/g, '<br/>')
917+
// Replace multiple spaces with non-breaking spaces
918+
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
919+
// Replace tabs with non-breaking spaces (preserve indentation)
920+
.replace(/\t/g, '\u00a0\u00a0\u00a0\u00a0') // 1 tab = 4 spaces
921+
)
922+
// Convert non-breaking spaces back to regular spaces for markdown
923+
.replace(/\u00a0/g, ' ')
924+
);
908925
}
909926
910927
onChange({
@@ -1267,14 +1284,16 @@
12671284
12681285
const jsonValue = editor.getJSON();
12691286
const htmlValue = editor.getHTML();
1270-
let mdValue = turndownService
1271-
.turndown(
1272-
(preserveBreaks ? htmlValue.replace(/<p><\/p>/g, '<br/>') : htmlValue).replace(
1273-
/ {2,}/g,
1274-
(m) => m.replace(/ /g, '\u00a0')
1287+
let mdValue = stripTrailingWhitespace(
1288+
turndownService
1289+
.turndown(
1290+
(preserveBreaks ? htmlValue.replace(/<p><\/p>/g, '<br/>') : htmlValue).replace(
1291+
/ {2,}/g,
1292+
(m) => m.replace(/ /g, '\u00a0')
1293+
)
12751294
)
1276-
)
1277-
.replace(/\u00a0/g, ' ');
1295+
.replace(/\u00a0/g, ' ')
1296+
);
12781297
12791298
if (value === '') {
12801299
editor.commands.clearContent(); // Clear content if value is empty
@@ -1300,7 +1319,7 @@
13001319
preserveBreaks
13011320
? value
13021321
: marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
1303-
breaks: false
1322+
breaks: true
13041323
})
13051324
);
13061325

0 commit comments

Comments
 (0)