docs: unify documentation and streamline code comments

- Translated project documentation (LEARNING-JOURNEY, CONTRIBUTORS, AI_DISCLOSURE) to English for better accessibility.
- Standardized internal code documentation by converting XML-doc blocks to standard comment format.
- Cleaned up inline comments and removed redundant versioning metadata across the codebase.
- Refactored non-functional text elements to improve readability and maintain a consistent style.
This commit is contained in:
2026-05-11 00:52:15 +02:00
parent a37882893e
commit c4c85cf4b8
33 changed files with 666 additions and 1364 deletions
+66 -141
View File
@@ -52,10 +52,8 @@ public sealed class ChatLogWindow : Window
private int ActivatePos = -1;
internal string Chat = string.Empty;
// Hellion Chat — v0.6.0 input history was extracted into
// InputHistoryService so pop-out windows with their own ChatInputBar
// share the same Up/Down history with the main window. The cursor
// stays window-local because each window navigates independently.
// Input history extracted into InputHistoryService so pop-out windows share
// the same Up/Down history. Cursor stays window-local (independent navigation).
private int InputBacklogIdx = -1;
public bool TellSpecial;
private readonly Stopwatch LastResize = new();
@@ -74,11 +72,8 @@ public sealed class ChatLogWindow : Window
public Vector2 LastWindowPos { get; private set; } = Vector2.Zero;
public Vector2 LastWindowSize { get; private set; } = Vector2.Zero;
// Window position recovery: guards against off-screen positions after a
// display layout change (monitor disconnected, resolution changed). On
// the first draw after plugin load we run a one-shot bounds check to see
// whether the stored position still overlaps any visible viewport area.
// The manual reset button in the settings forces the position regardless.
// Guards against off-screen positions after a display layout change.
// One-shot bounds check on first draw; manual reset button bypasses it.
private bool DidOnLoadBoundsCheck;
internal bool RequestPositionReset { get; set; }
@@ -112,9 +107,7 @@ public sealed class ChatLogWindow : Window
IsOpen = true;
RespectCloseHotkey = false;
DisableWindowSounds = true;
// AllowBackgroundBlur wird nach AddWindow zentral in Plugin.Setup
// für alle registrierten Windows gesetzt — keine Per-Window-Logik
// hier nötig.
// AllowBackgroundBlur is set centrally in Plugin.Setup after AddWindow.
PayloadHandler = new PayloadHandler(this);
HandlerLender = new Lender<PayloadHandler>(() => new PayloadHandler(this));
@@ -122,10 +115,8 @@ public sealed class ChatLogWindow : Window
SetUpTextCommandChannels();
SetUpAllCommands();
// Cache the registered wrapper instances so Dispose can detach the same
// event objects the constructor attached to, without going through
// Register() again (which would re-create the wrapper if the command
// happened to be missing from the dictionary).
// Cache wrapper instances so Dispose can detach the same event objects
// without going through Register() again.
_clearHellionCommand = Plugin.Commands.Register(
"/clearhellion",
"Clear the Hellion Chat log"
@@ -397,11 +388,10 @@ public sealed class ChatLogWindow : Window
}
}
// Delegates to InputHistoryService so pop-out ChatInputBar instances share
// history. Deduplication lives inside the service.
private void AddBacklog(string message)
{
// v0.6.0 — delegates to the shared InputHistoryService so pop-out
// ChatInputBar instances see the same history. Move-to-newest
// deduplication lives inside the service.
InputHistoryService.Push(message);
}
@@ -417,15 +407,12 @@ public sealed class ChatLogWindow : Window
if (Plugin.Config.PreviewPosition is PreviewPosition.Inside)
height -= Plugin.InputPreview.PreviewHeight;
// Hellion Chat v0.6.1 — Header-Toolbar rendert auf Window-Ebene über
// einem horizontalen Layout-Pfad und wird von GetContentRegionAvail
// hier drin NICHT automatisch berücksichtigt, daher expliziter Abzug.
// Banner dagegen rendert in DrawChatLog VOR diesem ganzen Block und
// ImGui zieht seine Höhe automatisch von GetContentRegionAvail ab,
// weil der Cursor schon weiter unten steht — kein eigener Abzug.
// Header toolbar height is not subtracted by GetContentRegionAvail automatically
// (it renders outside the normal layout path), so we subtract it explicitly.
// The hint banner renders before this block so ImGui already accounts for it.
height -= ImGui.GetFrameHeightWithSpacing();
// v1.2.0 — Status-Bar am Window-Boden reserviert 22 px + 2 px Spacing.
// Status bar at the window bottom reserves 22px + 2px spacing.
height -= StatusBar.Height + 2;
return height;
@@ -659,10 +646,8 @@ public sealed class ChatLogWindow : Window
LastWindowSize = currentSize;
LastWindowPos = ImGui.GetWindowPos();
// Window position recovery. Manual reset takes precedence and snaps
// the window to the safe default unconditionally; the one-shot
// on-load check only fires when the persisted position has no
// overlap with any visible viewport area.
// Manual reset snaps unconditionally; on-load check only fires when the
// stored position has no overlap with any visible viewport.
if (RequestPositionReset)
{
RequestPositionReset = false;
@@ -684,11 +669,8 @@ public sealed class ChatLogWindow : Window
if (IsChatMode && Plugin.InputPreview.IsDrawable)
Plugin.InputPreview.CalculatePreview();
// Hellion Chat v0.6.1 — render the one-time hint banner first so it
// sits above the tab area / sidebar in full window width. ImGui's
// GetContentRegionAvail subtracts its height automatically because the
// cursor advances past it before the message log calls
// GetRemainingHeightForMessageLog, so we don't track the height here.
// Render the hint banner first so it sits above the tab area at full
// window width. ImGui accounts for its height automatically.
DrawV061HintBannerIfNeeded();
if (Plugin.Config.SidebarTabView)
@@ -713,8 +695,7 @@ public sealed class ChatLogWindow : Window
DrawChannelName(activeTab);
}
// v1.0.2 — compute inputColour up front so the channel selector button
// can also tint with it (existing input-text push remains below).
// inputColour computed up front so the channel selector button can share it.
var inputType = activeTab.CurrentChannel.UseTempChannel
? activeTab.CurrentChannel.TempChannel.ToChatType()
: activeTab.CurrentChannel.Channel.ToChatType();
@@ -1032,11 +1013,8 @@ public sealed class ChatLogWindow : Window
}
else
{
// We cannot lookup ExtraChat channel names from index over
// IPC so we just don't show the name if it's the tabs channel.
//
// We don't call channel.ToChatType().Name() as it has the
// long name as used in the settings window.
// ExtraChat channel names aren't available over IPC by index,
// so we skip the name lookup and show the short form instead.
channelNameChunks =
[
new TextChunk(
@@ -1122,8 +1100,8 @@ public sealed class ChatLogWindow : Window
Plugin.CurrentTab.CurrentChannel.TempTellTarget = null;
}
// Instead of calling SetChannel(), we ask the ExtraChat plugin to set a
// channel override by just calling the command directly.
// ExtraChat linkshell channel switch: call the prefix command through the
// game chat because ExtraChat only registers stub handlers in Dalamud.
if (channel.Value.IsExtraChatLinkshell())
{
// Check that the command is registered in Dalamud so the game code
@@ -1169,10 +1147,8 @@ public sealed class ChatLogWindow : Window
];
}
// v0.6.0 — pop-out windows route submission through this wrapper.
// The main-window Chat buffer is briefly used as a vehicle for
// SendChatBox (which reads it directly) and restored afterwards so
// the main window does not visibly lose any half-typed input.
// Pop-out windows route submission here. The main Chat buffer is briefly
// used as a vehicle for SendChatBox and restored afterwards.
internal void SendChatBoxFromExternal(Tab tab, string text)
{
var saved = Chat;
@@ -1217,7 +1193,7 @@ public sealed class ChatLogWindow : Window
?? activeTab.CurrentChannel.TellTarget;
if (target != null)
{
// ContentId 0 is a case where we can't directly send messages, so we send a /tell formatted message and let the game handle it
// ContentId 0: can't send directly, so format as /tell and let the game handle it.
if (target.ContentId == 0)
{
trimmed = $"/tell {target.ToTargetString()} {trimmed}";
@@ -1383,8 +1359,8 @@ public sealed class ChatLogWindow : Window
var maxLines = Plugin.Config.MaxLinesToRender;
var startLine = messages.Count > maxLines ? messages.Count - maxLines : 0;
// Card-mode pre-loop hoist: theme/drawList/winLeft/winRight/border
// are invariant per DrawMessages call; only cursorY moves per row.
// Card-mode pre-loop: theme/drawList/winLeft/winRight/border are invariant
// per DrawMessages call; only cursorY moves per row.
var theme = Plugin.ThemeRegistry.Active;
var drawList = ImGui.GetWindowDrawList();
var winLeft = ImGui.GetWindowPos().X;
@@ -1541,11 +1517,9 @@ public sealed class ChatLogWindow : Window
var lineWidth = ImGui.GetContentRegionAvail().X;
// v1.2.0 — Card-Rows als Default, Compact-Density als Opt-Out.
// Card-Mode: Sender-Header in Channel-Color auf eigener Zeile,
// dann Body, dann subtile Border-Bottom als Card-Trenner.
// Compact-Mode: bisheriges Verhalten — Sender + Space + Content
// auf einer Zeile via SameLine.
// v1.2.0 card mode: sender on its own line in channel color, then body,
// then a subtle border as a card separator.
// Compact mode: sender + space + content on one line via SameLine.
var useCard = !Plugin.Config.UseCompactDensity;
if (useCard)
{
@@ -1558,7 +1532,7 @@ public sealed class ChatLogWindow : Window
{
DrawChunks(message.Sender, true, handler, lineWidth);
}
// KEIN SameLine — Body landet auf eigener Zeile.
// No SameLine — body renders on its own line.
}
// We need to draw something otherwise the item visibility check below won't work.
@@ -1572,8 +1546,7 @@ public sealed class ChatLogWindow : Window
else
DrawChunks(message.Content, true, handler, lineWidth);
// Subtile Border-Bottom als Card-Trenner. Border-Farbe mit
// reduzierter Alpha (RGBA → 0x33) für dezente Trennung.
// Border bottom as card separator. Alpha reduced to 0x33 for subtlety.
{
var rowEndY = ImGui.GetCursorScreenPos().Y;
drawList.AddLine(
@@ -1646,9 +1619,8 @@ public sealed class ChatLogWindow : Window
if (!tabItem.Success)
continue;
// v1.2.0 — Active-Tab-Underline-Pill (2 px Akzent statt Background-Fill).
// Bewusst direkt nach TabItem-Setup; GetItemRectMin/Max referenziert noch
// das Tab. ImGui hat keine native Underline-API, daher direkter DrawList-Pass.
// Active-tab underline pill (2px accent). No native ImGui underline API,
// so we use a direct DrawList pass.
{
var theme = Plugin.ThemeRegistry.Active;
var min = ImGui.GetItemRectMin();
@@ -1680,7 +1652,7 @@ public sealed class ChatLogWindow : Window
private void DrawTabSidebar()
{
var currentTab = -1;
// v1.2.0 — Sidebar fix 44 px, kein Resize. Mehr Platz fürs Chat-Log.
// Sidebar fixed at 44px, no resize.
using var tabTable = ImRaii.Table(
"tabs-table",
2,
@@ -1696,28 +1668,19 @@ public sealed class ChatLogWindow : Window
var hasTabSwitched = false;
var childHeight = GetRemainingHeightForMessageLog();
// v1.2.0 — Sidebar-Child ohne Theme-ChildBg, sonst füllt das
// bläuliche Frame-Rect auch den oberen HeaderToolbar-Padding-Bereich
// aus (sieht aus wie ein angeschnittener Block oberhalb der Buttons).
// Vertikale Trennung zur Message-Spalte bleibt durch BordersInnerV
// der Tab-Table erhalten.
// Sidebar child without ChildBg tint to avoid a colored block above the
// header toolbar area. Vertical separation is handled by BordersInnerV.
using (ImRaii.PushColor(ImGuiCol.ChildBg, 0u))
using (var child = ImRaii.Child("##chat2-tab-sidebar", new Vector2(-1, childHeight)))
{
if (child)
{
// v1.2.0 — Top-Padding spiegelt die HeaderToolbar-Höhe der
// rechten Spalte (DrawChatHeaderToolbar wird dort als erstes
// gerendert, eine Frame-Zeile + ItemSpacing). Ohne diesen
// Padding würden die Sidebar-Buttons oben am Window-Top
// kleben, während die Messages erst unter der Toolbar
// beginnen — vertikales Mismatch.
// Top padding mirrors the HeaderToolbar height so sidebar buttons
// align with the message log start.
ImGui.Dummy(new Vector2(0, ImGui.GetFrameHeightWithSpacing()));
var previousTab = Plugin.CurrentTab;
// Hellion Chat — auto-tell-tabs section divider rendered
// exactly once before the first temp tab, with a live unit
// counter pulled directly from the tab list.
// Divider rendered once before the first temp tab with a live unit counter.
var tempTabHeaderRendered = false;
var tempTabCount = Plugin.Config.Tabs.Count(t => t.IsTempTab);
@@ -1752,11 +1715,8 @@ public sealed class ChatLogWindow : Window
if (showGreetedAffordance)
{
// Greeted toggle sits left of the selectable so the
// click areas stay separate. The icon also doubles
// as the visual "I'm done with this person" cue.
// Compact frame padding keeps the icon dezent next
// to the tab name instead of a chunky button block.
// Greeted toggle left of the selectable to keep click areas separate.
// Compact padding keeps the icon next to the tab name.
var greetedIcon = tab.IsGreeted
? FontAwesomeIcon.CheckCircle
: FontAwesomeIcon.Check;
@@ -1784,10 +1744,8 @@ public sealed class ChatLogWindow : Window
ImGui.SameLine();
}
// v1.2.0 — Icon-only Sidebar mit Tooltip beim Hover.
// Active-Tab kriegt Akzent-Color am Icon, Greeted-Tabs
// werden auf TextDim gedimmt (löst den alten Header-
// Dim-Trick ab, da wir keine Selectable mehr nutzen).
// Icon-only sidebar with tooltip on hover. Active tab gets accent color;
// greeted tabs are dimmed; tell tabs get a hash-based tint.
var theme = Plugin.ThemeRegistry.Active;
var icon = TabIconMapping.Resolve(tab);
uint iconColor;
@@ -1801,8 +1759,8 @@ public sealed class ChatLogWindow : Window
}
else if (tab.IsTempTab && tab.TellTarget != null && tab.TellTarget.IsSet())
{
// v1.2.0 — Hash-Color-Tint differenziert parallele Auto-Tell-Tabs
// visuell ohne dass User pro Tab manuell ein Custom-Icon setzen muss.
// Hash-based color tint differentiates parallel Auto-Tell tabs
// without requiring manual icon assignment per tab.
iconColor = TabTintCache.GetTint(tab);
}
else
@@ -1835,9 +1793,8 @@ public sealed class ChatLogWindow : Window
if (isCurrentTab)
{
// v1.2.0 — Vertikale Akzent-Pill an der linken Window-Kante.
// 3 px breit, halbe Tab-Höhe, vertikal zentriert. ImGui hat keine
// native Pill-API, daher direkter DrawList-Pass.
// Vertical accent pill on the left window edge, 3px wide, half tab height,
// vertically centered. Direct DrawList pass, no native ImGui API for this.
var min = ImGui.GetItemRectMin();
var max = ImGui.GetItemRectMax();
const float pillWidth = 3f;
@@ -1853,10 +1810,8 @@ public sealed class ChatLogWindow : Window
); // leichter Rounding
}
// v1.2.0 — Unread-Dot oben rechts am Icon. Sichtbar ohne Hover, damit
// User Tabs mit ungelesenen Messages sofort erkennt. Aktive Tabs haben
// per Konvention Unread = 0 (LastTab-Branch in ChatLogWindow), daher
// kollidiert der Dot nicht mit der Active-Pill.
// Unread dot top-right of the icon. Active tabs have Unread=0 by convention
// so the dot never conflicts with the active pill.
if (!isCurrentTab && tab.UnreadMode != UnreadMode.None && tab.Unread > 0)
{
var min = ImGui.GetItemRectMin();
@@ -1868,10 +1823,7 @@ public sealed class ChatLogWindow : Window
min.Y + dotRadius + dotPadding
);
// v1.2.0 — Sanfter Pulse-Effekt: Alpha schwankt zwischen 60% und
// 100% mit ~2-Sekunden-Cycle (subtil, nicht hektisch).
// Plugin.Config.ReduceMotion (Field seit v1.1.0) skipt den Pulse
// und rendert statisch — Default ist Animation an.
// Sin-based 2s pulse: alpha oscillates 60-100%. Skipped when ReduceMotion is on.
var dotColor = theme.Colors.StatusDanger;
if (!Plugin.Config.ReduceMotion)
{
@@ -1941,14 +1893,8 @@ public sealed class ChatLogWindow : Window
Plugin.WantedTab = null;
}
// Hellion Chat v0.6.1 — visible pop-out trigger right above the message
// log so users discover the feature without having to right-click the tab.
// Renders only for the active tab in the main ChatLogWindow; pop-out
// windows have their own render path and skip this toolbar.
//
// Hellion Chat v1.3.0 also renders the optional Honorific title slot
// left of the pop-out button, when HonorificService reports an active
// custom title and the user has ShowHonorificTitleInHeader enabled.
// DrawChatHeaderToolbar: renders the pop-out button for the active tab.
// v1.3.0 also renders the optional Honorific title slot left of it.
private void DrawChatHeaderToolbar(Tab tab)
{
DrawHonorificTitleSlot();
@@ -1973,16 +1919,9 @@ public sealed class ChatLogWindow : Window
}
}
// Renders the Honorific custom title to the left of the pop-out button,
// wrapped in guillemets to match how the game itself displays titles.
// We lay out the title first, then DrawPopOutButton uses
// GetContentRegionAvail to anchor itself flush right, which is why the
// call order in DrawChatHeaderToolbar matters: title first, button second.
//
// The slot stays on the same line as the pop-out button so the chat
// log doesn't lose vertical space; we use ImGui.SameLine after our
// text so the cursor X is still on the toolbar row when the pop-out
// button takes over.
// Title rendered first so DrawPopOutButton can anchor flush right via
// GetContentRegionAvail. Call order in DrawChatHeaderToolbar matters.
// SameLine keeps both on the same toolbar row.
private void DrawHonorificTitleSlot()
{
var service = Plugin.HonorificService;
@@ -2028,8 +1967,7 @@ public sealed class ChatLogWindow : Window
var theme = Plugin.ThemeRegistry.Active;
// Group so the tooltip's IsItemHovered check fires for hover anywhere
// on the crown-plus-title pair, not just one of the two.
// Group so IsItemHovered covers both the crown icon and the title text.
ImGui.BeginGroup();
using (ImRaii.PushColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(theme.Colors.TextMuted)))
using (Plugin.FontManager.FontAwesome.Push())
@@ -2051,11 +1989,7 @@ public sealed class ChatLogWindow : Window
ImGui.SameLine();
}
// Hellion Chat v0.6.1 — One-Time-Hint-Banner introducing the chat header
// pop-out toolbar button and the right-click pathway. Reuses the visual
// pattern from Popout.cs DrawHintBannerIfNeeded so users see a familiar
// dismiss-affordance. Returns the vertical space the banner consumed
// (0 when not shown) so the message log can shrink accordingly.
// One-time hint banner for the pop-out header button and right-click pathway.
private float DrawV061HintBannerIfNeeded()
{
if (Plugin.Config.SeenPopOutHeaderHint)
@@ -2070,10 +2004,7 @@ public sealed class ChatLogWindow : Window
var bg = new System.Numerics.Vector4(0.16f, 0.20f, 0.28f, 1f);
var dismiss = false;
var openSettings = false;
// RAII for the style stack so an early return in this block
// (or a later refactor that introduces one) can never leave the
// ImGui style stack unbalanced. Matches the convention used
// elsewhere in this file.
// RAII style stack so an early return can never leave ImGui unbalanced.
using (ImRaii.PushColor(ImGuiCol.ChildBg, bg))
using (ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 1f))
using (
@@ -2176,10 +2107,8 @@ public sealed class ChatLogWindow : Window
internal readonly List<bool> PopOutDocked = [];
internal readonly HashSet<Guid> PopOutWindows = [];
// v0.6.0 — live enumeration of all active Popout windows so the
// KeybindManager can find a focused ChatInputBar to forward tab-cycle
// keybinds to. Filter on IsOpen prevents touching closed-but-still-
// registered popouts.
// Live enumeration of active Popout windows for KeybindManager tab-cycle forwarding.
// Filters on IsOpen to skip closed-but-registered popouts.
internal IEnumerable<Popout> ActivePopouts =>
Plugin.WindowSystem.Windows.OfType<Popout>().Where(p => p.IsOpen);
@@ -2352,8 +2281,7 @@ public sealed class ChatLogWindow : Window
}
finally
{
// ImGuiListClipperPtr wraps an unmanaged ImGuiListClipper allocated above.
// Without Destroy() the unmanaged block leaks per autocomplete render.
// Destroy frees the unmanaged ImGuiListClipper allocated above; without it the block leaks per render.
clipper.Destroy();
}
}
@@ -2687,9 +2615,8 @@ public sealed class ChatLogWindow : Window
return $"Player {hashCode:X8}";
}
// Snap threshold in pixels: at least this much of the window must overlap
// a visible viewport so the user can still grab the first tab header.
// Below the threshold the window is considered off-screen.
// Snap threshold: minimum window overlap with a visible viewport before
// we consider it off-screen.
private const int OnScreenMinOverlapX = 100;
private const int OnScreenMinOverlapY = 40;
@@ -2725,9 +2652,7 @@ public sealed class ChatLogWindow : Window
$"[Window-Recovery] {source}: snapping main window from {LastWindowPos} (size {LastWindowSize}) to {safePos}."
);
// Pop-outs are intentionally non-persistent (cleared on plugin reload),
// so an off-screen pop-out can never survive a session boundary. The
// main window above is the only persistence target that needs an
// explicit recovery path.
// Pop-outs don't persist across sessions so they can never end up off-screen
// after a reload. Only the main window needs explicit recovery.
}
}