diff --git a/apps/matrix/apps/web/src/lib/components/chat/Message.svelte b/apps/matrix/apps/web/src/lib/components/chat/Message.svelte index 5eaafc3f2..2bf503ba7 100644 --- a/apps/matrix/apps/web/src/lib/components/chat/Message.svelte +++ b/apps/matrix/apps/web/src/lib/components/chat/Message.svelte @@ -67,15 +67,43 @@ .replace(/'/g, '''); } - // Convert URLs to clickable links - function linkifyText(text: string, isOwn: boolean): string { + // Apply markdown formatting (bold, italic, code, strikethrough) + function applyMarkdown(text: string, isOwn: boolean): string { + const codeColor = isOwn ? 'bg-white/20 text-white' : 'bg-black/5 dark:bg-white/10'; + + // Inline code (backticks) - process first to avoid conflicts + text = text.replace(/`([^`]+)`/g, `$1`); + + // Bold (**text** or __text__) + text = text.replace(/\*\*([^*]+)\*\*/g, '$1'); + text = text.replace(/__([^_]+)__/g, '$1'); + + // Italic (*text* or _text_) - be careful not to match inside URLs + text = text.replace(/(?$1'); + text = text.replace(/(?$1'); + + // Strikethrough (~~text~~) + text = text.replace(/~~([^~]+)~~/g, '$1'); + + return text; + } + + // Convert URLs to clickable links and apply markdown + function formatMessageBody(text: string, isOwn: boolean): string { const escaped = escapeHtml(text); const linkClass = isOwn ? 'underline underline-offset-2 hover:opacity-80' : 'text-primary underline underline-offset-2 hover:opacity-80'; - return escaped.replace(urlRegex, (url) => { + + // First apply markdown + let formatted = applyMarkdown(escaped, isOwn); + + // Then linkify URLs + formatted = formatted.replace(urlRegex, (url) => { return `${url}`; }); + + return formatted; } // Extract first URL for preview @@ -403,7 +431,7 @@

{:else} -

{@html linkifyText(message.body, message.isOwn)}

+

{@html formatMessageBody(message.body, message.isOwn)}

{#if firstUrl()}