diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.test.ts b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.test.ts
new file mode 100644
index 00000000000..5279ee865a6
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.test.ts
@@ -0,0 +1,29 @@
+import { useMarkdown } from './useMarkdown';
+
+describe('useMarkdown', () => {
+ const { renderMarkdown } = useMarkdown();
+
+ it('should not add a trailing newline inside a fenced code block', () => {
+ const html = renderMarkdown('```\nhttps://example.com/webhook/abc\n```');
+
+ expect(html).toContain('https://example.com/webhook/abc');
+ });
+
+ it('should keep newlines between lines of a fenced code block', () => {
+ const html = renderMarkdown('```\nline 1\nline 2\n```');
+
+ expect(html).toContain('line 1\nline 2');
+ });
+
+ it('should keep the language class on a fenced code block', () => {
+ const html = renderMarkdown('```js\nconst a = 1;\n```');
+
+ expect(html).toContain('const a = 1;');
+ });
+
+ it('should keep an intentional blank last line in a fenced code block', () => {
+ const html = renderMarkdown('```\nline 1\n\n```');
+
+ expect(html).toContain('line 1\n');
+ });
+});
diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.ts b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.ts
index c123f8aa733..4ac7b07c1e0 100644
--- a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.ts
+++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/useMarkdown.ts
@@ -17,6 +17,15 @@ export function useMarkdown() {
},
});
+ // markdown-it ends fenced code with a newline. Triple-click copy would include it.
+ const renderFence = md.renderer.rules.fence;
+ md.renderer.rules.fence = (tokens, idx, options, env, self) => {
+ tokens[idx].content = tokens[idx].content.replace(/\n$/, '');
+ return renderFence
+ ? renderFence(tokens, idx, options, env, self)
+ : self.renderToken(tokens, idx, options);
+ };
+
function renderMarkdown(content: string) {
try {
return md.render(content);