From 246eb2b0bb25cd8b9aa74677dd18c73811d10337 Mon Sep 17 00:00:00 2001 From: HueByte Date: Thu, 19 Feb 2026 18:59:12 +0100 Subject: [PATCH] feat: add @mention highlighting in chat lines with new SplitMentions method --- src/EchoHub.Client/UI/ChatRenderer.cs | 31 ++++++++++++++++++++++++++- src/EchoHub.Client/UI/MainWindow.cs | 19 ++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) 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); + } }