diff --git a/.github/forge-posts/v1.4.7.md b/.github/forge-posts/v1.4.7.md
new file mode 100644
index 0000000..24f62df
--- /dev/null
+++ b/.github/forge-posts/v1.4.7.md
@@ -0,0 +1,29 @@
+---
+subtitle: Backlog Cleanup and Mid-Features
+versionsnatur: Mid-Feature-Patch
+---
+
+Achter Sub-Patch der v1.4.x Polish-Sweep-Serie. Erstes User-sichtbares Feature-Bundle seit v1.4.5 — angepinnte Tell-Tabs
+die Relog überleben, opt-in Honorific-Glow, plus eine konfigurierbare Sidebar.
+
+- **TempTell anpinnen**: Rechtsklick auf einen TempTell-Tab in der Sidebar → „Tab anpinnen". Angepinnte Tabs überleben
+ Plugin-Reload und Char-Logout, behalten ihre Konversations-Historie (wird beim Rehydrate aus dem MessageStore
+ nachgeladen) und bleiben an die gleiche /tell-Person gebunden. Hard-Cap 5 angepinnte Tabs in einem separaten Pool —
+ die normalen Auto-Tell-Tabs (15er Cap) sind davon entkoppelt, Gesamt-Decke 20. Die Sidebar gruppiert angepinnte Tabs
+ in einer eigenen „Angepinnt"-Sektion mit eigenem Trenner.
+- **Honorific Glow-Outline**: rendert jetzt eine 8-Richtungs-DrawList-Outline wenn der Honorific-Titel eine Glow-Farbe
+ trägt. Opt-in via **Settings → Integrationen → Glow-Outline rendern (Honorific)** (Default OFF). Gradient (Color3 /
+ GradientColourSet / Wave / Pulse) wird geparst und im DTO weitergereicht, rendert aktuell aber statisch als
+ Primärfarbe — der volle Gradient-Port (Animations-Algorithmus + Pride-Palette) kommt als eigener Cycle nach.
+- **Sidebar-Breite konfigurierbar**: in **Theme & Layout** ein Slider 44–160 px. Default bleibt 44 px (icon-only), aber
+ breiter machen damit Sektion-Header wie „Aktive Tells (3)" oder „Angepinnt (2)" nicht abgeschnitten werden.
+- **Settings-Save Channel-Fix**: ein Save mit aktivem Party- oder Linkshell-Tab konnte den Chat-Input zurück auf
+ `/tell ` springen lassen. `Configuration.UpdateFrom` bewahrt jetzt den Runtime-`CurrentChannel`
+ über den persistent-Tab-Merge hinweg, und `TabSwitched` deep-cloned den Seed-Channel statt sich den `UsedChannel` mit
+ dem vorigen Tab zu teilen.
+- **Internal**: `IPluginLogProxy`-Indirektion vor Dalamud's `IPluginLog` über alle ~91 `Plugin.Log`-Call-Sites. Damit
+ läuft `MessageStore.Migrate0` voll-isoliert in xUnit (F12.1-Lücke aus v1.4.6 geschlossen). Plus: TempTab-Counter als
+ abgeleitete Property statt gecachtes Interlocked-Feld — die neuen Pin/Unpin-Übergänge sind Cold-Path, kein
+ Lock-Free-Vorteil mehr. Migration v16 → v17 ist rein additiv (neues `Tab.IsPinned`-Bool, Default false).
+
+Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
diff --git a/HellionChat/AutoTellTabsService.cs b/HellionChat/AutoTellTabsService.cs
index 4ad5057..d354bac 100644
--- a/HellionChat/AutoTellTabsService.cs
+++ b/HellionChat/AutoTellTabsService.cs
@@ -67,7 +67,7 @@ internal sealed class AutoTellTabsService : IDisposable
private void RehydratePinnedTabs()
{
var pinned = Plugin.Config.Tabs.Count(TabLifecycleHelpers.IsInPinnedPool);
- Plugin.LogProxy.Info($"[Pin] Rehydrate scan: {pinned} pinned tab(s) found");
+ Plugin.LogProxy.Debug($"[Pin] Rehydrate scan: {pinned} pinned tab(s) found");
foreach (var tab in Plugin.Config.Tabs)
{
@@ -93,7 +93,7 @@ internal sealed class AutoTellTabsService : IDisposable
// sees the recent conversation, not a blank tab.
PreloadHistory(tab, tab.TellTarget.Name, tab.TellTarget.World, Guid.Empty);
- Plugin.LogProxy.Info(
+ Plugin.LogProxy.Debug(
$"[Pin] Rehydrated '{tab.Name}' -> Tell target {tab.TellTarget.Name}@{tab.TellTarget.World}"
);
}
@@ -456,7 +456,7 @@ internal sealed class AutoTellTabsService : IDisposable
{
if (!tab.IsTempTab || tab.IsPinned)
{
- Plugin.LogProxy.Info(
+ Plugin.LogProxy.Debug(
$"[Pin] TryPin skipped: IsTempTab={tab.IsTempTab} IsPinned={tab.IsPinned}"
);
return false;
@@ -472,7 +472,7 @@ internal sealed class AutoTellTabsService : IDisposable
}
tab.IsPinned = true;
- Plugin.LogProxy.Info(
+ Plugin.LogProxy.Debug(
$"[Pin] Pinned tab '{tab.Name}' target={tab.TellTarget?.Name}@{tab.TellTarget?.World}"
);
_plugin.SaveConfig();
@@ -495,7 +495,7 @@ internal sealed class AutoTellTabsService : IDisposable
}
tab.IsPinned = false;
- Plugin.LogProxy.Info($"[Pin] Unpinned tab '{tab.Name}'");
+ Plugin.LogProxy.Debug("[Pin] Unpinned tab '{tab.Name}'");
_plugin.SaveConfig();
}
@@ -509,7 +509,7 @@ internal sealed class AutoTellTabsService : IDisposable
tab.IsTempTab = false;
tab.IsPinned = false;
tab.TellTarget = TellTarget.Empty();
- Plugin.LogProxy.Info(
+ Plugin.LogProxy.Debug(
$"[Pin] Promoted tab '{tab.Name}' to permanent (tell-binding dropped)"
);
_plugin.SaveConfig();
diff --git a/HellionChat/HellionChat.csproj b/HellionChat/HellionChat.csproj
index 43c63fa..3d11f01 100644
--- a/HellionChat/HellionChat.csproj
+++ b/HellionChat/HellionChat.csproj
@@ -1,7 +1,7 @@
- 1.4.6
+ 1.4.7
enable
enable
diff --git a/HellionChat/HellionChat.yaml b/HellionChat/HellionChat.yaml
index 549b301..992b5f4 100755
--- a/HellionChat/HellionChat.yaml
+++ b/HellionChat/HellionChat.yaml
@@ -35,6 +35,49 @@ tags:
- Replacement
- Privacy
changelog: |-
+ **v1.4.7 — Backlog Cleanup and Mid-Features (2026-05-13)**
+
+ Eighth sub-patch of the v1.4.x polish-sweep series. First
+ user-visible feature bundle since v1.4.5 — pinned tell tabs that
+ survive relog, opt-in Honorific glow rendering, and a configurable
+ sidebar.
+
+ - TempTell Pin: right-click a TempTell tab in the sidebar to pin
+ it. Pinned tabs survive relog, keep their conversation history
+ (loaded on demand from the message store), and stay bound to
+ the same /tell partner. Hard cap of 5 pinned tabs in a pool
+ separate from the 15-tab auto-tell pool — total ceiling is 20
+ tabs. New 'Pinned' section in the sidebar with its own divider
+ header
+ - Honorific Glow outline now renders when the title carries a
+ Glow colour. Opt-in via Settings → Integrations → 'Render glow
+ outlines (Honorific)' (default off, dodges the per-frame
+ DrawList overhead on low-end hardware). Gradient (Color3 /
+ GradientColourSet / Wave / Pulse) is parsed but rendered
+ statically — a later cycle will port the full animation
+ - Sidebar width is now configurable in Theme & Layout (range
+ 44–160 px). Default stays icon-only; widen to fit section
+ headers like 'Active Tells (3)' without truncation
+ - Settings Save no longer pops the chat input back to /tell with
+ a pinned partner — Configuration.UpdateFrom now preserves the
+ runtime CurrentChannel across the persistent-tab merge, and
+ TabSwitched deep-clones the seeded channel instead of sharing
+ the previous tab's UsedChannel
+ - Util/ImGuiUtil.cs DrawArrows IconButton id now uses
+ (id + 1).ToString() instead of the operator-precedence quirk
+ id + 1.ToString() — generated IDs stay numerically stable
+ - Internal: IPluginLogProxy indirection over Dalamud's IPluginLog
+ routes all ~91 Plugin.Log call sites through a testable proxy.
+ MessageStore.Migrate0 can now run in xUnit without loading
+ Dalamud.dll, closing the gap F12.1 left in v1.4.6
+ - Internal: TempTab counter switched from an Interlocked cached
+ field to a derived Tabs.Count(predicate) — pin-state transitions
+ are cold-path and don't need lock-free reads
+
+ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
+
+ ---
+
**v1.4.6 — Code Hygiene and Refactor (2026-05-12)**
Maintenance patch. No user-visible behaviour changes; tightens the
@@ -117,19 +160,4 @@ changelog: |-
---
- **v1.4.3 — Faster plugin load + new repo (2026-05-08)**
-
- Heavy 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.
-
- - Two-phase async load via IAsyncDalamudPlugin
- - Schema-gate replaces the v9→v16 migration chain; old configs
- require a v1.4.2 install first
- - AutoTranslate cache loads on first use instead of every startup
- - Custom font (Hellion-Exo2) appears with a brief pop after load
- - Repo moved to gitea.hellion-forge.cloud — update your custom-repo URL
-
- ---
-
Full history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases
diff --git a/README.md b/README.md
index 46ddd17..ebec976 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/actions/workflows/build.yml)
[](LICENSE)
-[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/latest)
+[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/latest)
[](https://github.com/goatcorp/Dalamud)
[](https://dotnet.microsoft.com/)
[](https://www.finalfantasyxiv.com/)
@@ -11,7 +11,7 @@
-**Version 1.4.6** — Privacy-first chat plugin for FINAL FANTASY XIV / Dalamud, built on
+**Version 1.4.7** — Privacy-first chat plugin for FINAL FANTASY XIV / Dalamud, built on
[Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2).
Hellion Chat is a privacy-first plugin built on the Chat 2 foundation. The majority of the engine comes from Chat 2
@@ -286,20 +286,23 @@ An optional submission to the Dalamud main plugin repo (in addition to the custo
## Project Status
-**Version 1.4.6** — Maintenance patch. No user-visible behaviour changes; tightens the development feedback loop and
-pulls in two ChatTwo upstream bugfixes. `scripts/preflight.sh` gains a csharpier reflow check and a markdownlint pass at
-the pre-push gate. `FontManager`'s font-fallback catch-filter now covers `InvalidOperationException` and
-`ArgumentException` on top of the IO triad, so a corrupted font config no longer takes down the atlas build.
-`BrandingLinks` and `IntegrationLinks` URLs validate themselves on plugin load — a typo in a future URL rotation throws
-at startup instead of failing silently when a user clicks the broken button. Cherry-picked from ChatTwo upstream
-`f35b7d3`: `Chat.SetChannel` no longer leaks the native `Utf8String` when the linkshell check rejects the channel, and
-`Tab.Clone` now deep-clones `UsedChannel` and `TellTarget` (the previous reference copy let PopOut and Temp tabs mutate
-each other's channel state). The active-tab underline pill scales with DPI and rounds to physical pixels for crisp
-rendering above 100 % DPI. Internal items: `HellionStyle` ChildBgAlpha extracted to a testable helper,
-`Plugin.SaveConfig` clones only the temp-tab subset, `SettingsOverview` caches the draw-list per frame,
-`Dalamud.Utility.Util` static surface routed through an `IPlatformUtil` indirection (`MessageStore`'s `IsWine` probe is
-now testable in isolation). No schema bump, no migration. Seventh sub-patch of the v1.4.x polish sweep series (as of
-2026-05-12).
+**Version 1.4.7** — Backlog cleanup and the first user-visible feature bundle since v1.4.5. TempTell tabs can now be
+pinned via right-click; pinned tabs survive relog, keep their conversation history (loaded on demand from the message
+store), and stay bound to the same `/tell` partner. A hard cap of 5 pinned tabs lives in a pool separate from the 15-tab
+auto-tell pool, so the total ceiling is 20 tabs. The sidebar groups pinned tabs into their own section with its own
+divider header. Honorific glow outlines now render when the title carries a Glow colour — opt-in via **Settings →
+Integrations → Render glow outlines (Honorific)**, default off, so v1.4.6 visuals stay untouched for users who don't
+care and the per-frame DrawList overhead is skipped on low-end hardware. Honorific gradient (Color3 / GradientColourSet
+/ Wave / Pulse) is parsed and stashed for a later cycle, but currently renders as the primary colour. Sidebar width is
+configurable in **Theme & Layout** between 44 and 160 px; default stays icon-only so existing users see no layout
+change. `Configuration.UpdateFrom` now preserves the runtime `CurrentChannel` across the persistent-tab merge, and
+`TabSwitched` deep-clones the seeded channel — together they fix a Settings-Save regression where the chat input could
+pop back to `/tell ` after touching settings while on a Party or Linkshell tab. Internal items:
+`IPluginLogProxy` indirection over Dalamud's `IPluginLog` routes all ~91 `Plugin.Log` call sites through a testable
+proxy, closing the test-isolation gap F12.1 left in v1.4.6 (`MessageStore.Migrate0` now runs in xUnit without loading
+`Dalamud.dll`). `Util/ImGuiUtil.cs`'s `DrawArrows` IconButton id gets explicit parentheses on the increment. Migration
+v16 → v17 is additive (new `Tab.IsPinned` flag, default false). Eighth sub-patch of the v1.4.x polish sweep series (as
+of 2026-05-13).
Hellion Chat is a standalone plugin, no longer a fork in the repository sense. Fully completed:
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 8f14bb0..7f440a3 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -10,6 +10,45 @@ to the release pages for details.
---
+## Hellion Chat 1.4.7 — Backlog Cleanup and Mid-Features (2026-05-13)
+
+Eighth sub-patch of the v1.4.x polish-sweep series. First user-visible feature bundle since v1.4.5 — pinned tell tabs
+that survive relog, opt-in Honorific glow rendering, a configurable sidebar, plus a Settings-Save channel-preservation
+fix surfaced during smoke testing.
+
+- TempTell Pin: right-click a TempTell tab in the sidebar and choose "Pin Tab" / "Tab anpinnen". Pinned tabs survive
+ plugin reload and character logout, keep their conversation history (loaded on demand from the message store on
+ rehydrate), and stay bound to the same `/tell` partner. Hard cap of 5 pinned tabs in a pool separate from the 15-tab
+ auto-tell pool — total ceiling is 20 tabs. The sidebar groups pinned tabs into their own section with a divider header
+- Honorific glow outlines now render via an 8-direction DrawList pre-pass when the title carries a Glow colour. Opt-in
+ via **Settings → Integrations → Render glow outlines (Honorific)** (default off). Honorific's gradient surface
+ (`Color3`, `GradientColourSet`, `GradientAnimationStyle`) is parsed and stashed for a later cycle but renders as the
+ primary colour until then — the v1.4.7 DTO already mirrors all four extra fields so the JSON roundtrip doesn't
+ silent-drop them
+- Sidebar width configurable in **Theme & Layout** (44–160 px, default 44 stays icon-only). The icon button stretches
+ with the configured width so a widened sidebar looks intentional, not a 36 px icon floating in empty space
+- `Configuration.UpdateFrom` now preserves the runtime `CurrentChannel` across the persistent-tab merge alongside
+ `Messages` and `LastSendUnread`. `TabSwitched` deep-clones the seeded channel from the previous tab instead of sharing
+ the same `UsedChannel` instance. Together these fix a regression where Settings-Save on a Party or Linkshell tab
+ popped the chat input back to `/tell ` on the next interaction
+- `Util/ImGuiUtil.cs` `DrawArrows` IconButton id uses `(id + 1).ToString()` with explicit parentheses instead of the
+ operator-precedence quirk `id + 1.ToString()` (which resolved to `id.ToString() + "1"`). Single live caller is
+ `Ui/DbViewer.cs:227` page-navigation
+- Internal: `IPluginLogProxy` indirection over Dalamud's `IPluginLog` routes all ~91 `Plugin.Log` call sites through a
+ testable proxy. `MessageStore.Migrate0` can now run in xUnit without loading `Dalamud.dll`, closing the gap F12.1 left
+ in v1.4.6. Production wrapper `DalamudPluginLogProxy` and Build-Suite `FakePluginLogProxy` mirror the full
+ `IPluginLog` surface (`Verbose`/`Debug`/`Information`/`Info`/`Warning`/`Error`/`Fatal`) with single-string,
+ `Exception+string`, and `params object[]` overloads
+- Internal: TempTab counter switched from an `Interlocked` cached field to a derived `Tabs.Count(predicate)`. Pin-state
+ transitions (TryPin / Unpin / Promote) are cold-path and don't need lock-free reads; counter mutation surface dropped
+ from 5 to 0 sites. Build-Suite floor 688 → 710 (+22)
+- Schema bump v16 → v17 is additive: new `Tab.IsPinned` bool, default false. Existing v16 configs load cleanly and get
+ their `Version` stamp bumped after the gate check
+
+Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
+
+---
+
## Hellion Chat 1.4.6 — Code Hygiene and Refactor (2026-05-12)
Maintenance patch. No user-visible behaviour changes; tightens the development feedback loop, fixes two
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index ebece1e..8af91c5 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -10,14 +10,34 @@ the plugin's privacy-first scope during brainstorming.
---
-## Next Cycle (v1.4.7)
+## Next Cycle (v1.4.8)
-**Backlog Cleanup.** Roll up the remaining audit items deferred from v1.4.0–v1.4.6 and the new entries surfaced during
-v1.4.6 (notably the `Plugin.Log` indirection that would unlock fully isolated `MessageStore` construction tests, plus
-follow-up scope hinted at in the ChatTwo upstream f35b7d3 cherry-picks). Scope is consolidated during brainstorm.
+**Hook-Layer Cycle.** Receive-suppressed-tells toggle (cross-reference XIVIM #73 bubble-layer sub-task), Database Viewer
+full-text search via SQLite FTS5, plus preparation for the later Ad-Block cycle. Hook-layer investigation is shared
+across these items so they cluster naturally in one sub-patch.
---
+## v1.4.7 — Backlog Cleanup and Mid-Features (released 2026-05-13)
+
+Eighth sub-patch of the v1.4.x Polish Sweep series. First user-visible feature bundle since v1.4.5. TempTell tabs can
+now be pinned via right-click; pinned tabs survive plugin reload and character logout, keep their conversation history
+(loaded on demand from the message store on rehydrate), and stay bound to the same `/tell` partner. A hard cap of 5
+pinned tabs lives in a pool separate from the 15-tab auto-tell pool, total ceiling 20. The sidebar groups pinned tabs
+into their own section with a divider header, and the sidebar width itself is now configurable in **Theme & Layout**
+between 44 and 160 px. Honorific glow outlines render when the title carries a Glow colour, opt-in via **Settings →
+Integrations → Render glow outlines (Honorific)** (default off). Honorific's gradient (Color3 / GradientColourSet / Wave
+/ Pulse) is parsed but rendered statically — a later cycle will port the full animation algorithm or land an upstream
+IPC PR for the resolved frame colour. `Configuration.UpdateFrom` now preserves the runtime `CurrentChannel` across the
+persistent-tab merge, and `TabSwitched` deep-clones the seeded channel instead of sharing the previous tab's
+`UsedChannel` — together they fix a Settings-Save regression where the chat input could pop back to
+`/tell ` after touching settings on a Party or Linkshell tab. Internal items: `IPluginLogProxy`
+indirection over Dalamud's `IPluginLog` routes all ~91 `Plugin.Log` call sites through a testable proxy, closing the
+F12.1 test-isolation gap (`MessageStore.Migrate0` runs in xUnit now). TempTab counter switched from `Interlocked` cached
+field to derived `Tabs.Count(predicate)`. Migration v16 → v17 is additive (new `Tab.IsPinned` flag). Build-Suite floor
+688 → 710 (+22 tests across Pin-lifecycle predicates, pool limits, Tab.Clone roundtrip, MessageStore Migrate0
+construction, and Honorific TitleData JSON roundtrip).
+
## v1.4.6 — Code Hygiene and Refactor (released 2026-05-12)
Seventh sub-patch of the v1.4.x Polish Sweep series. Maintenance patch — no user-visible behaviour changes; tightens the
diff --git a/repo.json b/repo.json
index 81b8df0..526d5ba 100644
--- a/repo.json
+++ b/repo.json
@@ -3,7 +3,7 @@
"Author": "Jon Kazama (Hellion Forge)",
"Name": "Hellion Chat",
"InternalName": "HellionChat",
- "AssemblyVersion": "1.4.6.0",
+ "AssemblyVersion": "1.4.7.0",
"Description": "A Hellion Forge plugin — privacy-focused chat replacement for FINAL FANTASY XIV, built for EU, US and JP data rules.\n\nBy default only your own conversations are stored. Public chat, NPC dialogue, system messages and battle logs are discarded at the storage layer unless you opt in. Retention windows are configurable per channel, history can be wiped retroactively, and everything can be exported on demand.\n\nFeatures:\n- Channel whitelist with a Privacy-First default\n- Per-channel retention with a daily background sweep\n- Retroactive cleanup with preview and Ctrl+Shift confirm\n- Export to Markdown, JSON or CSV\n- First-run wizard with three profiles: Privacy-First, Casual, Full History\n- Bilingual UI (EN/DE) with live language switching\n- Own config and database — no shared state with other plugins\n\nBased on Chat 2 by Infi and Anna (EUPL-1.2).\nSupport: https://discord.gg/X9V7Kcv5gR",
"ApplicableVersion": "any",
"RepoUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat",
@@ -14,12 +14,12 @@
"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- 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",
+ "Changelog": "**v1.4.7 — Backlog Cleanup and Mid-Features (2026-05-13)**\n\nEighth sub-patch of the v1.4.x polish-sweep series. First user-visible feature bundle since v1.4.5 — pinned tell tabs that survive relog, opt-in Honorific glow rendering, and a configurable sidebar.\n\n- TempTell Pin: right-click a TempTell tab in the sidebar to pin it. Pinned tabs survive relog, keep their conversation history (loaded on demand from the message store), and stay bound to the same /tell partner. Hard cap of 5 pinned tabs in a pool separate from the 15-tab auto-tell pool — total ceiling is 20 tabs. New 'Angepinnt' / 'Pinned' section in the sidebar with its own divider header\n- Honorific Glow outline now renders when the title carries a Glow colour. Opt-in via Settings → Integrations → 'Render glow outlines (Honorific)' (default off, dodges the per-frame DrawList overhead on low-end hardware). Gradient (Color3 / GradientColourSet / Wave / Pulse) is parsed but rendered statically — a later cycle will port the full animation\n- Sidebar width is now configurable in Theme & Layout (range 44–160 px). Default stays icon-only; widen to fit section headers like 'Aktive Tells (3)' without truncation\n- Settings Save no longer pops the chat input back to /tell with a pinned partner — Configuration.UpdateFrom now preserves the runtime CurrentChannel across the persistent-tab merge, and TabSwitched deep-clones the seeded channel instead of sharing the previous tab's UsedChannel\n- Util/ImGuiUtil.cs DrawArrows IconButton id now uses (id + 1).ToString() instead of the operator-precedence quirk id + 1.ToString() — generated IDs stay numerically stable\n- Internal: IPluginLogProxy indirection over Dalamud's IPluginLog routes all ~91 Plugin.Log call sites through a testable proxy. MessageStore.Migrate0 can now run in xUnit without loading Dalamud.dll, closing the gap F12.1 left in v1.4.6\n- Internal: TempTab counter switched from an Interlocked cached field to a derived Tabs.Count(predicate) — pin-state transitions are cold-path and don't need lock-free reads\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n---\n\n**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\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",
- "DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.4.6/latest.zip",
- "TestingAssemblyVersion": "1.4.6.0",
+ "DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.4.7/latest.zip",
+ "DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.4.7/latest.zip",
+ "DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.4.7/latest.zip",
+ "TestingAssemblyVersion": "1.4.7.0",
"IconUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/icon.png",
"ImageUrls": [
"https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/chatWindow.png",