From c5fe69f0d341c7ee88cd766a6adfa020ff0815ba Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 12 May 2026 21:28:16 +0200 Subject: [PATCH] feat(themes): swap Moonlit Bloom for Crystal Nocturne, sort built-ins by colour family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Crystal Nocturne (royal sapphire + electric magenta on obsidian, by CRYSTALLITE) replaces Moonlit Bloom in the built-in roster. The same chat-channel tinting convention applies: sapphire-blue identity on party/team channels, accent-magenta on tells, and an alternating mint/yellow/peach palette across the eight linkshell slots so each LS stays individually distinguishable on the dark obsidian background. Users who had Moonlit Bloom selected fall back to the default Hellion Arctic on the first plugin load. A custom JSON copy of Moonlit Bloom dropped into pluginConfigs/HellionChat/themes/ keeps working as a user theme. Plus a cosmetic re-sort of the registry: insertion order now drives a deliberate Theme-Picker grid layout (3 columns) — blue family in row 1, purple to magenta in row 2, green/warm/classic in row 3, Synthwave Sunset alone in row 4 as a retro bonus. --- .github/forge-posts/v1.4.6.md | 1 + HellionChat/HellionChat.yaml | 4 + HellionChat/Themes/Builtin/CrystalNocturne.cs | 82 +++++++++++++++++++ HellionChat/Themes/Builtin/MoonlitBloom.cs | 80 ------------------ HellionChat/Themes/ThemeRegistry.cs | 12 ++- README.md | 2 +- docs/CHANGELOG.md | 4 + docs/THEME-AUTHORING.md | 2 +- repo.json | 2 +- 9 files changed, 102 insertions(+), 87 deletions(-) create mode 100644 HellionChat/Themes/Builtin/CrystalNocturne.cs delete mode 100644 HellionChat/Themes/Builtin/MoonlitBloom.cs diff --git a/.github/forge-posts/v1.4.6.md b/.github/forge-posts/v1.4.6.md index 040e7b4..0285ae2 100644 --- a/.github/forge-posts/v1.4.6.md +++ b/.github/forge-posts/v1.4.6.md @@ -13,5 +13,6 @@ Wartungs-Patch ohne User-sichtbare Änderungen. Saubere Code-Basis als Vorbereit - **Aktive-Tab-Underline pixel-perfect bei DPI-Scaling**: Die Underline-Pill skaliert jetzt mit `ImGuiHelpers.GlobalScale` und rundet die DrawList-Koordinaten auf physische Pixel. Kein Sub-Pixel-Blur mehr auf 125/150%-Setups. - **IconButton-Width-Fix**: der manuelle `width - 2 * CellPadding.X`-Subtract verlor den HUD-Scale (Padding skaliert, der raw int nicht). Gemessene Breite läuft jetzt unverändert durch. - **Test-Isolation für MessageStore**: `Dalamud.Utility.Util`-Surface (IsWine, OpenLink) läuft jetzt durch eine `IPlatformUtil`-Indirektion. MessageStores `IsWine`-Probe ist isoliert testbar in der Build-Suite. Plus: HellionStyle-ChildBgAlpha als Pure-Helper extrahiert, Plugin.SaveConfig kopiert nur Session-Tabs statt der ganzen Tab-Liste, SettingsOverview cached den DrawList einmal pro Frame. +- **Built-in-Theme-Roster**: Crystal Nocturne (Royal Sapphire + Electric Magenta auf Obsidian, von CRYSTALLITE) ersetzt Moonlit Bloom. User mit Moonlit Bloom als aktivem Theme fallen beim ersten Plugin-Load auf Hellion Arctic zurück. Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2). diff --git a/HellionChat/HellionChat.yaml b/HellionChat/HellionChat.yaml index db7db94..549b301 100755 --- a/HellionChat/HellionChat.yaml +++ b/HellionChat/HellionChat.yaml @@ -65,6 +65,10 @@ changelog: |- Dalamud.Utility.Util surface routed through an IPlatformUtil indirection (MessageStore IsWine probe is now testable in isolation) + - Built-in themes: Crystal Nocturne (sapphire and electric + magenta over obsidian, by CRYSTALLITE) replaces Moonlit Bloom. + Users with Moonlit Bloom selected fall back to Hellion Arctic + on first load Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2). diff --git a/HellionChat/Themes/Builtin/CrystalNocturne.cs b/HellionChat/Themes/Builtin/CrystalNocturne.cs new file mode 100644 index 0000000..ad7cf5f --- /dev/null +++ b/HellionChat/Themes/Builtin/CrystalNocturne.cs @@ -0,0 +1,82 @@ +using HellionChat.Util; + +namespace HellionChat.Themes.Builtin; + +internal static class CrystalNocturne +{ + public const string Slug = "crystal-nocturne"; + + public static Theme Build() => + new( + Slug: Slug, + Name: "Crystal Nocturne", + Author: "CRYSTALLITE", + Description: "Royal sapphire and electric magenta over obsidian — a nocturne for the crystal-lit dance floor.", + Colors: new ThemeColors( + PrimaryDark: ColourUtil.HexToRgba("#1D4ED8"), + Primary: ColourUtil.HexToRgba("#3B82F6"), + PrimaryLight: ColourUtil.HexToRgba("#93C5FD"), + PrimaryGlow: ColourUtil.HexToRgba("#3B82F699"), + AccentDark: ColourUtil.HexToRgba("#A21CAF"), + Accent: ColourUtil.HexToRgba("#D946EF"), + AccentLight: ColourUtil.HexToRgba("#F0ABFC"), + Identity: ColourUtil.HexToRgba("#3B82F6"), + WindowBg: ColourUtil.HexToRgba("#08070F"), + ChildBg: ColourUtil.HexToRgba("#11101F"), + FrameBg: ColourUtil.HexToRgba("#1C1A33"), + Surface: ColourUtil.HexToRgba("#262340"), + SurfaceHover: ColourUtil.HexToRgba("#332D55"), + Border: ColourUtil.HexToRgba("#D946EF55"), + TextPrimary: ColourUtil.HexToRgba("#F5F3FF"), + TextMuted: ColourUtil.HexToRgba("#A5A0C0"), + TextDim: ColourUtil.HexToRgba("#4B4763"), + StatusSuccess: ColourUtil.HexToRgba("#10B981"), + StatusDanger: ColourUtil.HexToRgba("#F43F5E"), + StatusWarning: ColourUtil.HexToRgba("#FACC15"), + StatusInfo: ColourUtil.HexToRgba("#3B82F6") + ), + Layout: new ThemeLayout( + WindowRounding: 2f, + ChildRounding: 1f, + PopupRounding: 2f, + FrameRounding: 1f, + GrabRounding: 1f, + TabRounding: 1f, + ScrollbarRounding: 2f, + WindowBorderSize: 1f, + FrameBorderSize: 1f + ), + Typography: new ThemeTypography(), + IsBuiltIn: true, + ChatColors: new ThemeChatColors( + new Dictionary + { + // Crystal Nocturne — sapphire-blue identity for party/team channels, + // accent-magenta for tells, with mint/peach accents on linkshells + // so the eight LS slots stay individually distinguishable on the + // dark obsidian background. + [HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#F5F3FF"), + [HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#FACC15"), + [HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#F0B090"), + [HellionChat.Code.ChatType.TellIncoming] = ColourUtil.HexToRgba("#F0ABFC"), + [HellionChat.Code.ChatType.TellOutgoing] = ColourUtil.HexToRgba("#F0ABFC"), + [HellionChat.Code.ChatType.Party] = ColourUtil.HexToRgba("#93C5FD"), + [HellionChat.Code.ChatType.Alliance] = ColourUtil.HexToRgba("#F0B090"), + [HellionChat.Code.ChatType.FreeCompany] = ColourUtil.HexToRgba("#A8C8E8"), + [HellionChat.Code.ChatType.NoviceNetwork] = ColourUtil.HexToRgba("#10B981"), + [HellionChat.Code.ChatType.CrossParty] = ColourUtil.HexToRgba("#93C5FD"), + [HellionChat.Code.ChatType.Linkshell1] = ColourUtil.HexToRgba("#10B981"), + [HellionChat.Code.ChatType.Linkshell2] = ColourUtil.HexToRgba("#FACC15"), + [HellionChat.Code.ChatType.Linkshell3] = ColourUtil.HexToRgba("#F0ABFC"), + [HellionChat.Code.ChatType.Linkshell4] = ColourUtil.HexToRgba("#93C5FD"), + [HellionChat.Code.ChatType.Linkshell5] = ColourUtil.HexToRgba("#F0B090"), + [HellionChat.Code.ChatType.Linkshell6] = ColourUtil.HexToRgba("#A5A0C0"), + [HellionChat.Code.ChatType.Linkshell7] = ColourUtil.HexToRgba("#D946EF"), + [HellionChat.Code.ChatType.Linkshell8] = ColourUtil.HexToRgba("#3B82F6"), + [HellionChat.Code.ChatType.CustomEmote] = ColourUtil.HexToRgba("#F0B090"), + [HellionChat.Code.ChatType.StandardEmote] = ColourUtil.HexToRgba("#F0B090"), + [HellionChat.Code.ChatType.Echo] = ColourUtil.HexToRgba("#A5A0C0"), + } + ) + ); +} diff --git a/HellionChat/Themes/Builtin/MoonlitBloom.cs b/HellionChat/Themes/Builtin/MoonlitBloom.cs deleted file mode 100644 index 544bb02..0000000 --- a/HellionChat/Themes/Builtin/MoonlitBloom.cs +++ /dev/null @@ -1,80 +0,0 @@ -using HellionChat.Util; - -namespace HellionChat.Themes.Builtin; - -internal static class MoonlitBloom -{ - public const string Slug = "moonlit-bloom"; - - public static Theme Build() => - new( - Slug: Slug, - Name: "Moonlit Bloom", - Author: "Hellion Forge", - Description: "Bloom Magenta + Soft Sage auf Deep Violet Night.", - Colors: new ThemeColors( - PrimaryDark: ColourUtil.HexToRgba("#C957D0"), - Primary: ColourUtil.HexToRgba("#E374E8"), - PrimaryLight: ColourUtil.HexToRgba("#EF8AF4"), - PrimaryGlow: ColourUtil.HexToRgba("#E374E899"), - AccentDark: ColourUtil.HexToRgba("#7AAC5C"), - Accent: ColourUtil.HexToRgba("#9CCB7C"), - AccentLight: ColourUtil.HexToRgba("#B6E297"), - Identity: ColourUtil.HexToRgba("#E374E8"), - WindowBg: ColourUtil.HexToRgba("#0E0C1F"), - ChildBg: ColourUtil.HexToRgba("#15122B"), - FrameBg: ColourUtil.HexToRgba("#1F1A38"), - Surface: ColourUtil.HexToRgba("#28224A"), - SurfaceHover: ColourUtil.HexToRgba("#332B5B"), - Border: ColourUtil.HexToRgba("#E374E844"), - TextPrimary: ColourUtil.HexToRgba("#ECE6F5"), - TextMuted: ColourUtil.HexToRgba("#9A8BB0"), - TextDim: ColourUtil.HexToRgba("#554B6E"), - StatusSuccess: ColourUtil.HexToRgba("#7AAC5C"), - StatusDanger: ColourUtil.HexToRgba("#E85C6A"), - StatusWarning: ColourUtil.HexToRgba("#E8B590"), - StatusInfo: ColourUtil.HexToRgba("#6278FF") - ), - Layout: new ThemeLayout( - WindowRounding: 6f, - ChildRounding: 5f, - PopupRounding: 5f, - FrameRounding: 4f, - GrabRounding: 4f, - TabRounding: 4f, - ScrollbarRounding: 4f, - WindowBorderSize: 1f, - FrameBorderSize: 1f - ), - Typography: new ThemeTypography(), - IsBuiltIn: true, - ChatColors: new ThemeChatColors( - new Dictionary - { - // Moonlit Bloom — Bloom-Magenta-Tönung. Sage-Drift in NoviceNetwork - // und Linkshell4. Tell-Pink-Identität bleibt sichtbar. - [HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#ECE6F5"), - [HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#F0D080"), - [HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#F09A60"), - [HellionChat.Code.ChatType.TellIncoming] = ColourUtil.HexToRgba("#EF8AF4"), - [HellionChat.Code.ChatType.TellOutgoing] = ColourUtil.HexToRgba("#EF8AF4"), - [HellionChat.Code.ChatType.Party] = ColourUtil.HexToRgba("#A0B0F0"), - [HellionChat.Code.ChatType.Alliance] = ColourUtil.HexToRgba("#F0B090"), - [HellionChat.Code.ChatType.FreeCompany] = ColourUtil.HexToRgba("#A8C8E8"), - [HellionChat.Code.ChatType.NoviceNetwork] = ColourUtil.HexToRgba("#9CCB7C"), - [HellionChat.Code.ChatType.CrossParty] = ColourUtil.HexToRgba("#A0B0F0"), - [HellionChat.Code.ChatType.Linkshell1] = ColourUtil.HexToRgba("#9CCB7C"), - [HellionChat.Code.ChatType.Linkshell2] = ColourUtil.HexToRgba("#F0BC92"), - [HellionChat.Code.ChatType.Linkshell3] = ColourUtil.HexToRgba("#F0D080"), - [HellionChat.Code.ChatType.Linkshell4] = ColourUtil.HexToRgba("#B6E297"), - [HellionChat.Code.ChatType.Linkshell5] = ColourUtil.HexToRgba("#A0B0F0"), - [HellionChat.Code.ChatType.Linkshell6] = ColourUtil.HexToRgba("#C098D8"), - [HellionChat.Code.ChatType.Linkshell7] = ColourUtil.HexToRgba("#EF8AF4"), - [HellionChat.Code.ChatType.Linkshell8] = ColourUtil.HexToRgba("#E8B0E8"), - [HellionChat.Code.ChatType.CustomEmote] = ColourUtil.HexToRgba("#E8B590"), - [HellionChat.Code.ChatType.StandardEmote] = ColourUtil.HexToRgba("#E8B590"), - [HellionChat.Code.ChatType.Echo] = ColourUtil.HexToRgba("#9A8BB0"), - } - ) - ); -} diff --git a/HellionChat/Themes/ThemeRegistry.cs b/HellionChat/Themes/ThemeRegistry.cs index ac26dd7..53b4610 100644 --- a/HellionChat/Themes/ThemeRegistry.cs +++ b/HellionChat/Themes/ThemeRegistry.cs @@ -15,17 +15,21 @@ public sealed class ThemeRegistry public ThemeRegistry(string? customThemesDir = null) { + // Insertion order drives the Theme-Picker grid layout (3 columns). + // Row 1: blue family. Row 2: purple to magenta family. + // Row 3: green / warm / classic. Row 4: Synthwave Sunset as a + // retro bonus on its own line. _builtIns = new Dictionary(StringComparer.OrdinalIgnoreCase) { { HellionArctic.Slug, HellionArctic.Build() }, { HellionSpectrum.Slug, HellionSpectrum.Build() }, - { Chat2Classic.Slug, Chat2Classic.Build() }, - { EventHorizon.Slug, EventHorizon.Build() }, - { MoonlitBloom.Slug, MoonlitBloom.Build() }, { NightBlue.Slug, NightBlue.Build() }, + { EventHorizon.Slug, EventHorizon.Build() }, { IndigoViolet.Slug, IndigoViolet.Build() }, - { ForgeMerchantman.Slug, ForgeMerchantman.Build() }, + { CrystalNocturne.Slug, CrystalNocturne.Build() }, { MintGrove.Slug, MintGrove.Build() }, + { ForgeMerchantman.Slug, ForgeMerchantman.Build() }, + { Chat2Classic.Slug, Chat2Classic.Build() }, { SynthwaveSunset.Slug, SynthwaveSunset.Build() }, }; diff --git a/README.md b/README.md index 7ddf9cc..46ddd17 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Hellion Chat is developed under **Hellion Forge**, the specialized modding and p #### Custom Themes (v1.1.0) HellionChat ships a theme engine with ten built-in themes (Hellion Arctic, Hellion Spectrum, Chat 2 Classic, Event -Horizon, Moonlit Bloom, Mint Grove, Night Blue, Indigo Violet, Forge Merchantman, Synthwave Sunset) and a JSON-based +Horizon, Crystal Nocturne, Mint Grove, Night Blue, Indigo Violet, Forge Merchantman, Synthwave Sunset) and a JSON-based authoring format for custom themes. Schema and step-by-step guide in [`docs/THEME-AUTHORING.md`](docs/THEME-AUTHORING.md). Hellion Spectrum is Deuteranopia/Protanopia-safe (red-green color blindness) based on the Wong/Okabe-Ito palette. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b4b70ea..2374956 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -47,6 +47,10 @@ upstream-inherited bugs from ChatTwo `f35b7d3`, and prepares the code for the v1 indirection. `MessageStore`'s `IsWine` probe is now reachable from the xUnit AppDomain via a `FakePlatformUtil` fixture (full isolated MessageStore construction still pending — `Plugin.Log.Information` in `Migrate0` is a separate Dalamud-static surface, slated for v1.4.7) +- Built-in themes: Crystal Nocturne (royal sapphire and electric magenta over obsidian, by CRYSTALLITE) replaces + Moonlit Bloom in the built-in roster. Users who had Moonlit Bloom selected fall back to the default Hellion Arctic + on the first plugin load; an existing custom JSON copy of Moonlit Bloom under `pluginConfigs/HellionChat/themes/` + keeps working unchanged Modding & support: join Hellion Forge — diff --git a/docs/THEME-AUTHORING.md b/docs/THEME-AUTHORING.md index 9fcee0c..5520b67 100644 --- a/docs/THEME-AUTHORING.md +++ b/docs/THEME-AUTHORING.md @@ -141,7 +141,7 @@ A theme can tint these toward its brand family (e.g., a purple theme can shift T **don't** flip them (Tell suddenly green, Yell suddenly cyan). RP groups and combat-spec setups depend on the visual hierarchy. -The eight colored built-in themes (Hellion Arctic, Hellion Spectrum, Event Horizon, Moonlit Bloom, Mint Grove, Night +The eight colored built-in themes (Hellion Arctic, Hellion Spectrum, Event Horizon, Crystal Nocturne, Mint Grove, Night Blue, Indigo Violet, Forge Merchantman) all follow this rule — read their source for reference. Chat 2 Klassik intentionally ships without `chatChannels` so the user keeps their existing picks. diff --git a/repo.json b/repo.json index 6910163..81b8df0 100644 --- a/repo.json +++ b/repo.json @@ -14,7 +14,7 @@ "CanUnloadAsync": false, "LoadPriority": 0, "Punchline": "A Hellion Forge plugin. Privacy-first chat for FFXIV, built to stay out of your way.", - "Changelog": "**v1.4.6 — Code Hygiene and Refactor (2026-05-12)**\n\nMaintenance patch. No user-visible behaviour changes; tightens the development feedback loop, fixes two upstream-inherited bugs, and prepares the code for the v1.4.7 backlog cleanup.\n\n- preflight.sh gains a csharpier reflow check and a markdownlint pass so style drift and markdown violations are caught at the pre-push gate\n- FontManager fallback catches the full set of atlas-toolkit throws (IO, InvalidOperation, ArgumentException) — a corrupt font config no longer takes down the whole atlas build\n- BrandingLinks and IntegrationLinks URLs validated on plugin load — a typo in a future URL rotation now throws at startup\n- Cherry-picked from ChatTwo upstream f35b7d3: Chat.SetChannel no longer leaks the native Utf8String when the linkshell check rejects the channel\n- Cherry-picked from ChatTwo upstream f35b7d3: Tab.Clone now deep-clones UsedChannel and TellTarget — PopOut and Temp tabs no longer mutate each other's channel state\n- Active-tab underline scales with DPI and rounds to physical pixels for crisp rendering above 100% scaling\n- IconButton width parameter no longer subtracts HUD-scaled padding from a raw int (measured width passes through verbatim)\n- Internal: HellionStyle ChildBgAlpha extracted to a testable helper; Plugin.SaveConfig clones only the temp tabs; SettingsOverview caches the draw-list per frame; Dalamud.Utility.Util surface routed through an IPlatformUtil indirection (MessageStore IsWine probe is now testable in isolation)\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n---\n\n**v1.4.5 — UX and Robustness (2026-05-12)**\n\nSixth sub-patch of the v1.4.x polish-sweep series. Chat-log draw failures surface as a notification, the first-run wizard has an explicit Later option, the input history clears on plugin reload, and the status bar version slot stops clipping in narrow windows.\n\n- Chat window draw errors now show a one-shot notification instead of failing silently — stack trace stays in /xllog\n- First-run wizard: explicit \"Later — keep defaults\" button. Closing the X no longer silently accepts the defaults; the wizard reopens on the next plugin load if nothing was picked\n- InputHistoryService clears on plugin dispose so the previous session's typed commands don't bleed into the next load\n- Status bar hides the version slot when the chat window is too narrow to fit all five slots without overlap\n- Internal: explicit session-only Auto-Tell-Tab invariant in Plugin.cs plus a pinning test in the Build-Suite\n- Internal: FontManager falls back to the system font if the embedded Hellion font resource is missing — logs a Warning\n\n---\n\n**v1.4.4 — Threading and IPC safety polish (2026-05-12)**\n\nFifth sub-patch of the v1.4.x polish-sweep series. Threading assumptions are documented per-method, a hot-path lock falls away, and the privacy filter speaks up when an unknown ChatType shows up.\n\n- AutoTellTabs hot-path getter uses an Interlocked counter instead of taking the lock on every read\n- Honorific integration: per-method threading banners, plus Warning-level log on unsubscribe failure\n- AutoTranslate warmup thread marked IsBackground so plugin unload doesn't wait for it\n- PrivacyFilter logs once per unknown ChatType so a future patch's added channel doesn't drop off the radar\n- New installs persist unknown channels by default; existing configs keep their explicit choice\n\n---\n\n**v1.4.3 — Faster plugin load + new repo (2026-05-08)**\n\nHeavy startup work (migrations, hooks, windows) now runs async so Dalamud's UI stays responsive during load. Load time is comparable to v1.4.2 — this is the foundation for v1.4.4 optimisations.\n\n- Two-phase async load via IAsyncDalamudPlugin\n- Schema-gate replaces the v9→v16 migration chain; old configs require a v1.4.2 install first\n- AutoTranslate cache loads on first use instead of every startup\n- Custom font (Hellion-Exo2) appears with a brief pop after load\n- Repo moved to gitea.hellion-forge.cloud — update your custom-repo URL\n\n---\n\nFull history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases", + "Changelog": "**v1.4.6 — Code Hygiene and Refactor (2026-05-12)**\n\nMaintenance patch. No user-visible behaviour changes; tightens the development feedback loop, fixes two upstream-inherited bugs, and prepares the code for the v1.4.7 backlog cleanup.\n\n- preflight.sh gains a csharpier reflow check and a markdownlint pass so style drift and markdown violations are caught at the pre-push gate\n- FontManager fallback catches the full set of atlas-toolkit throws (IO, InvalidOperation, ArgumentException) — a corrupt font config no longer takes down the whole atlas build\n- BrandingLinks and IntegrationLinks URLs validated on plugin load — a typo in a future URL rotation now throws at startup\n- Cherry-picked from ChatTwo upstream f35b7d3: Chat.SetChannel no longer leaks the native Utf8String when the linkshell check rejects the channel\n- Cherry-picked from ChatTwo upstream f35b7d3: Tab.Clone now deep-clones UsedChannel and TellTarget — PopOut and Temp tabs no longer mutate each other's channel state\n- Active-tab underline scales with DPI and rounds to physical pixels for crisp rendering above 100% scaling\n- IconButton width parameter no longer subtracts HUD-scaled padding from a raw int (measured width passes through verbatim)\n- Internal: HellionStyle ChildBgAlpha extracted to a testable helper; Plugin.SaveConfig clones only the temp tabs; SettingsOverview caches the draw-list per frame; Dalamud.Utility.Util surface routed through an IPlatformUtil indirection (MessageStore IsWine probe is now testable in isolation)\n- Built-in themes: Crystal Nocturne (sapphire and electric magenta over obsidian, by CRYSTALLITE) replaces Moonlit Bloom. Users with Moonlit Bloom selected fall back to Hellion Arctic on first load\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n---\n\n**v1.4.5 — UX and Robustness (2026-05-12)**\n\nSixth sub-patch of the v1.4.x polish-sweep series. Chat-log draw failures surface as a notification, the first-run wizard has an explicit Later option, the input history clears on plugin reload, and the status bar version slot stops clipping in narrow windows.\n\n- Chat window draw errors now show a one-shot notification instead of failing silently — stack trace stays in /xllog\n- First-run wizard: explicit \"Later — keep defaults\" button. Closing the X no longer silently accepts the defaults; the wizard reopens on the next plugin load if nothing was picked\n- InputHistoryService clears on plugin dispose so the previous session's typed commands don't bleed into the next load\n- Status bar hides the version slot when the chat window is too narrow to fit all five slots without overlap\n- Internal: explicit session-only Auto-Tell-Tab invariant in Plugin.cs plus a pinning test in the Build-Suite\n- Internal: FontManager falls back to the system font if the embedded Hellion font resource is missing — logs a Warning\n\n---\n\n**v1.4.4 — Threading and IPC safety polish (2026-05-12)**\n\nFifth sub-patch of the v1.4.x polish-sweep series. Threading assumptions are documented per-method, a hot-path lock falls away, and the privacy filter speaks up when an unknown ChatType shows up.\n\n- AutoTellTabs hot-path getter uses an Interlocked counter instead of taking the lock on every read\n- Honorific integration: per-method threading banners, plus Warning-level log on unsubscribe failure\n- AutoTranslate warmup thread marked IsBackground so plugin unload doesn't wait for it\n- PrivacyFilter logs once per unknown ChatType so a future patch's added channel doesn't drop off the radar\n- New installs persist unknown channels by default; existing configs keep their explicit choice\n\n---\n\n**v1.4.3 — Faster plugin load + new repo (2026-05-08)**\n\nHeavy startup work (migrations, hooks, windows) now runs async so Dalamud's UI stays responsive during load. Load time is comparable to v1.4.2 — this is the foundation for v1.4.4 optimisations.\n\n- Two-phase async load via IAsyncDalamudPlugin\n- Schema-gate replaces the v9→v16 migration chain; old configs require a v1.4.2 install first\n- AutoTranslate cache loads on first use instead of every startup\n- Custom font (Hellion-Exo2) appears with a brief pop after load\n- Repo moved to gitea.hellion-forge.cloud — update your custom-repo URL\n\n---\n\nFull history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases", "AcceptsFeedback": true, "DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.4.6/latest.zip", "DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.4.6/latest.zip",