chore: comments, second pass -- the task codes the first pass missed

The first sweep matched a character class that swallowed the digit, so a bare
B1 slipped through while B1-2 was caught. Searching the whole A-Z space instead
of guessing prefixes turned up 130-odd more: B0 through B6, C2, C3, D1, H2, M6,
P7, P8, T2, W2, plus GP-04, KB-01, OD-1, PM-1, PM-3, SEC-01, TR-4, TR-7, UI-11,
UI-12, XC-8 and API-3.

Kept deliberately: 41 B4 01 is a byte signature, "N0" a format string,
#L119-L128 a source anchor, LS4/LS6 are linkshells, and A=FF B=0C G=41 R=C2
explains a colour-channel order. Those look like codes and are not.

Also translated the eight German comments left in the theme files and
ImGuiUtil. Seven of them described what a palette does to which channel, which
is worth reading -- just not in a second language in an otherwise English
codebase.
This commit is contained in:
2026-08-19 22:03:12 +02:00
parent 5b738e6885
commit 16557213cd
59 changed files with 262 additions and 193 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ using System.Collections.Generic;
namespace HellionChat.Ui.Components;
// B2 (PERF-B2): variable-height clip plan. ImGuiListClipper needs a constant
// Variable-height clip plan. ImGuiListClipper needs a constant
// row height, and since v1.10.0 neither density has one (compact rows wrap
// too), so both compute a plan from the cached per-row heights: a lead dummy
// for the rows above
+3 -3
View File
@@ -34,11 +34,11 @@ internal sealed class ChunkRenderer
// names change every plugin reload to avoid stable cross-session linkage.
_salt = new Random().Next().ToString();
// Not yet consumed in C2/C3; E-task wiring will likely add log call-sites later.
// No call sites yet; logging here will likely come later.
_ = _logger;
}
// B2-1/B2-2 render-observability: the formatted sender text the real draw
// render-observability: the formatted sender text the real draw
// path actually produced (post-ForDisplay). A SelfTest reads this after
// driving DrawChunks to prove the WorldSuffixMode/NameFormMode reformat
// reached the real render entry — never the helper in isolation. null until
@@ -52,7 +52,7 @@ internal sealed class ChunkRenderer
float lineWidth = 0f
)
{
// UI-7: render a copy with the sender name reformatted per the user's
// Render a copy with the sender name reformatted per the user's
// display options. Skipped in screenshot mode so the name-anonymising
// path in DrawChunk stays reliable (privacy wins). ForDisplay returns
// the list unchanged when nothing applies, so non-sender lists and the
+3 -3
View File
@@ -64,7 +64,7 @@ internal sealed class InputBar
private bool _wasInputTextHovered;
private bool? _isFocusedOverride; // Test-only; null = honour per-frame Draw() value.
// UI-11 plugin-disclosure arm-and-hold: holds the buffer that armed the
// plugin-disclosure arm-and-hold: holds the buffer that armed the
// disclosure warning. null = not armed. Compared by value so an edit
// re-arms and a resend on the identical buffer goes through. 1.5.6 parity
// (ChatInputBar 1d3b429:27).
@@ -196,7 +196,7 @@ internal sealed class InputBar
ImGui.SameLine();
DrawQuickButtons();
// UI-11: yellow inline warning while a plugin-only-glyph message is
// Yellow inline warning while a plugin-only-glyph message is
// armed-and-held (buffer unchanged since it armed). Renders on its own
// line below the input row. 1.5.6 parity (ChatInputBar 1d3b429:93-103).
if (
@@ -589,7 +589,7 @@ internal sealed class InputBar
if (string.IsNullOrEmpty(text))
return;
// UI-11: plugin-disclosure arm-and-hold. Arm + scan on the RAW
// Plugin-disclosure arm-and-hold. Arm + scan on the RAW
// _pendingMessage (NOT the trimmed `text`) so the Draw warning gate
// (_pendingMessage == _disclosureArmedBuffer) matches byte-for-byte even
// when the buffer has leading/trailing whitespace. 1.5.6 armed/held/
+10 -10
View File
@@ -18,13 +18,13 @@ internal sealed class MessageList
private PayloadHandler? _handler;
// B3-5: scroll-to-bottom state. Per-instance, so pop-out windows (own
// Scroll-to-bottom state. Per-instance, so pop-out windows (own
// MessageList instance, PluginHostFactory.cs:263-266) isolate automatically —
// the old 1.5.6 updateScrollState flag is NOT needed here.
private bool _scrolledUp;
private bool _scrollToBottomRequested;
// B2: the height cache is only valid while these inputs are unchanged.
// The height cache is only valid while these inputs are unchanged.
// FontManager's own fingerprint covers font sizes only, not density / the two
// name-display modes / width — a stale height would misplace the clipper dummies.
// Per tab, not per list: the old single field let a width change in tab A mark
@@ -73,12 +73,12 @@ internal sealed class MessageList
return snap;
}
// SelfTest hook (B3-5 reset-invariant, REQUIRED — not optional). Lets
// SelfTest hook (reset-invariant, REQUIRED — not optional). Lets
// ScrollSnapDecisionStep flip the request flag without a real click, so the
// post-snap reset can be asserted; without it only the OR branch is testable.
internal void RequestScrollToBottomForSelfTest() => _scrollToBottomRequested = true;
// SelfTest hook (B2): runs the real planner against a caller fixture so the
// SelfTest hook: runs the real planner against a caller fixture so the
// step asserts the plan without a live scroll child (GetScrollY is garbage headless).
internal CardClipPlan PlanCardClipForSelfTest(
IReadOnlyList<float> heights,
@@ -86,7 +86,7 @@ internal sealed class MessageList
float viewportHeight
) => CardClipPlanner.Plan(heights, scrollY, viewportHeight);
// SelfTest hook (B2): drives the live invalidation, returns the tab's remaining
// SelfTest hook: drives the live invalidation, returns the tab's remaining
// cached-height count so the step can assert the drop. nowMs is a parameter so
// the step can step past the settle window without sleeping (v1.10.0).
internal int RunHeightCacheInvalidationForSelfTest(Tab tab, float contentWidth, long nowMs)
@@ -165,7 +165,7 @@ internal sealed class MessageList
MeasureTimestampColumn(tab);
// B2: drop stale cached heights before the snapshot draw. Both densities
// Drop stale cached heights before the snapshot draw. Both densities
// need this now -- compact rows are not constant height either, they wrap.
// Width read here while it is valid.
InvalidateHeightCacheIfLayoutChanged(
@@ -187,7 +187,7 @@ internal sealed class MessageList
var frozen = _fingerprintGates[tab.Identifier].IsPending;
DrawRows(tab, messages, compact ? _drawCompactRow : _drawCardRow, frozen);
// B3-5: scroll values are frame-constant inside the child, so this
// Scroll values are frame-constant inside the child, so this
// reflects the current frame's state wherever it runs; kept after the
// render to mirror the 1.5.6 end-of-DrawMessageLog placement.
_scrolledUp = ImGui.GetScrollMaxY() - ImGui.GetScrollY() > 1f;
@@ -282,7 +282,7 @@ internal sealed class MessageList
ImGui.SetCursorPos(origin with { X = origin.X + _stampColumnWidth });
}
// B3-5: Discord-style full-width bar pinned to the bottom edge of the
// Discord-style full-width bar pinned to the bottom edge of the
// visible region while the user is scrolled up. Geometry comes from window
// pos + size (visible region), never from the content flow: when scrolled
// up the visible bottom sits above the content bottom, so the
@@ -335,7 +335,7 @@ internal sealed class MessageList
private void DrawCompactRow(Message message, string? previousStamp)
{
// B2-1/B2-2: render the sender through DrawChunks (the name-aware path
// Render the sender through DrawChunks (the name-aware path
// that applies WorldSuffixMode/NameFormMode via ForDisplay), not as a
// flat SenderSource.TextValue string. message.Sender already carries the
// channel brackets/colon as ChunkSource.None wrappers (MessageManager
@@ -566,7 +566,7 @@ internal sealed class MessageList
private void DrawCardRow(Message message, string? previousStamp)
{
// B2-1/B2-2: sender via DrawChunks (name-aware path), on its own line
// Sender via DrawChunks (name-aware path), on its own line
// with content below — 1.5.6 card parity (ChatLogWindow.cs:1913, no
// SameLine after the sender). The 1.5.6 channel-colour push on the
// sender is deferred styling polish (deferred to v1.9.0); plain
@@ -133,7 +133,7 @@ internal sealed class AboutTab
LastHonorificStatusKey = kind.ToString();
var colors = _themes.Active.Colors;
// Null-safety via the `is { } v` pattern, never `.Value` raw (spec SEC-2):
// Null-safety via the `is { } v` pattern, never `.Value` raw :
// the version is bound only on the arms that have it; the impossible
// Detected/Incompatible-without-version state falls through to default.
switch (kind)
@@ -29,7 +29,7 @@ internal sealed class ThemePicker
(HellionStrings.Settings_Theme_Category_Retro, new[] { "synthwave-sunset" }, false),
];
// T2 ThemePickerCategoryStep diffs this against ThemeRegistry.BuiltinSlugs
// ThemePickerCategoryStep diffs this against ThemeRegistry.BuiltinSlugs
// to enforce coverage. Kept on the static map so the test does not pierce instance state.
internal static IEnumerable<string> CategoryMapSlugs => CategoryMap.SelectMany(c => c.Slugs);
+6 -6
View File
@@ -22,7 +22,7 @@ internal sealed class Sidebar
{
public const float IconOnlyWidth = 38f;
// B1-3a: expanded sidebar width is user-configurable (Config.SidebarWidth),
// Expanded sidebar width is user-configurable (Config.SidebarWidth),
// clamped to these bounds (matches the ChannelsTab slider range). Replaces
// the old fixed 150px ExpandedWidth constant.
public const float MinSidebarWidth = 40f;
@@ -38,7 +38,7 @@ internal sealed class Sidebar
// previously active one -- that row was still active when it was painted.
internal int LastRenderedActiveSurfaceCount { get; private set; }
// B3-2 render observability: counts greeted glyphs actually drawn this frame.
// render observability: counts greeted glyphs actually drawn this frame.
// Incremented ONLY in the real glyph branch in DrawRow; reset at Draw start.
// The SelfTest reads it after driving the real Draw — no dead service roundtrip.
internal int LastRenderedGreetedGlyphCount;
@@ -48,7 +48,7 @@ internal sealed class Sidebar
// beside it.
private const float PinGlyphScale = 0.6f;
// B3-4 render observability: section headers actually drawn this frame.
// render observability: section headers actually drawn this frame.
// Incremented only in the real header branch; reset at Draw start.
internal int LastDrawnSectionHeaderCount;
@@ -149,7 +149,7 @@ internal sealed class Sidebar
var dimAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextDim);
var dl = ImGui.GetWindowDrawList();
// B3-4 sectioned render order (1.5.6 parity): persistent → pinned
// sectioned render order (1.5.6 parity): persistent → pinned
// TempTabs → unpinned TempTabs. Only the display sequence regroups;
// the tab list itself stays untouched and every row keeps its
// ORIGINAL list index for PushID, so an open context-menu popup
@@ -250,7 +250,7 @@ internal sealed class Sidebar
// Only split off a separate pop-out hit area when there's room for
// both buttons. Below that, the whole row stays as a single
// selectable strip without the pop-out affordance.
// A3: gate the pop-out affordance on the expanded sidebar too. In
// Gate the pop-out affordance on the expanded sidebar too. In
// icon-only mode avail still clears the width threshold, which used to
// paint the pop-out glyph over the tab icon. The row stays a single
// selectable strip when collapsed; right-click pop-out is unaffected.
@@ -476,7 +476,7 @@ internal sealed class Sidebar
// The hit area sits at the LEFT edge of the row, but the item must
// be submitted AFTER TabContextMenu.Draw — any interactive item
// between the row button and the popup call would steal the
// right-click trigger (B3-1 ordering constraint).
// right-click trigger (ordering constraint).
ImGui.SetCursorScreenPos(origin);
// CheckCircle = greeted, plain Check = still pending (1.5.6 mapping).
+1 -1
View File
@@ -82,7 +82,7 @@ internal static class TabContextMenu
ClearPendingRename();
}
// Per-tab notification sound (B3-3). The checkbox gates the picker so
// Per-tab notification sound. The checkbox gates the picker so
// tabs that never want a sound keep the popup short.
if (
ImGui.Checkbox(
+2 -2
View File
@@ -36,7 +36,7 @@ public class DbViewer : Window
private int CurrentPage = 1;
private string SimpleSearchTerm = "";
// v1.4.8 H2: opt-in full-text search across the whole DB via FTS5.
// v1.4.8: opt-in full-text search across the whole DB via FTS5.
// Transient UI state (per-session), not persisted -- users opt in fresh
// every time so they always see the page-filter as the default mode.
private bool UseFullTextSearch;
@@ -232,7 +232,7 @@ public class DbViewer : Window
tooltipRight: Language.Page_ArrowRight_Tooltip
);
// Full-text search toggle (v1.4.8 H2). IsFtsIndexBuilt is a cached
// Full-text search toggle (v1.4.8). IsFtsIndexBuilt is a cached
// volatile bool in MessageStore -- single field read per frame, no
// SELECT count(*). ImRaii.Disabled blocks any click while the index
// is still being built, so no defensive force-off branch needed
+1 -1
View File
@@ -168,7 +168,7 @@ internal sealed class InputPreview : Window
{
ImGui.TextUnformatted(Language.Options_Preview_Header);
// Primary path (A2) resets the Lender counter in MainWindow.Draw();
// Primary path resets the Lender counter in MainWindow.Draw();
// this fallback covers the edge-case where MainWindow is closed but
// InputPreview is still open, preventing handler pool growth.
if (!_mainWindow.IsOpen)
+1 -1
View File
@@ -44,7 +44,7 @@ internal sealed class ChannelPopoutPool
// A popped tab gets its own input bar, so strip stale tell state first —
// otherwise a popped-out stale-tell tab would be a send surface that
// bypasses the click-path activation strip. Previous = the main window's
// active tab; popping the active tab itself must not strip (TR-4 guard).
// active tab; popping the active tab itself must not strip (guard).
TabLifecycleHelpers.OnTabActivated(tab, Plugin.Instance.MainWindow?.ActiveTab);
var slot = _slots.TryReserve(tab.Identifier);
@@ -103,7 +103,7 @@ internal sealed class ChannelPopoutWindow : Window, IFocusableChatWindow
}
// IFocusableChatWindow — this pop-out's own InputBar carries the focus state
// the keybind tail checks when deciding whether to route at this surface (C3).
// the keybind tail checks when deciding whether to route at this surface.
public bool HasFocusedInput => _input.IsFocused;
// Arm-and-hold the one-frame Activate flag; the pop-out's Draw applies the
+3 -3
View File
@@ -82,14 +82,14 @@ internal sealed class MainWindow : Window, IFocusableChatWindow
RespectCloseHotkey = false;
}
// UI-12: per-window focus-dependent opacity. ResolveBgAlpha stays guard-free
// Per-window focus-dependent opacity. ResolveBgAlpha stays guard-free
// and pure so the self-test can drive it directly; PreDraw owns the guard +
// wiring. 1.5.6 parity (focused → WindowOpacity, unfocused →
// WindowOpacityInactive, ChatLogWindow.PreOpenCheck 1d3b429:724).
internal float ResolveBgAlpha(bool isFocused) =>
isFocused ? Plugin.Config.WindowOpacity : Plugin.Config.WindowOpacityInactive;
// B1-2 / P7: rebuild flags from a fresh base every frame so toggling
// Rebuild flags from a fresh base every frame so toggling
// CanMove/CanResize/ShowTitleBar back on actually CLEARS NoMove/NoResize/
// NoTitleBar (not accumulating). Move/resize/title-bar logic as 1.5.6
// (ChatLogWindow.PreOpenCheck 1d3b429:703-710); base flags = today's
@@ -240,7 +240,7 @@ internal sealed class MainWindow : Window, IFocusableChatWindow
}
// IFocusableChatWindow — the keybind tail resolves which surface owns the
// input focus before routing a channel-set/REPLY/prefill at it (C3).
// input focus before routing a channel-set/REPLY/prefill at it.
public bool HasFocusedInput => _input.IsFocused;
// Arm-and-hold: field writes only, safe from the framework thread; the draw
+1 -1
View File
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace HellionChat.Ui.Windows;
// `internal` to match the Plugin.SettingsWindow property in W2; `public` here
// `internal` to match the Plugin.SettingsWindow property; `public` here
// would raise CS0053 against the internal members. Matches MainWindow shape.
internal sealed class SettingsWindow : Window
{