From bed6040f7c10652f777fa3672d9e0e4f8a9bdc20 Mon Sep 17 00:00:00 2001 From: HueByte Date: Thu, 19 Feb 2026 09:11:01 +0100 Subject: [PATCH] feat: Implement chat line wrapping and viewport resizing for message display --- src/EchoHub.Client/UI/ChatRenderer.cs | 52 ++++++++++++++++++++++++ src/EchoHub.Client/UI/MainWindow.cs | 31 ++++++++++++-- src/EchoHub.Client/UI/UserPanelDialog.cs | 3 +- 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/EchoHub.Client/UI/ChatRenderer.cs b/src/EchoHub.Client/UI/ChatRenderer.cs index 911c595..d1b1fbb 100644 --- a/src/EchoHub.Client/UI/ChatRenderer.cs +++ b/src/EchoHub.Client/UI/ChatRenderer.cs @@ -35,6 +35,58 @@ public partial class ChatLine public override string ToString() => string.Concat(Segments.Select(s => s.Text)); + /// + /// Wrap this line into multiple lines that fit within the given width. + /// Continuation lines are indented with the specified number of spaces. + /// + public List Wrap(int width, int continuationIndent = 0) + { + if (width <= 0 || TextLength <= width) + return [this]; + + var results = new List(); + var currentSegments = new List(); + int col = 0; + + foreach (var segment in Segments) + { + int segPos = 0; + while (segPos < segment.Text.Length) + { + int remaining = width - col; + if (remaining <= 0) + { + // Emit current line and start a new one + results.Add(new ChatLine(currentSegments)); + currentSegments = []; + + // Add indent for continuation + if (continuationIndent > 0) + { + currentSegments.Add(new ChatSegment(new string(' ', continuationIndent), null)); + col = continuationIndent; + } + else + { + col = 0; + } + + remaining = width - col; + } + + int take = Math.Min(segment.Text.Length - segPos, remaining); + currentSegments.Add(new ChatSegment(segment.Text.Substring(segPos, take), segment.Color)); + col += take; + segPos += take; + } + } + + if (currentSegments.Count > 0) + results.Add(new ChatLine(currentSegments)); + + return results; + } + /// /// Parse a string containing ANSI 24-bit color escape codes into colored segments. /// Format: \x1b[38;2;R;G;Bm (foreground color), \x1b[0m (reset) diff --git a/src/EchoHub.Client/UI/MainWindow.cs b/src/EchoHub.Client/UI/MainWindow.cs index 3cebdd3..a385f4b 100644 --- a/src/EchoHub.Client/UI/MainWindow.cs +++ b/src/EchoHub.Client/UI/MainWindow.cs @@ -45,6 +45,7 @@ public sealed class MainWindow : Runnable private readonly Dictionary _channelTopics = []; private string _currentChannel = string.Empty; private string _currentUser = string.Empty; + private int _lastChatWidth; /// /// Fired when the user selects a channel. Parameter is the channel name. @@ -192,6 +193,9 @@ public sealed class MainWindow : Runnable // Apply our custom color schemes to all views ApplyColorSchemes(); + // Re-wrap messages when the chat area is resized + _messageList.ViewportChanged += (_, _) => OnChatViewportChanged(); + // Window-level key handling for Ctrl+C (quit) KeyDown += OnWindowKeyDown; } @@ -368,6 +372,16 @@ public sealed class MainWindow : Runnable } } + private void OnChatViewportChanged() + { + var newWidth = _messageList.Viewport.Width; + if (newWidth > 0 && newWidth != _lastChatWidth) + { + _lastChatWidth = newWidth; + RefreshMessages(); + } + } + private void OnWindowKeyDown(object? sender, Key e) { // Ctrl+C quits from anywhere @@ -588,11 +602,22 @@ public sealed class MainWindow : Runnable { if (_channelMessages.TryGetValue(_currentChannel, out var messages)) { + var width = _messageList.Viewport.Width; var source = new ChatListSource(); - source.AddRange(messages); + + if (width > 0) + { + foreach (var line in messages) + source.AddRange(line.Wrap(width)); + } + else + { + source.AddRange(messages); + } + _messageList.Source = source; - if (messages.Count > 0) - _messageList.SelectedItem = messages.Count - 1; + if (source.Count > 0) + _messageList.SelectedItem = source.Count - 1; } else { diff --git a/src/EchoHub.Client/UI/UserPanelDialog.cs b/src/EchoHub.Client/UI/UserPanelDialog.cs index 0291063..b82e357 100644 --- a/src/EchoHub.Client/UI/UserPanelDialog.cs +++ b/src/EchoHub.Client/UI/UserPanelDialog.cs @@ -146,7 +146,8 @@ public sealed class UserPanelDialog Width = Dim.Fill(1), Height = 3, Text = bioText, - ReadOnly = true + ReadOnly = true, + WordWrap = true }; bioView.SetScheme(new Scheme {