chore: comments say what the code does, not which task produced it

A comment that reads "MUST stay in lockstep with TryGetActiveCrossfade (K8)"
helps nobody outside the plan that used to have a K8 in it, and the plans are
not in this repo. Same for "Spec FR-4", "plan §B.2", "Sub-Task 4.4" and the
F/R/M/A/S round codes scattered through the style engine and the self-tests.

Personal names go too. "tester feedback from Jin (v1.4.7)" and "Flo decision
2026-06-15" carry the reason fine without naming anyone -- the version and the
reason are the parts a reader can act on, and a public repo should not need a
cast list to be read.

The rule applied throughout: keep the why, drop the reference. Version numbers
stay, since those resolve through the changelog. 77 files.

ChunkUtil also carried 281 lines of commented-out code -- an older ToChunks
variant and two helpers with no callers, inherited and never removed. Deleted;
git remembers them.
This commit is contained in:
2026-08-19 21:50:31 +02:00
parent 52237fda7e
commit 5b738e6885
48 changed files with 113 additions and 397 deletions
+2 -2
View File
@@ -58,8 +58,8 @@ internal sealed class AutoTellTabsService : IDisposable
// Derived from the tab list on read. Pin/Unpin/Promote/Logout simply // Derived from the tab list on read. Pin/Unpin/Promote/Logout simply
// mutate IsPinned or remove tabs — the count adapts automatically. // mutate IsPinned or remove tabs — the count adapts automatically.
// Replaces the F2.1 Interlocked counter because the new pin-state // Replaces an Interlocked counter: the pin-state transitions are cold-path
// transitions are cold-path and don't need lock-free reads. // and don't need lock-free reads.
internal int ActiveTempTabCount => internal int ActiveTempTabCount =>
Plugin.Config.Tabs.Count(TabLifecycleHelpers.IsInUnpinnedPool); Plugin.Config.Tabs.Count(TabLifecycleHelpers.IsInUnpinnedPool);
+5 -5
View File
@@ -77,7 +77,7 @@ public class Configuration : IPluginConfiguration
.PrivacyDefaults .PrivacyDefaults
.DefaultPersistUnknownChannels; .DefaultPersistUnknownChannels;
// F3.2: dedup unknown-ChatType warnings so a chatty filter doesn't spam // Dedup unknown-ChatType warnings so a chatty filter doesn't spam
// the log every frame. NonSerialized so the warning fires once per // the log every frame. NonSerialized so the warning fires once per
// runtime, not once-ever-per-install. // runtime, not once-ever-per-install.
[NonSerialized] [NonSerialized]
@@ -107,7 +107,7 @@ public class Configuration : IPluginConfiguration
var known = Enum.IsDefined(typeof(ChatType), type); var known = Enum.IsDefined(typeof(ChatType), type);
// F3.2: log first occurrence of a ChatType the running build doesn't // Log the first occurrence of a ChatType the running build doesn't
// recognise — i.e. one a future FFXIV patch may have added. // recognise — i.e. one a future FFXIV patch may have added.
if (!known && !listed && _warnedUnknownChannels.Add(type)) if (!known && !listed && _warnedUnknownChannels.Add(type))
{ {
@@ -402,9 +402,9 @@ public class Tab
public bool IsTempTab; public bool IsTempTab;
// Pinned TempTabs survive plugin reload and logout — tester feedback from // Pinned TempTabs survive plugin reload and logout -- tester feedback in
// Jin (v1.4.7). Pinned tabs live in their own pool (MaxPinnedTempTabs) // v1.4.7. Pinned tabs live in their own pool (MaxPinnedTempTabs) separate
// separate from the AutoTellTabsLimit bucket. // from the AutoTellTabsLimit bucket.
public bool IsPinned; public bool IsPinned;
public bool AllSenderMessages; public bool AllSenderMessages;
public TellTarget TellTarget = TellTarget.Empty(); public TellTarget TellTarget = TellTarget.Empty();
+5 -5
View File
@@ -234,7 +234,7 @@ internal sealed unsafe class Chat : IDisposable
// Seed the just-typed character into our input field and focus it, the // Seed the just-typed character into our input field and focus it, the
// same InputBar.AppendPending + Activate prefill path inventory item-links // same InputBar.AppendPending + Activate prefill path inventory item-links
// use. Prefill-only — no tab switch (Flo decision 2026-06-15). // use. Prefill only, deliberately: no tab switch.
if (input != null) if (input != null)
{ {
Plugin.InputBar.AppendPending(input); Plugin.InputBar.AppendPending(input);
@@ -355,9 +355,9 @@ internal sealed unsafe class Chat : IDisposable
if (playerName != null) if (playerName != null)
{ {
// Right-click -> Send Tell: prefill our input the same way our own // Right-click -> Send Tell: prefill our input the same way our own
// "Send Tell" payload menu does (PayloadHandler), then focus. Prefill- // "Send Tell" payload menu does (PayloadHandler), then focus. Prefill
// only — no tab switch, no ChatActivatedArgs revival (Flo decision // only, deliberately: no tab switch, no ChatActivatedArgs revival.
// 2026-06-15). The game supplies worldName here, so no sheet lookup. // The game supplies worldName here, so no sheet lookup.
PrefillTellInput( PrefillTellInput(
playerName->ToString(), playerName->ToString(),
worldName != null ? worldName->ToString() : null worldName != null ? worldName->ToString() : null
@@ -393,7 +393,7 @@ internal sealed unsafe class Chat : IDisposable
{ {
// In-foray right-click -> Send Tell: same prefill path as the non-foray // In-foray right-click -> Send Tell: same prefill path as the non-foray
// tell. The foray-specific TellSpecial channel routing stays deferred // tell. The foray-specific TellSpecial channel routing stays deferred
// (v1.8.1, SetEurekaTellChannel) — prefill-only here (Flo decision 2026-06-15). // (v1.8.1, SetEurekaTellChannel) -- prefill only here as well.
PrefillTellInput( PrefillTellInput(
playerName->ToString(), playerName->ToString(),
worldName != null ? worldName->ToString() : null worldName != null ? worldName->ToString() : null
+1 -1
View File
@@ -530,7 +530,7 @@ internal unsafe class KeybindManager : IDisposable
{ {
// Direct channel-switch binds (CMD_SAY/PARTY/numbered linkshells/…): switch // Direct channel-switch binds (CMD_SAY/PARTY/numbered linkshells/…): switch
// the game channel AND mirror it onto the resolved tab so the input pill // the game channel AND mirror it onto the resolved tab so the input pill
// shows the real send target (pill-sync, Flo decision 2026-06-15). // shows the real send target (pill-sync).
Plugin.Instance.Functions.Chat.SetChannel(channel); Plugin.Instance.Functions.Chat.SetChannel(channel);
// Only mirror onto the tab when the game actually accepted the switch — an // Only mirror onto the tab when the game actually accepted the switch — an
// empty linkshell slot leaves the game channel untouched, so the pill must // empty linkshell slot leaves the game channel untouched, so the pill must
@@ -135,7 +135,7 @@ internal sealed class PayloadHandlerInitHostedService(
{ {
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{ {
// §6.2 cycle-resolution: both singletons exist by the time HostedServices // Cycle resolution: both singletons exist by the time HostedServices
// run, so this is the first safe point to wire the setter. // run, so this is the first safe point to wire the setter.
messageList.AttachPayloadHandler(payloadHandler); messageList.AttachPayloadHandler(payloadHandler);
@@ -172,7 +172,7 @@ internal sealed class PayloadHandlerInitHostedService(
// InputBar -> CommandHelpWindow -> MainWindow -> InputBar (MS.DI does not catch // InputBar -> CommandHelpWindow -> MainWindow -> InputBar (MS.DI does not catch
// it through FactoryCallSite registrations and the resolve recurses silently). // it through FactoryCallSite registrations and the resolve recurses silently).
// Both singletons exist by host.StartAsync time, so this is the first safe point // Both singletons exist by host.StartAsync time, so this is the first safe point
// to wire the setter — same §6.2 pattern as MessageList.AttachPayloadHandler. // to wire the setter — same setter-injection pattern as MessageList.AttachPayloadHandler.
internal sealed class CommandHelpWindowInitHostedService( internal sealed class CommandHelpWindowInitHostedService(
CommandHelpWindow commandHelpWindow, CommandHelpWindow commandHelpWindow,
MainWindow mainWindow MainWindow mainWindow
@@ -190,7 +190,7 @@ internal sealed class CommandHelpWindowInitHostedService(
// Attaches the singleton PayloadHandler to every pre-allocated pop-out // Attaches the singleton PayloadHandler to every pre-allocated pop-out
// window's MessageList post-container-build. Pool/window cannot take the // window's MessageList post-container-build. Pool/window cannot take the
// PayloadHandler via ctor (that would close the silent FactoryCallSite cycle — // PayloadHandler via ctor (that would close the silent FactoryCallSite cycle —
// same §6.2 reason as MessageList.AttachPayloadHandler / CommandHelpWindow. // same setter-injection reason as MessageList.AttachPayloadHandler / CommandHelpWindow.
// AttachMainWindow). Both singletons exist by host.StartAsync time. // AttachMainWindow). Both singletons exist by host.StartAsync time.
internal sealed class ChannelPopoutInitHostedService( internal sealed class ChannelPopoutInitHostedService(
ChannelPopoutPool pool, ChannelPopoutPool pool,
+3 -3
View File
@@ -20,7 +20,7 @@ internal sealed class TypingIpc : IDisposable
private ICallGateProvider<ChatInputState> StateQueryGate { get; } private ICallGateProvider<ChatInputState> StateQueryGate { get; }
private ICallGateProvider<ChatInputState, object?> StateChangedGate { get; } private ICallGateProvider<ChatInputState, object?> StateChangedGate { get; }
// v1.4.9 R4: ChatTwo IPC compatibility mirror. Some third-party plugins // v1.4.9: ChatTwo IPC compatibility mirror. Some third-party plugins
// have a no-fork policy and subscribe only to ChatTwo.*-prefixed IPC // have a no-fork policy and subscribe only to ChatTwo.*-prefixed IPC
// gates. HellionChat replaces ChatTwo (conflict detection prevents // gates. HellionChat replaces ChatTwo (conflict detection prevents
// parallel loading), so mirroring the ChatTwo provider slots lets those // parallel loading), so mirroring the ChatTwo provider slots lets those
@@ -50,7 +50,7 @@ internal sealed class TypingIpc : IDisposable
"HellionChat.ChatInputStateChanged" "HellionChat.ChatInputStateChanged"
); );
// v1.4.9 R4: ChatTwo-prefixed compatibility slots (see class-level comment). // v1.4.9: ChatTwo-prefixed compatibility slots (see class-level comment).
ChatTwoStateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>( ChatTwoStateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>(
"ChatTwo.GetChatInputState" "ChatTwo.GetChatInputState"
); );
@@ -102,7 +102,7 @@ internal sealed class TypingIpc : IDisposable
HasState = true; HasState = true;
LastState = state; LastState = state;
StateChangedGate.SendMessage(state); StateChangedGate.SendMessage(state);
// v1.4.9 R4: mirror on ChatTwo-prefixed slot for no-fork-policy plugins. // v1.4.9: mirror on ChatTwo-prefixed slot for no-fork-policy plugins.
ChatTwoStateChangedGate.SendMessage(state); ChatTwoStateChangedGate.SendMessage(state);
} }
+3 -3
View File
@@ -22,7 +22,7 @@ internal sealed class IpcManager : IDisposable
object? object?
> InvokeGate { get; } > InvokeGate { get; }
// v1.4.9 R4: ChatTwo IPC compatibility mirror. Third-party plugins with // v1.4.9: ChatTwo IPC compatibility mirror. Third-party plugins with
// a no-fork policy (e.g. Artisan, AllaganTools) only subscribe to the // a no-fork policy (e.g. Artisan, AllaganTools) only subscribe to the
// ChatTwo.*-prefixed context-menu integration gates. Mirroring all four // ChatTwo.*-prefixed context-menu integration gates. Mirroring all four
// provider slots under the ChatTwo namespace lets those plugins keep // provider slots under the ChatTwo namespace lets those plugins keep
@@ -65,7 +65,7 @@ internal sealed class IpcManager : IDisposable
object? object?
>("HellionChat.Invoke"); >("HellionChat.Invoke");
// v1.4.9 R4: ChatTwo-prefixed mirrors of the four context-menu slots // v1.4.9: ChatTwo-prefixed mirrors of the four context-menu slots
// above. Share the same Register/Unregister backing methods so a // above. Share the same Register/Unregister backing methods so a
// plugin that subscribes via either namespace lands in the same // plugin that subscribes via either namespace lands in the same
// Registered list. SendMessage on Invoke fans out to both gates. // Registered list. SendMessage on Invoke fans out to both gates.
@@ -103,7 +103,7 @@ internal sealed class IpcManager : IDisposable
) )
{ {
InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content); InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content);
// v1.4.9 R4: fan out the same event to plugins listening on ChatTwo.Invoke. // v1.4.9: fan out the same event to plugins listening on ChatTwo.Invoke.
ChatTwoInvokeGate.SendMessage(id, sender, contentId, payload, senderString, content); ChatTwoInvokeGate.SendMessage(id, sender, contentId, payload, senderString, content);
} }
+6 -7
View File
@@ -248,9 +248,8 @@ internal class MessageManager : IAsyncDisposable
_logger.LogError(ex, "Error in FilterAllTabs"); _logger.LogError(ex, "Error in FilterAllTabs");
} }
// v1.4.9 R3 profiling: Information so the xllog tail surfaces this // Information, not Debug, so the xllog tail surfaces this without a
// without a Debug filter. Belt-and-suspenders for future plugin-load // filter. Kept as a guard against future plugin-load regressions.
// regressions; remains in place after Sub-Task 3.4 Befund.
_logger.LogInformation($"FilterAllTabs took {stopwatch.ElapsedMilliseconds}ms"); _logger.LogInformation($"FilterAllTabs took {stopwatch.ElapsedMilliseconds}ms");
}); });
} }
@@ -436,10 +435,10 @@ internal class MessageManager : IAsyncDisposable
// TEST-MIRROR: ../_Helpers/TabSoundDecision.cs // TEST-MIRROR: ../_Helpers/TabSoundDecision.cs
// Unseen ("count only what you haven't seen") suppresses unread on an inactive // Unseen ("count only what you haven't seen") suppresses unread on an inactive
// tab when the active tab ALSO shows this message — you already saw it in the // tab when the active tab ALSO shows this message — you already saw it in the
// tab you're looking at (1.5.6 / upstream ChatTwo behavior). Pre-F2 the "active // tab you're looking at (1.5.6 / upstream ChatTwo behavior). The "active tab"
// tab" was wrongly pinned to Tabs[0], so this fired against the wrong tab; F2 // used to be pinned to Tabs[0], so this fired against the wrong one until
// recoupled CurrentTab to the REAL active tab, so currentTabMatches is now // CurrentTab was recoupled to the real active tab, and currentTabMatches is
// measured against the tab you actually see. All -> always counts; None -> // now measured against the tab you see. All -> always counts; None ->
// counts here and is gated out at the display layer. Pure + SelfTest-able. // counts here and is gated out at the display layer. Pure + SelfTest-able.
internal static bool ShouldCountUnread(Tab tab, Tab currentTab, bool currentTabMatches) => internal static bool ShouldCountUnread(Tab tab, Tab currentTab, bool currentTabMatches) =>
!( !(
+3 -4
View File
@@ -245,7 +245,7 @@ internal class MessageStore : IDisposable
private SqliteConnection Connect() private SqliteConnection Connect()
{ {
// v1.4.9 R3 profiling: trace cost of SQLite open + pragma-apply. Paired // v1.4.9 profiling: trace cost of SQLite open + pragma-apply. Paired
// with the Migrate-Stopwatch below — Connect alone is the cheap half // with the Migrate-Stopwatch below — Connect alone is the cheap half
// (Open + a handful of PRAGMAs); the expensive half typically lives in // (Open + a handful of PRAGMAs); the expensive half typically lives in
// Migrate, especially on a large DB after a schema bump. // Migrate, especially on a large DB after a schema bump.
@@ -260,7 +260,7 @@ internal class MessageStore : IDisposable
private void Migrate() private void Migrate()
{ {
// v1.4.9 R3 profiling: trace cost of the schema-migration chain. On a // v1.4.9 profiling: trace cost of the schema-migration chain. On a
// large DB after a fresh schema bump this is the dominant SQLite cost // large DB after a fresh schema bump this is the dominant SQLite cost
// at plugin-load, not Connect. // at plugin-load, not Connect.
var migrateSw = System.Diagnostics.Stopwatch.StartNew(); var migrateSw = System.Diagnostics.Stopwatch.StartNew();
@@ -939,8 +939,7 @@ internal class MessageStore : IDisposable
// storage form on both sides so the IN(...) compare matches. SQLite has a // storage form on both sides so the IN(...) compare matches. SQLite has a
// hard parameter limit of 999 in default builds, so we chunk the input -- // hard parameter limit of 999 in default builds, so we chunk the input --
// a 1000-hit FTS query never explodes the SELECT. Result ordering is not // a 1000-hit FTS query never explodes the SELECT. Result ordering is not
// guaranteed; callers re-sort (e.g. DbViewer sorts by Date descending in // guaranteed; callers re-sort (DbViewer sorts by Date descending).
// Sub-Task 4.4).
public IReadOnlyList<Message> LoadByGuids(IReadOnlyList<string> guidStrings) public IReadOnlyList<Message> LoadByGuids(IReadOnlyList<string> guidStrings)
{ {
if (guidStrings.Count == 0) if (guidStrings.Count == 0)
+3 -3
View File
@@ -263,7 +263,7 @@ internal sealed class PayloadHandler
// Eureka, Bozja and Occult need special handling as tells work different // Eureka, Bozja and Occult need special handling as tells work different
if (!Sheets.IsInForay()) if (!Sheets.IsInForay())
{ {
// §6.9: single SetPendingMessage call; v1.5.6 used incremental Chat += writes. // Single SetPendingMessage call; v1.5.6 used incremental Chat += writes.
// XC-8: shares the /tell builder with the native detours. IsPublic (not // XC-8: shares the /tell builder with the native detours. IsPublic (not
// IsNullOrEmpty) is resolved HERE — a private/null world must NOT leak @World. // IsNullOrEmpty) is resolved HERE — a private/null world must NOT leak @World.
_inputBar.SetPendingMessage( _inputBar.SetPendingMessage(
@@ -393,7 +393,7 @@ internal sealed class PayloadHandler
var inputChannel = chunk.Message?.Code.Type.ToInputChannel(); var inputChannel = chunk.Message?.Code.Type.ToInputChannel();
if (inputChannel != null && ImGui.Selectable(Language.Context_ReplyInSelectedChatMode)) if (inputChannel != null && ImGui.Selectable(Language.Context_ReplyInSelectedChatMode))
{ {
// §6.3: route channel-switch through MainWindow's active tab // Route the channel switch through MainWindow's active tab
_mainWindow.ActiveTab?.CurrentChannel?.SetChannel(inputChannel.Value); _mainWindow.ActiveTab?.CurrentChannel?.SetChannel(inputChannel.Value);
_inputBar.Activate = true; _inputBar.Activate = true;
} }
@@ -731,7 +731,7 @@ internal sealed class PayloadHandler
using (ImRaii.Tooltip()) using (ImRaii.Tooltip())
using (ImRaii.TextWrapPos(0.0f)) using (ImRaii.TextWrapPos(0.0f))
using ( using (
// §4.2: use active theme text colour instead of the former LogWindow.DefaultText static. // Use the active theme text colour instead of the former LogWindow.DefaultText static.
ImRaii.PushColor( ImRaii.PushColor(
ImGuiCol.Text, ImGuiCol.Text,
ColourUtil.RgbaToVector4(_themes.Active.Colors.TextPrimary) ColourUtil.RgbaToVector4(_themes.Active.Colors.TextPrimary)
+7 -7
View File
@@ -15,7 +15,7 @@ namespace HellionChat;
// Builds the generic-host DI container that drives v1.5.0+. The factory is // Builds the generic-host DI container that drives v1.5.0+. The factory is
// invoked synchronously from Plugin.ctor (after the schema gate clears) so the // invoked synchronously from Plugin.ctor (after the schema gate clears) so the
// container exists before PluginLifecycle.LoadAsync runs. See plan §1 for the // container exists before PluginLifecycle.LoadAsync runs. For the
// deliberate divergence from Lightless' deferred Func-delegate pattern. // deliberate divergence from Lightless' deferred Func-delegate pattern.
internal static class PluginHostFactory internal static class PluginHostFactory
{ {
@@ -48,7 +48,7 @@ internal static class PluginHostFactory
PluginHostDependencies dependencies PluginHostDependencies dependencies
) )
{ {
// Block A — Dalamud services (21 [PluginService] singletons). // Dalamud services (21 [PluginService] singletons).
services.AddSingleton(dependencies); services.AddSingleton(dependencies);
services.AddSingleton(dependencies.PluginInterface); services.AddSingleton(dependencies.PluginInterface);
services.AddSingleton(dependencies.PluginLog); services.AddSingleton(dependencies.PluginLog);
@@ -77,7 +77,7 @@ internal static class PluginHostFactory
services.AddSingleton(plugin.WindowSystem); services.AddSingleton(plugin.WindowSystem);
services.AddSingleton<PluginLifecycle>(); services.AddSingleton<PluginLifecycle>();
// Block B — HellionChat singletons. Factory lambdas because most // HellionChat singletons. Factory lambdas because most
// classes are internal-sealed and the default activator only sees // classes are internal-sealed and the default activator only sees
// public ctors. // public ctors.
services.AddSingleton<IPlatformUtil>(_ => new DalamudPlatformUtil()); services.AddSingleton<IPlatformUtil>(_ => new DalamudPlatformUtil());
@@ -307,7 +307,7 @@ internal static class PluginHostFactory
// Pop-out windows: each gets its OWN MessageList + InputBar so the // Pop-out windows: each gets its OWN MessageList + InputBar so the
// channel pill and message scroll are per-window. The PayloadHandler is // channel pill and message scroll are per-window. The PayloadHandler is
// attached post-build (ChannelPopoutInitHostedService), NEVER via ctor // attached post-build (ChannelPopoutInitHostedService), NEVER via ctor
// (plan §B.2 — would close a silent FactoryCallSite cycle). // (would close a silent FactoryCallSite cycle).
services.AddSingleton<Func<int, Ui.Windows.ChannelPopoutWindow>>(sp => services.AddSingleton<Func<int, Ui.Windows.ChannelPopoutWindow>>(sp =>
slot => new Ui.Windows.ChannelPopoutWindow( slot => new Ui.Windows.ChannelPopoutWindow(
slot, slot,
@@ -336,7 +336,7 @@ internal static class PluginHostFactory
sp.GetRequiredService<ILogger<Ui.Windows.ChannelPopoutPool>>() sp.GetRequiredService<ILogger<Ui.Windows.ChannelPopoutPool>>()
)); ));
// Block C — Windows. WindowSystem.AddWindow is called from // Windows. WindowSystem.AddWindow is called from
// PluginLifecycle.LoadAsync on the framework thread. // PluginLifecycle.LoadAsync on the framework thread.
services.AddSingleton(sp => new Ui.Windows.SettingsWindow( services.AddSingleton(sp => new Ui.Windows.SettingsWindow(
sp.GetRequiredService<Plugin>(), sp.GetRequiredService<Plugin>(),
@@ -380,8 +380,8 @@ internal static class PluginHostFactory
)); ));
#endif #endif
// The style lab: variants side by side, in-game, against the live theme. // The style lab: variants side by side, in-game, against the live theme.
// Permanent by Flo's call, and deliberately not behind DEBUG -- style // Permanent, and deliberately not behind DEBUG: style decisions get
// decisions happen in the build he actually runs. // made in the build that actually ships.
services.AddSingleton(sp => new Ui.Windows.InputBarLabWindow( services.AddSingleton(sp => new Ui.Windows.InputBarLabWindow(
sp.GetRequiredService<Plugin>() sp.GetRequiredService<Plugin>()
)); ));
+1 -1
View File
@@ -4,7 +4,7 @@ namespace HellionChat.Privacy;
internal static class PrivacyDefaults internal static class PrivacyDefaults
{ {
// F3.1: failsafe for ChatTypes added by future FFXIV patches. New installs // Failsafe for ChatTypes added by future FFXIV patches. New installs
// persist unknown channels so a major patch's added ChatType isn't silently // persist unknown channels so a major patch's added ChatType isn't silently
// dropped before the user can opt in or out. Existing configs keep their // dropped before the user can opt in or out. Existing configs keep their
// explicit choice — see Configuration.cs PrivacyPersistUnknownChannels. // explicit choice — see Configuration.cs PrivacyPersistUnknownChannels.
+1 -1
View File
@@ -56,7 +56,7 @@ internal sealed class CardClipPlanStep : ISelfTestStep
int remaining; int remaining;
try try
{ {
// v1.10.0/A1: the fingerprint gate waits for the value to settle, so // v1.10.0: the fingerprint gate waits for the value to settle, so
// the step walks a synthetic clock past the window instead of sleeping. // the step walks a synthetic clock past the window instead of sleeping.
var clock = Environment.TickCount64; var clock = Environment.TickCount64;
messages.RunHeightCacheInvalidationForSelfTest(tab, 400f, clock); messages.RunHeightCacheInvalidationForSelfTest(tab, 400f, clock);
@@ -3,7 +3,7 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// F2: CurrentTab is coupled to MainWindow.ActiveTab (no longer the fixed index-0 // CurrentTab is coupled to MainWindow.ActiveTab (no longer the fixed index-0
// Tabs lookup). Asserts ReferenceEquals between the two, with false-green // Tabs lookup). Asserts ReferenceEquals between the two, with false-green
// defenses: (1) empty-config exercises the getter's fallback; (2) null ActiveTab // defenses: (1) empty-config exercises the getter's fallback; (2) null ActiveTab
// opens the window so the Draw-seed sets it and retries via Waiting (bounded so a // opens the window so the Draw-seed sets it and retries via Waiting (bounded so a
@@ -5,12 +5,12 @@ using HellionChat.GameFunctions.Types;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// F2 (guided): interactive, fires NO synthetic probes. Shows the full measured // Guided: interactive, fires NO synthetic probes. Shows the full measured
// state every frame so a result is observable, not a guess, and walks the user // state every frame so a result is observable, not a guess, and walks the user
// through the real switch-away-and-back flow. It verifies the PRIVACY-relevant // through the real switch-away-and-back flow. It verifies the PRIVACY-relevant
// effect, keyed on the tab type: // effect, keyed on the tab type:
// - a NORMAL tab carrying a game-side tell must lose its RUNTIME target // - a NORMAL tab carrying a game-side tell must lose its RUNTIME target
// (CurrentChannel.TellTarget) on switch-away-and-back (the F1 strip), so a // (CurrentChannel.TellTarget) on switch-away-and-back, so a
// typed line can't /tell the old partner; // typed line can't /tell the old partner;
// - a BOUND auto-tell tab keeps its partner by design (leg1) — its binding is // - a BOUND auto-tell tab keeps its partner by design (leg1) — its binding is
// Tab.TellTarget and is deliberately untouched by the strip. // Tab.TellTarget and is deliberately untouched by the strip.
+1 -1
View File
@@ -7,7 +7,7 @@ using HellionChat.Util;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// v1.12.0/A2: the exporter now reads text from the chunk lists instead of the // v1.12.0: the exporter now reads text from the chunk lists instead of the
// raw SeStrings. That change is invisible to the build suite -- ExportToFile // raw SeStrings. That change is invisible to the build suite -- ExportToFile
// takes IEnumerable<Message>, Message needs SeString, and xUnit cannot load // takes IEnumerable<Message>, Message needs SeString, and xUnit cannot load
// Dalamud.dll, so even an empty list fails before the body runs. // Dalamud.dll, so even an empty list fails before the body runs.
@@ -127,7 +127,7 @@ internal sealed class FontManagerCtorSmokeStep : ISelfTestStep
return SelfTestStepResult.Fail; return SelfTestStepResult.Fail;
} }
// Report what was actually verified (Flo's request: don't just show Pass). // Report what was actually verified rather than a bare Pass.
// The glyph-range entry counts make the B1 dedup visible — the cjk-fallback // The glyph-range entry counts make the B1 dedup visible — the cjk-fallback
// range is now a small trimmed remainder next to the large primary range. // range is now a small trimmed remainder next to the large primary range.
var counts = fm.GlyphRangeLengths; var counts = fm.GlyphRangeLengths;
@@ -43,7 +43,7 @@ internal sealed class GlobalStyleScopeAllocStep : ISelfTestStep
GlobalStyleScope.Push(theme, registry, opacity).Dispose(); GlobalStyleScope.Push(theme, registry, opacity).Dispose();
var delta = GC.GetAllocatedBytesForCurrentThread() - before; var delta = GC.GetAllocatedBytesForCurrentThread() - before;
// Report the measured figure on BOTH outcomes (Flo's request: don't just // Report the measured figure on BOTH outcomes (a bare Pass hides
// show Pass) — the byte delta is the whole point of the GC-reserve probe. // show Pass) — the byte delta is the whole point of the GC-reserve probe.
var ok = delta <= AllocBudgetBytes; var ok = delta <= AllocBudgetBytes;
var status = ok ? "PASS" : "FAIL"; var status = ok ? "PASS" : "FAIL";
@@ -68,7 +68,7 @@ internal sealed class HonorificHeaderRenderStep : ISelfTestStep
); );
// Draw at a deliberately wide 420px so the title never hits the truncation // Draw at a deliberately wide 420px so the title never hits the truncation
// clamp — LastTitleRendered then reflects the GATE outcome, not the width. // clamp -- LastTitleRendered then reflects the gate outcome, not the width.
try try
{ {
// (a) available + valid title + toggle on -> title renders // (a) available + valid title + toggle on -> title renders
@@ -4,7 +4,7 @@ using HellionChat.Ui.StyleEngine;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// Master-spec §7.5 scope note: the hover registry must not grow frame by frame. // the hover registry must not grow frame by frame.
// Successor to HoverSheenAllocStep, which pinned the same contract against the // Successor to HoverSheenAllocStep, which pinned the same contract against the
// old sheen start-timestamp dictionary. // old sheen start-timestamp dictionary.
// //
+2 -2
View File
@@ -7,8 +7,8 @@ namespace HellionChat.SelfTests;
// B1-2 window flags. Drives the REAL MainWindow.PreDraw and asserts it wired // B1-2 window flags. Drives the REAL MainWindow.PreDraw and asserts it wired
// Window.Flags to ResolveFlags(CanMove, CanResize), then pins the pure // Window.Flags to ResolveFlags(CanMove, CanResize), then pins the pure
// fresh-base contract: false/false adds NoMove|NoResize, true/true clears them // fresh-base contract: false/false adds NoMove|NoResize, true/true clears them
// (the masterplan's "flags must rebuild from a fresh base, else NoMove sticks // -- flags must rebuild from a fresh base, or NoMove sticks after toggling
// after toggling back" risk). NoScrollbar|NoScrollWithMouse always present. // back. NoScrollbar|NoScrollWithMouse always present.
// Non-test caller of ResolveFlags: MainWindow.PreDraw. // Non-test caller of ResolveFlags: MainWindow.PreDraw.
internal sealed class MainWindowFlagsStep : ISelfTestStep internal sealed class MainWindowFlagsStep : ISelfTestStep
{ {
@@ -7,7 +7,7 @@ namespace HellionChat.SelfTests;
// step so the per-frame hot path never references file IO. Writes one // step so the per-frame hot path never references file IO. Writes one
// perf-baseline.json into the plugin ConfigDirectory, atomically (tmp + move) // perf-baseline.json into the plugin ConfigDirectory, atomically (tmp + move)
// like ThemeRegistry's theme writer, so a mid-write crash leaves either the // like ThemeRegistry's theme writer, so a mid-write crash leaves either the
// old file or the new file, never a half JSON. Field names track §7.5: // old file or the new file, never a half JSON. Field names track the performance-baseline layout:
// steady-state Draw cost (avg/max ms), the quad-proxy draw-call count // steady-state Draw cost (avg/max ms), the quad-proxy draw-call count
// (avg/max), and frame delta (avg/max). First-frame-HITCH is read off // (avg/max), and frame delta (avg/max). First-frame-HITCH is read off
// drawMs max/avg by the human author, platform-annotated in the notes. // drawMs max/avg by the human author, platform-annotated in the notes.
@@ -6,14 +6,14 @@ namespace HellionChat.SelfTests;
// Optional metric capture. Accumulates 1000 steady-state frames of ImGui IO // Optional metric capture. Accumulates 1000 steady-state frames of ImGui IO
// counters plus the plugin's full-Draw wall-time (Plugin.LastDrawMs, B5-1), // counters plus the plugin's full-Draw wall-time (Plugin.LastDrawMs, B5-1),
// then writes a single perf-baseline.json into the plugin ConfigDirectory so // then writes a single perf-baseline.json into the plugin ConfigDirectory so
// the cycle-notes author can copy the §7.5 figures without a separate // the cycle-notes author can copy the baseline figures without a separate
// profiling harness. The step only records — it never fails on a threshold // profiling harness. The step only records — it never fails on a threshold
// (the budgets are evaluated by a human against the JSON, §7.5 "optional, // (the budgets are evaluated by a human against the JSON ("optional,
// manual"). It returns Waiting until the sample window fills, mirroring the // manual"). It returns Waiting until the sample window fills, mirroring the
// per-frame poll idiom of ThemeSwitchSelfTestStep. // per-frame poll idiom of ThemeSwitchSelfTestStep.
internal sealed class PerformanceBaselineStep : ISelfTestStep internal sealed class PerformanceBaselineStep : ISelfTestStep
{ {
// §7.5 steady-state window. 1000 frames ≈ 16s at 60fps, long enough to // Steady-state window. 1000 frames ≈ 16s at 60fps, long enough to
// average out GC blips without making the manual step tedious. // average out GC blips without making the manual step tedious.
private const int TargetFrames = 1000; private const int TargetFrames = 1000;
@@ -4,7 +4,7 @@ using HellionChat.Code;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// F3: the unread dot the v1.8.x sidebar rebuild dropped. Drives the REAL // The unread dot the v1.8.x sidebar rebuild dropped. Drives the REAL
// Sidebar.Draw (render precedent: SidebarGreetedGlyphStep) with a probe tab that // Sidebar.Draw (render precedent: SidebarGreetedGlyphStep) with a probe tab that
// is inactive and carries Unread>0, then reads the render-observability counter // is inactive and carries Unread>0, then reads the render-observability counter
// so a regressed/absent dot fails. Asserts: dot drawn for an inactive Unseen tab; // so a regressed/absent dot fails. Asserts: dot drawn for an inactive Unseen tab;
@@ -7,7 +7,7 @@ using HellionChat.Util;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// F1: the activation strip. Drives the REAL OnTabActivated — the entry the // The activation strip. Drives the REAL OnTabActivated -- the entry the
// Sidebar/TopTabBar click handlers, the pop-out path and the Draw-seed all call // Sidebar/TopTabBar click handlers, the pop-out path and the Draw-seed all call
// — with local probe tabs (Plugin.Config.Tabs is never touched). Asserts the // — with local probe tabs (Plugin.Config.Tabs is never touched). Asserts the
// five contracts: strip-on-switch, no-strip-on-reclick (TR-4), leg1 preserve, // five contracts: strip-on-switch, no-strip-on-reclick (TR-4), leg1 preserve,
+1 -1
View File
@@ -7,7 +7,7 @@ using HellionChat.Ui.StyleEngine;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// v1.13.0/A7: the type scale has no call site in the message list until block C, // v1.13.0: the type scale has no call site in the message list until block C,
// so without this step block A would end with nothing to look at and two helpers // so without this step block A would end with nothing to look at and two helpers
// (TypeScale, BaselineMath) with no caller at all. // (TypeScale, BaselineMath) with no caller at all.
// //
+7 -6
View File
@@ -3,12 +3,12 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// F3: the unread decision (MessageManager.ShouldCountUnread). Unseen suppresses // The unread decision (MessageManager.ShouldCountUnread). Unseen suppresses
// unread on an inactive tab only when the active tab ALSO shows the message (you // unread on an inactive tab only when the active tab ALSO shows the message (you
// saw it there) — 1.5.6/upstream semantics, now measured against the REAL active // saw it there) -- 1.5.6/upstream semantics, measured against the real active
// tab thanks to F2. Asserts the truth table: suppressed when active tab also // tab. Asserts the truth table: suppressed when the active tab also matches;
// matches; counts when it does not (the Carla/Jin case); All always counts; None // counts when it does not; All always counts; None counts at the increment
// counts at the increment layer (the display gate hides it). // layer (the display gate hides it).
internal sealed class UnreadDecisionStep : ISelfTestStep internal sealed class UnreadDecisionStep : ISelfTestStep
{ {
public string Name => "Hellion Chat - Unread decision (per active tab)"; public string Name => "Hellion Chat - Unread decision (per active tab)";
@@ -27,7 +27,8 @@ internal sealed class UnreadDecisionStep : ISelfTestStep
} }
// (b) inactive Unseen tab + the active tab does NOT show the message // (b) inactive Unseen tab + the active tab does NOT show the message
// (currentTabMatches=false) => counts (badge). The Carla/Jin case. // (currentTabMatches=false) => counts (badge). Two people talking in
// a channel the active tab does not carry.
if (!MessageManager.ShouldCountUnread(inactive, active, currentTabMatches: false)) if (!MessageManager.ShouldCountUnread(inactive, active, currentTabMatches: false))
{ {
ImGui.Text("(b) inactive Unseen tab must count when the active tab does not show it"); ImGui.Text("(b) inactive Unseen tab must count when the active tab does not show it");
@@ -64,7 +64,7 @@ internal sealed class WizardStateSmokeStep : ISelfTestStep
// jumps straight to Step 4 (no Step-3 entry → no seed for // jumps straight to Step 4 (no Step-3 entry → no seed for
// FilterIncludePreviousSessions), commits, and asserts the history // FilterIncludePreviousSessions), commits, and asserts the history
// toggle remained on its pre-test value. Pins the null-semantics // toggle remained on its pre-test value. Pins the null-semantics
// from Spec Z.176 so a regression in CommitPending that started // so a regression in CommitPending that started
// writing seeded recommendations unconditionally would surface // writing seeded recommendations unconditionally would surface
// here. // here.
// CommitPending → ApplyRoleplay overwrites six privacy / // CommitPending → ApplyRoleplay overwrites six privacy /
+1 -1
View File
@@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging;
namespace HellionChat.Services; namespace HellionChat.Services;
// Routes an incoming tell to the configured TellAutoOpenMode (Off/Sidebar/ // Routes an incoming tell to the configured TellAutoOpenMode (Off/Sidebar/
// TopTab/Popout). Decoupled from AutoTellTabsService (Flo decision 2026-06-15): // TopTab/Popout). Deliberately decoupled from AutoTellTabsService:
// that service owns tab CREATION + lifecycle; this only REVEALS/pops the tab it // that service owns tab CREATION + lifecycle; this only REVEALS/pops the tab it
// finds. Popout guards on pool.IsOpen so it never double-pops a tab the // finds. Popout guards on pool.IsOpen so it never double-pops a tab the
// AutoTellTabsOpenAsPopout path already opened. Subscribes to the resolved // AutoTellTabsOpenAsPopout path already opened. Subscribes to the resolved
+11 -11
View File
@@ -54,7 +54,7 @@ public sealed class ThemeRegistry
internal void SetActiveChangedCallback(Action callback) => _onActiveChanged = callback; internal void SetActiveChangedCallback(Action callback) => _onActiveChanged = callback;
// Shared slug guard for any code path that turns a slug into a filename. // Shared slug guard for any code path that turns a slug into a filename.
// Both SaveEditingBuffer (F1) and ImportFromPath (M6) call this so the // Both SaveEditingBuffer and ImportFromPath call this so the
// path-traversal/invalid-char rules live in exactly one place. // path-traversal/invalid-char rules live in exactly one place.
// //
// Whitespace rejection is intentional: Path.GetInvalidFileNameChars on // Whitespace rejection is intentional: Path.GetInvalidFileNameChars on
@@ -112,7 +112,7 @@ public sealed class ThemeRegistry
public Theme Active => _active; public Theme Active => _active;
// Read-only exposure of the configured custom themes directory. // Read-only exposure of the configured custom themes directory.
// M6 ThemeImportExportRow opens this path via Process.Start. // ThemeImportExportRow opens this path via Process.Start.
public string? CustomThemesDir => _customThemesDir; public string? CustomThemesDir => _customThemesDir;
// Read-only enumeration of all built-in theme slugs. T2 ThemePickerCategoryStep // Read-only enumeration of all built-in theme slugs. T2 ThemePickerCategoryStep
@@ -120,8 +120,8 @@ public sealed class ThemeRegistry
public IEnumerable<string> BuiltinSlugs => _builtIns.Keys; public IEnumerable<string> BuiltinSlugs => _builtIns.Keys;
// True try-pattern lookup: returns false when neither built-in nor custom // True try-pattern lookup: returns false when neither built-in nor custom
// cache holds the slug, no fallback to default. M3 ThemePicker uses this // cache holds the slug, no fallback to default. ThemePicker uses this
// for card-rendering, M6 ThemeImportExportRow for fork-slug collisions. // for card-rendering, ThemeImportExportRow for fork-slug collisions.
// Cold-cache fallback: see `LoadCustomBySlug` lookup-by-slug reverse // Cold-cache fallback: see `LoadCustomBySlug` lookup-by-slug reverse
// iteration — it only walks the pre-populated _customCache. If a freshly // iteration — it only walks the pre-populated _customCache. If a freshly
// imported file has not been enumerated yet (or no warm-up ran), the first // imported file has not been enumerated yet (or no warm-up ran), the first
@@ -292,7 +292,7 @@ public sealed class ThemeRegistry
// Switch() prefers built-ins over custom themes with the same slug // Switch() prefers built-ins over custom themes with the same slug
// (see `Switch` built-in-first lookup), so saving a custom file under // (see `Switch` built-in-first lookup), so saving a custom file under
// a built-in slug persists the file but leaves the built-in active — // a built-in slug persists the file but leaves the built-in active —
// looks green, behaves broken. M4 ColorPicker DrawIdleState forks // looks green, behaves broken. ColorPicker DrawIdleState forks
// built-in themes into a custom slug before BeginEditing, M6 // built-in themes into a custom slug before BeginEditing, M6
// ImportFromPath renames built-in-colliding imports to <slug>_imported. // ImportFromPath renames built-in-colliding imports to <slug>_imported.
// New call-sites must either fork first or rename to a non-built-in slug. // New call-sites must either fork first or rename to a non-built-in slug.
@@ -308,7 +308,7 @@ public sealed class ThemeRegistry
// separators, parent-directory tokens, or platform-invalid filename chars. // separators, parent-directory tokens, or platform-invalid filename chars.
// Without this guard an imported theme with Slug "../../../etc/passwd" // Without this guard an imported theme with Slug "../../../etc/passwd"
// would let Path.Combine escape _customThemesDir entirely. Shared helper // would let Path.Combine escape _customThemesDir entirely. Shared helper
// so M6 ImportFromPath uses the exact same rule set. // so ImportFromPath uses the exact same rule set.
var safeSlug = _editingThemeBuffer.Slug; var safeSlug = _editingThemeBuffer.Slug;
if (!IsSafeThemeSlug(safeSlug)) if (!IsSafeThemeSlug(safeSlug))
{ {
@@ -326,8 +326,8 @@ public sealed class ThemeRegistry
// persist a custom file under a built-in slug — the file lands on disk, // persist a custom file under a built-in slug — the file lands on disk,
// Switch keeps the built-in active, and the post-save active-slug check // Switch keeps the built-in active, and the post-save active-slug check
// below returns false. The caller then sees "save failed" while a garbage // below returns false. The caller then sees "save failed" while a garbage
// file accumulates in the themes dir on every retry. M4 ColorPicker forks // file accumulates in the themes dir on every retry. ColorPicker forks
// built-in themes into a custom slug before BeginEditing, M6 ImportFromPath // built-in themes into a custom slug before BeginEditing, ImportFromPath
// renames built-in-colliding imports to <slug>_imported, so production // renames built-in-colliding imports to <slug>_imported, so production
// paths already steer clear; this guard catches everything else. // paths already steer clear; this guard catches everything else.
if (_builtIns.ContainsKey(safeSlug)) if (_builtIns.ContainsKey(safeSlug))
@@ -522,8 +522,8 @@ public sealed class ThemeRegistry
) )
{ {
var t = (float)(now - _crossfadeStartTickMs) / CrossfadeDurationMs; var t = (float)(now - _crossfadeStartTickMs) / CrossfadeDurationMs;
// A2: SmoothStep easing so the fade eases in/out instead of a // SmoothStep easing so the fade eases in and out instead of running
// linear ramp. MUST stay in lockstep with TryGetActiveCrossfade (K8). // linear. MUST stay in lockstep with TryGetActiveCrossfade.
var te = t * t * (3f - 2f * t); var te = t * t * (3f - 2f * t);
snapshot = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, te); snapshot = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, te);
} }
@@ -551,7 +551,7 @@ public sealed class ThemeRegistry
return false; return false;
var t = (float)elapsed / CrossfadeDurationMs; var t = (float)elapsed / CrossfadeDurationMs;
// A2: SmoothStep easing -- keep identical to ArmCrossfade (K8). // SmoothStep easing -- keep identical to ArmCrossfade.
var te = t * t * (3f - 2f * t); var te = t * t * (3f - 2f * t);
lerped = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, te); lerped = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, te);
return true; return true;
+2 -2
View File
@@ -18,7 +18,7 @@ internal sealed class CommandHelpWindow : Window
// Setter-injected post-ctor to break the InputBar -> CommandHelpWindow -> // Setter-injected post-ctor to break the InputBar -> CommandHelpWindow ->
// MainWindow -> InputBar singleton cycle (MS.DI does not detect cycles // MainWindow -> InputBar singleton cycle (MS.DI does not detect cycles
// through FactoryCallSite registrations). Wired in // through FactoryCallSite registrations). Wired in
// CommandHelpWindowInitHostedService.StartAsync, same §6.2 pattern as // CommandHelpWindowInitHostedService.StartAsync, same setter-injection pattern as
// MessageList.AttachPayloadHandler. // MessageList.AttachPayloadHandler.
private Windows.MainWindow? _mainWindow; private Windows.MainWindow? _mainWindow;
@@ -41,7 +41,7 @@ internal sealed class CommandHelpWindow : Window
RespectCloseHotkey = false; RespectCloseHotkey = false;
DisableWindowSounds = true; DisableWindowSounds = true;
// Logger injected for future diagnostic hooks (no call-sites yet in R2). // Logger injected for future diagnostic hooks; no call sites yet.
_ = _logger; _ = _logger;
} }
+1 -1
View File
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace HellionChat.Ui.Components; namespace HellionChat.Ui.Components;
// B2 (PERF-B2): variable-height clip plan. ImGuiListClipper needs a constant // B2 (PERF-B2): variable-height clip plan. ImGuiListClipper needs a constant
// row height, and since v1.10.0/A2 neither density has one (compact rows wrap // 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 // too), so both compute a plan from the cached per-row heights: a lead dummy
// for the rows above // for the rows above
// the viewport, the [first..last] index range that overlaps the viewport, and // the viewport, the [first..last] index range that overlaps the viewport, and
+2 -2
View File
@@ -118,7 +118,7 @@ internal sealed class InputBar
// Note: when MainWindow is closed, DrawInputField never runs, so // Note: when MainWindow is closed, DrawInputField never runs, so
// _isFocused keeps the last value written by the previous draw pass. // _isFocused keeps the last value written by the previous draw pass.
// The consumer that actually pushes this state across the IPC boundary // The consumer that actually pushes this state across the IPC boundary
// (TypingIpc.BuildState, see F3 Step 2) gates on Plugin.MainWindow.IsOpen // (TypingIpc.BuildState) gates on Plugin.MainWindow.IsOpen
// itself, so the stale backing-field never leaks to subscribers. Mirroring // itself, so the stale backing-field never leaks to subscribers. Mirroring
// the gate here would require an extra Plugin-backref in InputBar that the // the gate here would require an extra Plugin-backref in InputBar that the
// rest of the component doesn't need. // rest of the component doesn't need.
@@ -1183,7 +1183,7 @@ internal sealed class InputBar
} }
// DTO for an in-flight auto-translate completion. Lives as a companion type // DTO for an in-flight auto-translate completion. Lives as a companion type
// in this file because it is only consumed by InputBar (see v1.7.1 Fix #4 plan §2.4). // in this file because it is only consumed by InputBar.
internal sealed class AutoCompleteInfo internal sealed class AutoCompleteInfo
{ {
// ToComplete MUST be a mutable field (not an auto-property), because the // ToComplete MUST be a mutable field (not an auto-property), because the
+4 -4
View File
@@ -38,7 +38,7 @@ internal sealed class MessageList
private readonly Action<Message, string?> _drawCardRow; private readonly Action<Message, string?> _drawCardRow;
// Reused across frames: at MessageManager.MessageDisplayLimit a fresh array // Reused across frames: at MessageManager.MessageDisplayLimit a fresh array
// per frame is 40 KB of garbage, and A2 put the default density on this // per frame is 40 KB of garbage, and A later cycle put the default density on this
// path. The old comment named MaxLinesToRender and its 2500 default, a // path. The old comment named MaxLinesToRender and its 2500 default, a
// config field that had stopped bounding anything. // config field that had stopped bounding anything.
private float[] _heightScratch = []; private float[] _heightScratch = [];
@@ -49,7 +49,7 @@ internal sealed class MessageList
private bool _stampVisible; private bool _stampVisible;
private float _metaDrop; private float _metaDrop;
// §6.2: setter-injection breaks the PayloadHandler → MainWindow → MessageList → PayloadHandler 3-cycle. // Setter-injection breaks the PayloadHandler → MainWindow → MessageList → PayloadHandler 3-cycle.
// Wired by PayloadHandlerInitHostedService.StartAsync after both singletons exist. // Wired by PayloadHandlerInitHostedService.StartAsync after both singletons exist.
internal void AttachPayloadHandler(PayloadHandler handler) internal void AttachPayloadHandler(PayloadHandler handler)
{ {
@@ -88,7 +88,7 @@ internal sealed class MessageList
// SelfTest hook (B2): drives the live invalidation, returns the tab's remaining // SelfTest hook (B2): drives the live invalidation, returns the tab's remaining
// cached-height count so the step can assert the drop. nowMs is a parameter so // 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/A1). // the step can step past the settle window without sleeping (v1.10.0).
internal int RunHeightCacheInvalidationForSelfTest(Tab tab, float contentWidth, long nowMs) internal int RunHeightCacheInvalidationForSelfTest(Tab tab, float contentWidth, long nowMs)
{ {
InvalidateHeightCacheIfLayoutChanged(tab, contentWidth, nowMs); InvalidateHeightCacheIfLayoutChanged(tab, contentWidth, nowMs);
@@ -569,7 +569,7 @@ internal sealed class MessageList
// B2-1/B2-2: sender via DrawChunks (name-aware path), on its own line // B2-1/B2-2: sender via DrawChunks (name-aware path), on its own line
// with content below — 1.5.6 card parity (ChatLogWindow.cs:1913, no // 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 // SameLine after the sender). The 1.5.6 channel-colour push on the
// sender is deferred styling polish (masterplan §6 -> v1.9.0); plain // sender is deferred styling polish (deferred to v1.9.0); plain
// text here. // text here.
// A system message has no sender, so a header row would be a stamp on a // A system message has no sender, so a header row would be a stamp on a
// line of its own -- an empty gesture. Those stay single-line in both // line of its own -- an empty gesture. Those stay single-line in both
@@ -20,7 +20,7 @@ internal sealed class LivePreviewPanel : IDisposable
private static uint Abgr(StyleEngine.Token token, ThemeColors colors) => private static uint Abgr(StyleEngine.Token token, ThemeColors colors) =>
ColourUtil.RgbaToAbgr(Tokens.Resolve(token, colors)); ColourUtil.RgbaToAbgr(Tokens.Resolve(token, colors));
// Static counter for S5 reload-stress verification: after 10 reloads the // Static counter for reload-stress verification: after 10 reloads the
// counter must read 0 (plugin disabled) or 1 (plugin enabled). Anything // counter must read 0 (plugin disabled) or 1 (plugin enabled). Anything
// higher signals a Dispose skip and a subscriber leak against ThemeRegistry. // higher signals a Dispose skip and a subscriber leak against ThemeRegistry.
internal static int InstanceCount; internal static int InstanceCount;
@@ -254,7 +254,7 @@ internal sealed class DataPrivacyTab
) )
{ {
// Read-only statement, not a switch. Do not promote it to one // Read-only statement, not a switch. Do not promote it to one
// without an explicit Sub-Spec change: a toggle implies there is // without an explicit design change: a toggle implies there is
// something to turn off. // something to turn off.
ImGuiUtil.HelpText(HellionStrings.Settings_Telemetry_None); ImGuiUtil.HelpText(HellionStrings.Settings_Telemetry_None);
} }
@@ -173,8 +173,7 @@ internal sealed class ThemeImportExportRow
// Slug sanitisation BEFORE BeginEditing — SaveEditingBuffer would // Slug sanitisation BEFORE BeginEditing — SaveEditingBuffer would
// reject too, but rejecting here means an unsafe slug never enters // reject too, but rejecting here means an unsafe slug never enters
// the editing buffer. Shared helper ThemeRegistry.IsSafeThemeSlug // the editing buffer. Shared helper ThemeRegistry.IsSafeThemeSlug
// keeps the rule set in sync with F1's save-side guard (see // keeps the rule set in sync with the save-side guard.
// ThemeRegistry.IsSafeThemeSlug shared helper).
var importSlug = theme.Slug; var importSlug = theme.Slug;
if (!ThemeRegistry.IsSafeThemeSlug(importSlug)) if (!ThemeRegistry.IsSafeThemeSlug(importSlug))
{ {
@@ -186,9 +185,9 @@ internal sealed class ThemeImportExportRow
return; return;
} }
// Pragmatic deviation from §1.6 wording ("File.Copy into themes/"): // Not a plain File.Copy into themes/: BeginEditing+SaveEditingBuffer
// BeginEditing+SaveEditingBuffer produces the same end-state and // produces the same end state and reuses the validated save
// reuses the validated F1 save pipeline. Trade-off: destination // pipeline. Trade-off: destination
// filename becomes the theme's slug, not the original filename. // filename becomes the theme's slug, not the original filename.
// //
// Slug-collision handling: // Slug-collision handling:
+1 -1
View File
@@ -54,7 +54,7 @@ internal sealed class InputPreview : Window
DisableWindowSounds = true; DisableWindowSounds = true;
IsOpen = true; IsOpen = true;
// TODO Polish-Sweep: remove discard once logging call-sites exist // TODO: remove discard once logging call sites exist
_ = _logger; _ = _logger;
} }
@@ -12,7 +12,7 @@ namespace HellionChat.Ui.StyleEngine;
// ABGR before delegating to ImDrawList. // ABGR before delegating to ImDrawList.
internal static class DrawListExtensions internal static class DrawListExtensions
{ {
// A1 accent-tint (Variante A): how far the white sweep is pulled toward // Accent tint: how far the white sweep is pulled toward
// the element's accent hue. Kept low so the sheen reads as a tinted // the element's accent hue. Kept low so the sheen reads as a tinted
// highlight, not a saturated accent flash (effect level "subtle"). // highlight, not a saturated accent flash (effect level "subtle").
private const float SheenTintStrength = 0.35f; private const float SheenTintStrength = 0.35f;
+2 -2
View File
@@ -9,7 +9,7 @@ namespace HellionChat.Ui.StyleEngine;
// compare an unscaled value (Sidebar.GetWidth, the width slider bounds). // compare an unscaled value (Sidebar.GetWidth, the width slider bounds).
// //
// Deliberately NOT part of ThemeLayout: that record is serialised into theme // Deliberately NOT part of ThemeLayout: that record is serialised into theme
// JSON, and layout customisation is out of scope per the master spec. // JSON, and layout customisation is deliberately out of scope.
internal static class Metrics internal static class Metrics
{ {
// --- Sidebar --- // --- Sidebar ---
@@ -65,7 +65,7 @@ internal static class Metrics
return _cachedScale; return _cachedScale;
// Safe variant: GlobalScale throws while the interface manager is // Safe variant: GlobalScale throws while the interface manager is
// still coming up, and Block F pulls Metrics into more call sites. // still coming up, and Metrics reaches more call sites than it did.
_cachedScale = ImGuiHelpers.GlobalScaleSafe; _cachedScale = ImGuiHelpers.GlobalScaleSafe;
_cachedFrame = frame; _cachedFrame = frame;
return _cachedScale; return _cachedScale;
+1 -1
View File
@@ -23,7 +23,7 @@ internal enum TypeRole
// exactly Body and never be pushed -- a value with no call site, which is the one // exactly Body and never be pushed -- a value with no call site, which is the one
// thing this whole style track exists to stop. // thing this whole style track exists to stop.
// //
// The factors are defaults, not constants. The master spec puts typography under // The factors are defaults, not constants. Typography is meant to sit under
// theme control rather than user control, and ThemeTypography already exists as // theme control rather than user control, and ThemeTypography already exists as
// the extension point for exactly that. A const would wall it off. What is // the extension point for exactly that. A const would wall it off. What is
// deliberately absent either way is a user-facing slider per role. // deliberately absent either way is a user-facing slider per role.
@@ -18,8 +18,8 @@ namespace HellionChat.Ui.StyleEngine.Widgets;
// //
// So: no plate, but a tint. A tenth-opacity accent wash falling from the top // So: no plate, but a tint. A tenth-opacity accent wash falling from the top
// edge -- colour as atmosphere rather than as a box, which is what the plate got // edge -- colour as atmosphere rather than as a box, which is what the plate got
// wrong. Flo picked it from the lab over the rule-only variant; at this strength // wrong. Picked from the lab over the rule-only variant; at this strength it
// it survives the violet themes that killed the filled bar. // survives the violet themes that killed the filled bar.
// //
// Which face draws what is not a style choice here, it is a constraint. The meta // Which face draws what is not a style choice here, it is a constraint. The meta
// face has a glyph range of ASCII plus a middle dot, so only the world name and // face has a glyph range of ASCII plus a middle dot, so only the world name and
+2 -2
View File
@@ -7,7 +7,7 @@ namespace HellionChat.Ui.Windows;
// windows via the injected factory, all registered once in the WindowSystem // windows via the injected factory, all registered once in the WindowSystem
// (PluginLifecycle.RegisterWindows, framework thread). Open/Close is IsOpen + // (PluginLifecycle.RegisterWindows, framework thread). Open/Close is IsOpen +
// Bind/Unbind only — NEVER runtime AddWindow/RemoveWindow (v1.4.9 Stage-2 // Bind/Unbind only — NEVER runtime AddWindow/RemoveWindow (v1.4.9 Stage-2
// freeze lesson). Pure DI-sink: no PayloadHandler in the ctor (plan §B.2). // freeze lesson). Pure DI-sink: no PayloadHandler in the ctor.
internal sealed class ChannelPopoutPool internal sealed class ChannelPopoutPool
{ {
private readonly List<ChannelPopoutWindow> _instances; private readonly List<ChannelPopoutWindow> _instances;
@@ -30,7 +30,7 @@ internal sealed class ChannelPopoutPool
// Route each window's in-body close through the pool so closing releases // Route each window's in-body close through the pool so closing releases
// the slot. Wired here (post-construction) rather than via ctor to avoid // the slot. Wired here (post-construction) rather than via ctor to avoid
// a Window->Pool edge that would re-enter pool resolution (plan §B.2). // a Window->Pool edge that would re-enter pool resolution.
foreach (var window in _instances) foreach (var window in _instances)
window.CloseRequested = TryClose; window.CloseRequested = TryClose;
} }
@@ -13,7 +13,7 @@ namespace HellionChat.Ui.Windows;
// One pre-allocated pop-out window bound to a single Tab. Pure DI-sink: the // One pre-allocated pop-out window bound to a single Tab. Pure DI-sink: the
// PayloadHandler arrives via AttachPayloadHandler (post-build setter), NEVER // PayloadHandler arrives via AttachPayloadHandler (post-build setter), NEVER
// via ctor — see plan §B.2. The ###id carries the slot index so all N // via ctor. The ###id carries the slot index so all N
// instances are unique for WindowSystem.AddWindow and ImGui state is stable // instances are unique for WindowSystem.AddWindow and ImGui state is stable
// per slot (not per bound tab). // per slot (not per bound tab).
internal sealed class ChannelPopoutWindow : Window, IFocusableChatWindow internal sealed class ChannelPopoutWindow : Window, IFocusableChatWindow
@@ -66,10 +66,10 @@ internal sealed class ChannelPopoutWindow : Window, IFocusableChatWindow
// Wired post-build by ChannelPopoutPool so closing routes through the pool // Wired post-build by ChannelPopoutPool so closing routes through the pool
// (which owns the slot map). The window can't reach the pool by ctor without // (which owns the slot map). The window can't reach the pool by ctor without
// a DI cycle, so the pool sets this after construction. See plan §B.2. // a DI cycle, so the pool sets this after construction.
public Action<Guid>? CloseRequested { get; set; } public Action<Guid>? CloseRequested { get; set; }
// Post-build setter — see plan §B.2. Wired by ChannelPopoutInitHostedService. // Post-build setter. Wired by ChannelPopoutInitHostedService.
public void AttachPayloadHandler(PayloadHandler handler) => public void AttachPayloadHandler(PayloadHandler handler) =>
_messages.AttachPayloadHandler(handler); _messages.AttachPayloadHandler(handler);
@@ -78,7 +78,7 @@ internal sealed class ChannelPopoutWindow : Window, IFocusableChatWindow
Bound = tab; Bound = tab;
var isTell = tab is { IsTempTab: true, TellTarget: { } target } && target.IsSet(); var isTell = tab is { IsTempTab: true, TellTarget: { } target } && target.IsSet();
// Master §4.3 default sizes: Tell is the more compact conversation window. // Default sizes: Tell is the more compact conversation window.
Size = isTell ? new Vector2(380f, 320f) : new Vector2(420f, 320f); Size = isTell ? new Vector2(380f, 320f) : new Vector2(420f, 320f);
SizeCondition = ImGuiCond.FirstUseEver; SizeCondition = ImGuiCond.FirstUseEver;
+2 -2
View File
@@ -13,8 +13,8 @@ namespace HellionChat.Ui.Windows;
// Variants of UI elements, drawn side by side so decisions are made by looking // Variants of UI elements, drawn side by side so decisions are made by looking
// rather than by imagining. Reachable with /hellion lab. // rather than by imagining. Reachable with /hellion lab.
// //
// Permanent, by Flo's call: a dev playground for seeing ideas in-game against // Permanent: a playground for seeing ideas in-game against the live theme.
// the live theme. The radios default to whatever shipped, so the window also // The radios default to whatever shipped, so the window also
// documents which variant won and what it beat. // documents which variant won and what it beat.
// //
// It exists because the alternative was drawing mockups, and mockups are what // It exists because the alternative was drawing mockups, and mockups are what
+2 -2
View File
@@ -62,9 +62,9 @@ internal static class AutoTranslate
{ {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
AllEntries(); AllEntries();
// v1.4.9 R3 profiling: Information so the xllog tail surfaces this // v1.4.9 profiling: Information so the xllog tail surfaces this
// without a Debug filter. Belt-and-suspenders for future plugin-load // without a Debug filter. Belt-and-suspenders for future plugin-load
// regressions; remains in place after Sub-Task 3.4 Befund. // regressions.
Plugin.LogProxy.Information( Plugin.LogProxy.Information(
$"Warming up auto-translate took {sw.ElapsedMilliseconds}ms" $"Warming up auto-translate took {sw.ElapsedMilliseconds}ms"
); );
-281
View File
@@ -9,244 +9,6 @@ namespace HellionChat.Util;
internal static class ChunkUtil internal static class ChunkUtil
{ {
// internal static IEnumerable<Chunk> ToChunks(ReadOnlySeString msg, ChunkSource source, ChatType? defaultColour)
// {
// var chunks = new List<Chunk>();
//
// var italic = false;
// var foreground = new Stack<uint>();
// var glow = new Stack<uint>();
// Payload? link = null;
//
// void Append(string text)
// {
// chunks.Add(new TextChunk(source, link, text)
// {
// FallbackColour = defaultColour,
// Foreground = foreground.Count > 0 ? foreground.Peek() : null,
// Glow = glow.Count > 0 ? glow.Peek() : null,
// Italic = italic,
// });
// }
//
// foreach (var payload in msg)
// {
// if (payload.Type == ReadOnlySePayloadType.Text)
// {
// // We don't want to parse any null string
// var str = payload.ToString();
// var nulIndex = str.IndexOf('\0');
// if (nulIndex > 0)
// str = str[..nulIndex];
// if (string.IsNullOrEmpty(str))
// continue;
//
// Append(str);
// continue;
// }
//
// switch (payload.MacroCode)
// {
// case MacroCode.Italic:
// var newStatus = payload.TryGetExpression(out var expression) && expression.TryGetUInt(out var value) && value == 1;
// italic = newStatus;
// break;
// case MacroCode.Color:
// if (payload.TryGetExpression(out var eColor))
// {
// if (eColor.TryGetPlaceholderExpression(out var ph) && ph == (int)ExpressionType.StackColor)
// {
// if (foreground.Count > 0)
// foreground.Pop();
// }
// else if (TryResolveUInt(eColor, out var eColorVal))
// {
// var color = ColourUtil.ArgbToRgba(eColorVal);
//
// if (color > 0)
// foreground.Push(color);
// else if (foreground.Count > 0) // Push the previous color as we don't want invisible text
// foreground.Push(foreground.Peek());
// }
// }
// break;
// case MacroCode.EdgeColor:
// if (payload.TryGetExpression(out eColor))
// {
// if (eColor.TryGetPlaceholderExpression(out var ph) && ph == (int)ExpressionType.StackColor)
// {
// if (glow.Count > 0)
// glow.Pop();
// }
// else if (TryResolveUInt(eColor, out var eColorVal))
// {
// glow.Push(ColourUtil.ArgbToRgba(eColorVal));
// }
// }
// break;
// case MacroCode.ColorType:
// if (!payload.TryGetExpression(out var eColorType) || !eColorType.TryGetUInt(out var eColorTypeVal))
// {
// if (foreground.Count > 0)
// foreground.Pop();
// break;
// }
//
// if (eColorTypeVal == 0)
// {
// if (foreground.Count > 0)
// foreground.Pop();
// }
// else if (Sheets.UIColorSheet.TryGetRow(eColorTypeVal, out var row))
// {
// foreground.Push(row.Dark);
// }
// break;
// case MacroCode.EdgeColorType:
// if (!payload.TryGetExpression(out var eEdgeColor) || !eEdgeColor.TryGetUInt(out var eEdgeColorVal))
// {
// if (glow.Count > 0)
// glow.Pop();
// break;
// }
//
// if (eEdgeColorVal == 0)
// {
// if (glow.Count > 0)
// glow.Pop();
// }
// else if (Sheets.UIColorSheet.TryGetRow(eEdgeColorVal, out var row))
// {
// glow.Push(row.Dark);
// }
// break;
// case MacroCode.Fixed:
// if (!payload.TryGetExpression(out var expr1, out var expr2))
// break;
//
// if (expr1.TryGetUInt(out var group) && expr2.TryGetUInt(out var key))
// {
// chunks.Add(new IconChunk(source, null, BitmapFontIcon.AutoTranslateBegin));
// using var rssb = new RentedSeStringBuilder();
// var translatePayload = rssb.Builder
// .BeginMacro(MacroCode.Fixed)
// .AppendUIntExpression(group - 1)
// .AppendUIntExpression(key)
// .EndMacro()
// .ToReadOnlySeString();
//
// Append(Plugin.Evaluator.Evaluate(translatePayload).ToString());
// chunks.Add(new IconChunk(source, null, BitmapFontIcon.AutoTranslateEnd));
// }
// break;
// case MacroCode.Icon:
// if (payload.TryGetExpression(out var eIcon) && TryResolveInt(eIcon, out var iconVal))
// chunks.Add(new IconChunk(source, link, (BitmapFontIcon)iconVal));
// break;
// case MacroCode.Link:
// if (!payload.TryGetExpression(
// out var linkTypeExpr1,
// out var uintExpr2,
// out var intExpr3,
// out var intExpr4,
// out var strExpr5))
// break;
//
// if (!linkTypeExpr1.TryGetUInt(out var linkType))
// break;
//
// switch ((LinkMacroPayloadType)linkType)
// {
// case LinkMacroPayloadType.Terminator:
// link = null;
// break;
// case LinkMacroPayloadType.MapPosition:
// if (!uintExpr2.TryGetUInt(out var ids))
// break;
//
// if (!intExpr3.TryGetInt(out var rawX))
// break;
//
// if (!intExpr4.TryGetInt(out var rawY))
// break;
//
// var mapId = ids & 0xFF;
// var territoryId = (ids >> 16) & 0xFF;
// break;
// case (LinkMacroPayloadType)Payload.EmbeddedInfoType.DalamudLink - 1:
// if (!uintExpr2.TryGetUInt(out var commandId))
// break;
//
// if (!intExpr3.TryGetInt(out var extra1))
// break;
//
// if (!intExpr4.TryGetInt(out var extra2))
// break;
//
// if (!strExpr5.TryGetString(out var extraStr))
// break;
// break;
// case LinkMacroPayloadType.Quest:
// if (!uintExpr2.TryGetUInt(out var questId))
// break;
// break;
// case LinkMacroPayloadType.Status:
// if (!uintExpr2.TryGetUInt(out var statusId))
// break;
// break;
// case LinkMacroPayloadType.Item:
// if (!uintExpr2.TryGetUInt(out var itemId))
// break;
// break;
// case LinkMacroPayloadType.Character:
// if (!uintExpr2.TryGetUInt(out var flags))
// break;
//
// if (!intExpr3.TryGetUInt(out var worldId))
// break;
// break;
// case LinkMacroPayloadType.PartyFinder:
// if (!uintExpr2.TryGetUInt(out var listingId))
// break;
//
// // intExpr3 is unused
//
// if (!intExpr4.TryGetUInt(out worldId))
// break;
// break;
// case LinkMacroPayloadType.PartyFinderNotification:
// // no expr used
// break;
// case LinkMacroPayloadType.Achievement:
// if (!uintExpr2.TryGetUInt(out var achievementId))
// break;
// break;
// }
// break;
// case MacroCode.NonBreakingSpace:
// Append(" ");
// break;
// case PayloadType.Unknown:
// var rawPayload = (RawPayload)payload;
// else if (rawPayload.Data.Length > 1 && rawPayload.Data[1] == 0x14)
// {
// if (glow.Count > 0)
// {
// glow.Pop();
// }
// else if (rawPayload.Data.Length > 6 && rawPayload.Data[2] == 0x05 && rawPayload.Data[3] == 0xF6)
// {
// var (r, g, b) = (rawPayload.Data[4], rawPayload.Data[5], rawPayload.Data[6]);
// glow.Push(ColourUtil.ComponentsToRgba(r, g, b));
// }
// }
// break;
// }
// }
//
// return chunks;
// }
internal static IEnumerable<Chunk> ToChunks( internal static IEnumerable<Chunk> ToChunks(
SeString msg, SeString msg,
ChunkSource source, ChunkSource source,
@@ -499,47 +261,4 @@ internal static class ChunkUtil
return BitConverter.ToUInt32(numArray, 0); return BitConverter.ToUInt32(numArray, 0);
} }
// private static bool TryResolveUInt(in ReadOnlySeExpressionSpan expression, out uint value)
// {
// if (expression.TryGetUInt(out value))
// return true;
//
// if (expression.TryGetParameterExpression(out var exprType, out var operand1))
// {
// if (!TryResolveUInt(operand1, out var paramIndex))
// return false;
//
// if (paramIndex == 0)
// return false;
//
// paramIndex--;
// if ((ExpressionType)exprType == ExpressionType.GlobalNumber)
// {
// value = (uint) GlobalParametersCache.GetValue((int)paramIndex);
// return true;
// }
// // return (ExpressionType)exprType switch
// // {
// // // ExpressionType.LocalNumber => context.TryGetLNum((int)paramIndex, out value), // lnum
// // ExpressionType.GlobalNumber => (uint) GlobalParametersCache.GetValue((int)paramIndex), // gnum
// // _ => false, // gstr, lstr
// // };
// }
//
// return false;
// }
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// private static bool TryResolveInt(in ReadOnlySeExpressionSpan expression, out int value)
// {
// if (TryResolveUInt(expression, out var u32))
// {
// value = (int)u32;
// return true;
// }
//
// value = 0;
// return false;
// }
} }
+1 -2
View File
@@ -11,8 +11,7 @@ namespace HellionChat.Util;
// a later settings change reformats history too. // a later settings change reformats history too.
// Known trade-off: at non-default settings, this allocates one List<Chunk> per // Known trade-off: at non-default settings, this allocates one List<Chunk> per
// visible message per frame. Lists are small and the path is skipped at the // visible message per frame. Lists are small and the path is skipped at the
// neutral defaults, so GC pressure is low in practice. Accepted; noted in // neutral defaults, so GC pressure is low in practice. Accepted as-is.
// Cycle Notes.
internal static class SenderNameDisplay internal static class SenderNameDisplay
{ {
// Returns a copy of the list with the whole ChunkSource.Sender span // Returns a copy of the list with the whole ChunkSource.Sender span