The first sweep matched a character class that swallowed the digit, so a bare
B1 slipped through while B1-2 was caught. Searching the whole A-Z space instead
of guessing prefixes turned up 130-odd more: B0 through B6, C2, C3, D1, H2, M6,
P7, P8, T2, W2, plus GP-04, KB-01, OD-1, PM-1, PM-3, SEC-01, TR-4, TR-7, UI-11,
UI-12, XC-8 and API-3.
Kept deliberately: 41 B4 01 is a byte signature, "N0" a format string,
#L119-L128 a source anchor, LS4/LS6 are linkshells, and A=FF B=0C G=41 R=C2
explains a colour-channel order. Those look like codes and are not.
Also translated the eight German comments left in the theme files and
ImGuiUtil. Seven of them described what a palette does to which channel, which
is worth reading -- just not in a second language in an otherwise English
codebase.
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.
InputPreview was only rendered for PreviewPosition.Top/Bottom (the
DrawConditions IsWindowMode gate). Inside-mode (the default) and
Tooltip-mode had no caller at all because v1.5.6's inline-render path
lived on the deleted ChatLogWindow and was not migrated to the v1.7.0
Components-Layer.
Wire Inside-mode by calling CalculatePreviewHeight + DrawPreview
inline from MainWindow.DrawMainArea between the message-list child
and the input bar, with the message-list height reserved for the
preview block. Wire Tooltip-mode by sampling IsItemHovered() on the
input text widget inside InputBar.DrawInputField (analog to the
existing _isFocused = ImGui.IsItemFocused() idiom on the same line)
and exposing it as WasInputTextHovered; MainWindow opens the tooltip
after _input.Draw when both the hover-flag and PreviewPosition.Tooltip
are active.
Plan-drift acknowledged: the plan stated Plugin.InputPreview is
statically reachable, but the property was declared as an instance
member on Plugin.cs:101. Hoisted to internal static to match the
plan's intention (analog to Plugin.Config); updated the single
external instance-access site in PluginLifecycle.RegisterWindows
to the type-qualified form.
Verified in-game: Inside-mode preview block appears between message
list and input bar on first keystroke; tooltip-mode shows preview on
text-field hover only; Top/Bottom-mode unchanged; empty buffer hides
the preview in all modes. dotnet build clean, dotnet csharpier check
clean.
I resurrects InputPreview as a fully ctor-injected window:
- Class header public → internal sealed (Components-Layer style); Plugin.cs
property visibility corrected to internal to match
- Ctor takes 5 DI deps (ChunkRenderer, Lender<PayloadHandler>, MainWindow,
InputBar, ILogger) via Factory-Lambda DI-reg
- Window-hook split: PreOpenCheck() owns the state (Drawing/PreviewMessage/
HasEvaluation/PreviewHeight/LastLength), PreDraw() owns position/size
computation. Matches v1.5.6's split — avoids wasted position-math when
Window isn't drawn (DrawConditions gates on IsDrawable getter).
- Framework.Update subscribe/unsubscribe removed (PreOpenCheck runs per
draw-frame, same cadence as Framework.Update for our needs)
- Draw() borrows fresh PayloadHandler per-frame from Lender for popup
isolation (preview hover doesn't bleed into log)
- Defensive ResetCounter fallback when MainWindow closed + InputPreview
open — primary path is A2's MainWindow.Draw() ResetCounter
R2/R3 (J/K) next. A2 closes out the Lender DI-cycle for MainWindow.
The legacy ChatLogWindow.cs and its tightly coupled neighbours are gone:
PayloadHandler, Popout, ChatInputBar, AutoCompleteInfo, AutoTellTabTint,
the three tab-icon helpers, the old Ui/StatusBar and Ui/SymbolPicker
behind the components-layer replacements, HellionStyle + helpers, the
CompactInputSubmitter test mirror and the QuickPickerSelfTestStep. The
new component layer (MainWindow + the five components + GlobalStyleScope)
now drives the whole chat surface.
InputPreview, CommandHelpWindow and Debugger lose their ChatLogWindow
backref. The first two are skeleton windows for now — DrawConditions
always returns false until the new chat layer exposes equivalent state.
Debugger keeps the current-tab and vanilla-chat blocks; the payload
counters are explicitly marked offline. DbViewer renders Sender/Content
columns as plain TextValue strings instead of the removed DrawChunks.
GameFunctions.Chat and GameFunctions.KeybindManager keep the hook
plumbing intact but mark every ChatLogWindow.Activated /
ChangeTabDelta / TellSpecial site as offline so the FFXIV-side
integration still compiles and runs without an Activated entry point.
TypingIpc.BuildState reports the IPC state as not-typing / not-focused
until the new chat layer surfaces real focus and buffer state again.
Plugin.cs Draw uses StyleEngine.GlobalStyleScope.Push for the per-frame
theme push and stops calling BeginFrame / FinalizeFrame / HideStateCheck
/ DefaultText through the dead window. ImGuiUtil drops PostPayload +
WrapText + the surrounding word-wrap pipeline. PluginHostFactory and
PluginLifecycle drop the legacy DI singletons and AddWindow entries.
Build is clean and csharpier is clean across the trimmed 131-file tree.
Add .prettierrc.json, .markdownlint.json, .yamllint.yaml, .gitattributes
Run CSharpier, Prettier and markdownlint across the entire codebase.
No logic changes — formatting, using order and line endings only.
General code-quality and robustness pass across the plugin: thread-
safety on IPC state, resource-disposal cleanups, input validation,
defensive null-checks and a few small UX glitches. Compliance docs
(THIRD_PARTY_NOTICES, PRIVACY, COPYRIGHT) refreshed to v1.0.3.
Highlights
- ExtraChat IPC state synchronised across threads
- ChatLogWindow autocomplete no longer leaks the unmanaged
ImGuiListClipper allocation
- ChatLogWindow + Popout style stack stays balanced when config
toggles mid-frame
- Retention sweep and privacy cleanup wait for the actual filter
pass instead of the fire-and-forget Task that started it
- Configuration.LatestVersion bumped to 13 to match the active
migration path
- GameFunctions placeholder buffer guarded against oversized
replacement names
- TellTarget.IsSet, ResolveTempInputChannel, InputPreview, IconUtil,
Lender, Payloads, ExtraPayload all hardened against null / empty /
EOF / cycle inputs
- FontManager Lodestone download stays in scope for a follow-up
(timeout + lazy init pending)
- AutoTranslate replaced the msvcrt.dll memcmp P/Invoke with a
managed Span comparison
- Privacy cleanup worker thread marked IsBackground = true
- Database cleanup now removes both legacy files in one click
- Tell-target name redacted in the verbose debug log
Compliance
- THIRD_PARTY_NOTICES: last-reviewed bumped to v1.0.3, Pidgin 3.5.1,
SQLitePCLRaw.lib.e_sqlite3 3.50.3 listed as direct dependency with
CVE-2025-6965 / CVE-2025-7709 rationale
- PRIVACY: last-reviewed bumped to v1.0.3, BetterTTV trigger wording
clarified (list fetch at startup vs. on-demand image fetch)
- COPYRIGHT: upstream attribution range widened
Build: 0 warnings, 0 errors. No behavioural changes that would alter
existing user configuration or stored chat history.