diff --git a/src/EchoHub.Client/UI/ChatRenderer.cs b/src/EchoHub.Client/UI/ChatRenderer.cs
index d229ca6..b7b5f95 100644
--- a/src/EchoHub.Client/UI/ChatRenderer.cs
+++ b/src/EchoHub.Client/UI/ChatRenderer.cs
@@ -453,11 +453,40 @@ public class UserListSource : IListDataSource
///
/// Shared color attributes for chat rendering (timestamps, system messages).
///
-public static class ChatColors
+public static partial class ChatColors
{
public static readonly Attribute TimestampAttr = new(Color.DarkGray, Color.Black);
public static readonly Attribute SystemAttr = new(new Color(0, 180, 180), Color.Black);
public static readonly Attribute MentionHighlightAttr = new(Color.White, new Color(80, 40, 0));
+ public static readonly Attribute MentionTextAttr = new(new Color(255, 180, 50), Color.Black);
+
+ ///
+ /// Split text around @mentions, giving each @word the MentionTextAttr accent color.
+ /// Non-mention text uses the provided default color.
+ ///
+ public static List SplitMentions(string text, Attribute? defaultColor = null)
+ {
+ var segments = new List();
+ int lastIndex = 0;
+
+ foreach (Match match in MentionRegex().Matches(text))
+ {
+ if (match.Index > lastIndex)
+ segments.Add(new ChatSegment(text[lastIndex..match.Index], defaultColor));
+
+ segments.Add(new ChatSegment(match.Value, MentionTextAttr));
+ lastIndex = match.Index + match.Length;
+ }
+
+ if (lastIndex < text.Length)
+ segments.Add(new ChatSegment(text[lastIndex..], defaultColor));
+
+ return segments;
+ }
+
+ // Matches @username (letters, digits, underscores, hyphens — same as channel name chars)
+ [GeneratedRegex(@"@[\w-]+")]
+ private static partial Regex MentionRegex();
}
///
diff --git a/src/EchoHub.Client/UI/MainWindow.cs b/src/EchoHub.Client/UI/MainWindow.cs
index 5f94915..7bbf3bd 100644
--- a/src/EchoHub.Client/UI/MainWindow.cs
+++ b/src/EchoHub.Client/UI/MainWindow.cs
@@ -872,12 +872,13 @@ public sealed class MainWindow : Runnable
default:
var contentLines = message.Content.Split('\n');
var firstLine = contentLines[0].TrimEnd('\r');
- lines.Add(BuildChatLine(time, senderName, senderColor, $" {firstLine}"));
+ lines.Add(BuildChatLineWithMentions(time, senderName, senderColor, $" {firstLine}"));
// Continuation lines indented to align with first line's content
var indent = new string(' ', $"[{time}] {senderName} ".Length);
for (int i = 1; i < contentLines.Length; i++)
{
- lines.Add(new ChatLine($"{indent}{contentLines[i].TrimEnd('\r')}"));
+ var contText = $"{indent}{contentLines[i].TrimEnd('\r')}";
+ lines.Add(new ChatLine(ChatColors.SplitMentions(contText)));
}
break;
}
@@ -913,4 +914,18 @@ public sealed class MainWindow : Runnable
};
return new ChatLine(segments);
}
+
+ ///
+ /// Build a chat line with @mention highlighting in the suffix text.
+ ///
+ private static ChatLine BuildChatLineWithMentions(string time, string senderName, Attribute? senderColor, string suffix)
+ {
+ var segments = new List
+ {
+ new($"[{time}] ", ChatColors.TimestampAttr),
+ new(senderName, senderColor),
+ };
+ segments.AddRange(ChatColors.SplitMentions(suffix));
+ return new ChatLine(segments);
+ }
}