fix(chat): preserve fenced code blocks in plain-text copy output

The plain-text copy path for assistant messages was lossy when the message
contained fenced code blocks. We were unwrapping triple-backtick blocks first
and then running the rest of the markdown cleanup against the extracted code.
That caused valid code lines that start with markdown-like prefixes, such as
`#`, `-`, `*`, or `1.`, to be rewritten as headings or list items instead of
being copied verbatim.

This change protects fenced code before the generic markdown normalization runs.
Each fenced block is captured into a temporary array, replaced with a unique
placeholder token, and then restored after the existing regex passes finish.
The stored code trims only the trailing newline that comes from the fenced block
wrapper, so the restored content stays clean without altering the actual code.

As a result, "Copy as text" now preserves code inside fenced blocks exactly as
rendered, while keeping the existing markdown-to-plain-text behavior unchanged
for the rest of the message content.
This commit is contained in:
Haileyesus
2026-03-09 22:01:30 +03:00
parent a52483e9f7
commit e6c438fd49

View File

@@ -14,7 +14,12 @@ type CopyFormatOption = {
// Converts markdown into readable plain text for "Copy as text".
const convertMarkdownToPlainText = (markdown: string): string => {
let plainText = markdown.replace(/\r\n/g, '\n');
plainText = plainText.replace(/```[\w-]*\n([\s\S]*?)```/g, '$1');
const codeBlocks: string[] = [];
plainText = plainText.replace(/```[\w-]*\n([\s\S]*?)```/g, (_match, code: string) => {
const placeholder = `@@CODEBLOCK${codeBlocks.length}@@`;
codeBlocks.push(code.replace(/\n$/, ''));
return placeholder;
});
plainText = plainText.replace(/`([^`]+)`/g, '$1');
plainText = plainText.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '$1');
plainText = plainText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
@@ -27,6 +32,7 @@ const convertMarkdownToPlainText = (markdown: string): string => {
plainText = plainText.replace(/~~(.*?)~~/g, '$1');
plainText = plainText.replace(/<\/?[^>]+(>|$)/g, '');
plainText = plainText.replace(/\n{3,}/g, '\n\n');
plainText = plainText.replace(/@@CODEBLOCK(\d+)@@/g, (_match, index: string) => codeBlocks[Number(index)] ?? '');
return plainText.trim();
};