diff --git a/HellionChat/PluginHostFactory.cs b/HellionChat/PluginHostFactory.cs index cdf47e5..f74f191 100644 --- a/HellionChat/PluginHostFactory.cs +++ b/HellionChat/PluginHostFactory.cs @@ -132,7 +132,8 @@ internal static class PluginHostFactory services.AddSingleton(sp => new Ui.Components.MessageList( sp.GetRequiredService(), sp.GetRequiredService(), - sp.GetRequiredService() + sp.GetRequiredService(), + sp.GetRequiredService() )); services.AddSingleton(_ => new Ui.Components.SymbolPicker()); services.AddSingleton(sp => new Ui.Components.InputBar( diff --git a/HellionChat/Ui/Components/MessageList.cs b/HellionChat/Ui/Components/MessageList.cs index 7bb09b2..965d50a 100644 --- a/HellionChat/Ui/Components/MessageList.cs +++ b/HellionChat/Ui/Components/MessageList.cs @@ -12,8 +12,7 @@ namespace HellionChat.Ui.Components; // rows have a constant line height; card mode falls back to a linear // render with a per-message height cache and an IsItemVisible skip path // so off-screen rows place a Dummy of the cached height rather than -// running the full render again. Text-only rendering for now — full -// chunk/payload rendering re-attaches in a later cycle. +// running the full render again. internal sealed class MessageList { private const float CompactRowHeight = 18f; @@ -21,12 +20,28 @@ internal sealed class MessageList private readonly ThemeRegistry _themes; private readonly TokenResolver _resolver; private readonly FontManager _fonts; + private readonly ChunkRenderer _chunkRenderer; - public MessageList(ThemeRegistry themes, TokenResolver resolver, FontManager fonts) + private PayloadHandler? _handler; + + // §6.2: setter-injection breaks the PayloadHandler → MainWindow → MessageList → PayloadHandler 3-cycle. + // Wired by PayloadHandlerInitHostedService.StartAsync after both singletons exist. + internal void AttachPayloadHandler(PayloadHandler handler) + { + _handler = handler; + } + + public MessageList( + ThemeRegistry themes, + TokenResolver resolver, + FontManager fonts, + ChunkRenderer chunkRenderer + ) { _themes = themes; _resolver = resolver; _fonts = fonts; + _chunkRenderer = chunkRenderer; } public void Draw(Tab tab) @@ -40,10 +55,6 @@ internal sealed class MessageList // No own ImRaii.Child here — MainWindow already wraps the message // area in one. Nesting would give the window two stacked scrolls // and a runaway content-height computation. - var theme = _themes.Active; - var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary); - var mutedAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextMuted); - using var messages = tab.Messages.GetReadOnly(3); var compact = Plugin.Config.UseCompactDensity; @@ -53,15 +64,15 @@ internal sealed class MessageList var pinnedToBottom = ImGui.GetScrollY() >= ImGui.GetScrollMaxY() - 1f; if (compact) - DrawCompact(messages, textAbgr, mutedAbgr); + DrawCompact(messages); else - DrawCard(tab, messages, textAbgr, mutedAbgr); + DrawCard(tab, messages); if (pinnedToBottom) ImGui.SetScrollHereY(1f); } - private void DrawCompact(IReadOnlyList messages, uint textAbgr, uint mutedAbgr) + private void DrawCompact(IReadOnlyList messages) { unsafe { @@ -72,7 +83,7 @@ internal sealed class MessageList while (clipper.Step()) { for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - DrawCompactRow(messages[i], textAbgr, mutedAbgr); + DrawCompactRow(messages[i]); } clipper.End(); } @@ -83,18 +94,18 @@ internal sealed class MessageList } } - private void DrawCompactRow(Message message, uint textAbgr, uint mutedAbgr) + private void DrawCompactRow(Message message) { var timestamp = FormatTimestamp(message.Date); var sender = message.SenderSource.TextValue; - var content = message.ContentSource.TextValue; - var line = string.IsNullOrEmpty(sender) - ? $"{timestamp} {content}" - : $"{timestamp} {sender}: {content}"; - ImGui.TextUnformatted(line); + ImGui.TextUnformatted( + string.IsNullOrEmpty(sender) ? timestamp : $"{timestamp} {sender}: " + ); + ImGui.SameLine(); + _chunkRenderer.DrawChunks(message.Content, wrap: true, handler: _handler, lineWidth: 0f); } - private void DrawCard(Tab tab, IReadOnlyList messages, uint textAbgr, uint mutedAbgr) + private void DrawCard(Tab tab, IReadOnlyList messages) { var tabId = tab.Identifier; for (var i = 0; i < messages.Count; i++) @@ -127,11 +138,8 @@ internal sealed class MessageList { var timestamp = FormatTimestamp(message.Date); var sender = message.SenderSource.TextValue; - var content = message.ContentSource.TextValue; ImGui.TextUnformatted(string.IsNullOrEmpty(sender) ? timestamp : $"{timestamp} {sender}"); - ImGui.PushTextWrapPos(0f); - ImGui.TextUnformatted(content); - ImGui.PopTextWrapPos(); + _chunkRenderer.DrawChunks(message.Content, wrap: true, handler: _handler, lineWidth: 0f); } private static string FormatTimestamp(DateTimeOffset date)