chore(linting): refresh configs and sweep auto-fix
Pull in the refreshed linter and tooling configs (editorconfig, gitignore, gitattributes, prettierignore, prettierrc, markdownlint, yamllint, env.example, dotnet-tools) and run prettier and markdownlint in --fix / --write mode across the repo so the existing tree matches the new rules. - prettier 2-space indent on yaml/yml and json overrides, asterisk strong, underscore emphasis, proseWrap always - markdownlint MD007 indent aligned to 2 and MD049 to underscore so prettier output stays passing - preflight Block F also ignores CLAUDE.md (gitignored personal file) - prettierignore extended to keep HellionChat.yaml manifest and the NuGet packages.lock.json out of the formatter No semantic content changed; csharpier, build, full build-suite (729/729) and the new prettier/markdownlint/yamllint checks all green.
This commit is contained in:
+25
-19
@@ -1,13 +1,15 @@
|
||||
# AI Assistance Disclosure
|
||||
|
||||
HellionChat uses AI assistance per the [Dalamud Plugin AI Usage Policy](https://github.com/goatcorp/DalamudPluginsD17/)
|
||||
at the **Pair** level.
|
||||
HellionChat uses AI assistance per the
|
||||
[Dalamud Plugin AI Usage Policy](https://github.com/goatcorp/DalamudPluginsD17/) at the **Pair**
|
||||
level.
|
||||
|
||||
A note up front: HellionChat is currently not submitted to the official Dalamud plugin repository and technically has no
|
||||
obligation to disclose this. I would rather be upfront about how it is built.
|
||||
A note up front: HellionChat is currently not submitted to the official Dalamud plugin repository
|
||||
and technically has no obligation to disclose this. I would rather be upfront about how it is built.
|
||||
|
||||
HellionChat is my entry point into game modding and plugin development. I have never written a plugin for a game before.
|
||||
I work alone, so I get help where I need it. That is not something I want to hide.
|
||||
HellionChat is my entry point into game modding and plugin development. I have never written a
|
||||
plugin for a game before. I work alone, so I get help where I need it. That is not something I want
|
||||
to hide.
|
||||
|
||||
## How I Actually Work
|
||||
|
||||
@@ -18,12 +20,13 @@ I plan the architecture, decide what gets built, and own every design decision.
|
||||
- Read the Dalamud log output to verify behaviour
|
||||
- Run security and privacy audits on anything that touches user data
|
||||
|
||||
One of the main reasons I use AI is consistency. I want the HellionChat code to match the style of the upstream Chat 2
|
||||
codebase and stay readable for anyone who opens the repo, not just for me. Claude helps me catch when I am drifting from
|
||||
upstream conventions or writing something that only makes sense in my own head.
|
||||
One of the main reasons I use AI is consistency. I want the HellionChat code to match the style of
|
||||
the upstream Chat 2 codebase and stay readable for anyone who opens the repo, not just for me.
|
||||
Claude helps me catch when I am drifting from upstream conventions or writing something that only
|
||||
makes sense in my own head.
|
||||
|
||||
The balance is shifting toward more hand-written work as I get more comfortable with Dalamud and plugin development in
|
||||
general.
|
||||
The balance is shifting toward more hand-written work as I get more comfortable with Dalamud and
|
||||
plugin development in general.
|
||||
|
||||
## What AI Is Used For
|
||||
|
||||
@@ -34,23 +37,25 @@ general.
|
||||
|
||||
## What AI Is Not Used For
|
||||
|
||||
- **Visual assets.** Logos, icons, banners, and screenshots are human-drawn or taken from the running game.
|
||||
- **Visual assets.** Logos, icons, banners, and screenshots are human-drawn or taken from the
|
||||
running game.
|
||||
- **German translations.** Written by me as a native speaker.
|
||||
|
||||
## What Is Where
|
||||
|
||||
Upstream Chat 2 (by Infi & Anna, EUPL-1.2) is the foundation and was not produced with AI assistance.
|
||||
HellionChat-specific code lives in `HellionChat/Privacy/`, `HellionChat/Export/`,
|
||||
`HellionChat/Resources/HellionStrings*`, `Ui/SettingsTabs/Privacy.cs`, `Ui/FirstRunWizard.cs`, `Ui/HellionStyle.cs`,
|
||||
plus the Migrate3 recovery and plugin layout migration in `MessageStore.cs` and `Plugin.cs`. These were developed with
|
||||
Pair-level assistance as described above.
|
||||
Upstream Chat 2 (by Infi & Anna, EUPL-1.2) is the foundation and was not produced with AI
|
||||
assistance. HellionChat-specific code lives in `HellionChat/Privacy/`, `HellionChat/Export/`,
|
||||
`HellionChat/Resources/HellionStrings*`, `Ui/SettingsTabs/Privacy.cs`, `Ui/FirstRunWizard.cs`,
|
||||
`Ui/HellionStyle.cs`, plus the Migrate3 recovery and plugin layout migration in `MessageStore.cs`
|
||||
and `Plugin.cs`. These were developed with Pair-level assistance as described above.
|
||||
|
||||
## If AI-Assisted Development Is a Dealbreaker for You
|
||||
|
||||
Fair enough. There are solid alternatives:
|
||||
|
||||
- [Chat 2](https://github.com/Infiziert90/ChatTwo), the upstream project HellionChat is built on
|
||||
- [XIV Instant Messenger](https://github.com/NightmareXIV/XIVInstantMessenger), a different approach to FFXIV chat
|
||||
- [XIV Instant Messenger](https://github.com/NightmareXIV/XIVInstantMessenger), a different approach
|
||||
to FFXIV chat
|
||||
|
||||
Both are good projects. Use what fits you best.
|
||||
|
||||
@@ -71,4 +76,5 @@ Both are good projects. Use what fits you best.
|
||||
|
||||
## Contact
|
||||
|
||||
Questions about this disclosure: <https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues>
|
||||
Questions about this disclosure:
|
||||
<https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues>
|
||||
|
||||
+404
-345
@@ -1,48 +1,48 @@
|
||||
# Changelog — Hellion Chat
|
||||
|
||||
All user-facing changes to Hellion Chat. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
version numbers follow [Semantic Versioning](https://semver.org/).
|
||||
All user-facing changes to Hellion Chat. Format follows
|
||||
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/), version numbers follow
|
||||
[Semantic Versioning](https://semver.org/).
|
||||
|
||||
Detailed release notes per version are available directly on the
|
||||
[Gitea Release page](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases) and in the plugin
|
||||
changelog block (`HellionChat/HellionChat.yaml` → `changelog:`). This file summarises releases as an overview and links
|
||||
to the release pages for details.
|
||||
[Gitea Release page](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases) and
|
||||
in the plugin changelog block (`HellionChat/HellionChat.yaml` → `changelog:`). This file summarises
|
||||
releases as an overview and links to the release pages for details.
|
||||
|
||||
---
|
||||
|
||||
## Hellion Chat 1.5.0 — DI Foundation and Service Refactor (2026-05-17)
|
||||
|
||||
Major architecture cycle. The plugin bootstrap moves to a generic-host DI container
|
||||
(`Microsoft.Extensions.Hosting` + `IServiceCollection`) modelled on Lightless Sync. Service
|
||||
logging migrates from a static `Plugin.LogProxy` locator to typed
|
||||
`Microsoft.Extensions.Logging.ILogger<T>` via constructor injection, bridged over Dalamud's
|
||||
`IPluginLog` by a custom `DalamudLogger` trio.
|
||||
(`Microsoft.Extensions.Hosting` + `IServiceCollection`) modelled on Lightless Sync. Service logging
|
||||
migrates from a static `Plugin.LogProxy` locator to typed `Microsoft.Extensions.Logging.ILogger<T>`
|
||||
via constructor injection, bridged over Dalamud's `IPluginLog` by a custom `DalamudLogger` trio.
|
||||
|
||||
### Under the hood
|
||||
|
||||
- 18 instance-class services migrate to `ILogger<T>` via constructor injection across four
|
||||
slices: data layer (`MessageStore`, `MessageManager`, `AutoTellTabsService`), IPC and
|
||||
integrations (`HonorificService`, `IpcManager`, `TypingIpc`, `ExtraChat`, three
|
||||
`GameFunctions` classes), UI window layer (`ChatLogWindow`, `DbViewer`, `Popout`, three
|
||||
settings tabs), and root (`Commands`, `ThemeRegistry`, `PayloadHandler`).
|
||||
- `Plugin.LogProxy` stays in place for the eight buckets ctor injection cannot reach:
|
||||
static helpers (`EmoteCache`, `AutoTranslate`, `MemoryUtil`, `WrapperUtil`),
|
||||
Dalamud-reflected types (`Configuration`), the `Message` data class, and instance classes
|
||||
that only log from static methods (`FontManager`, one `GameFunctions` site).
|
||||
- Plugin.cs finishes at 1012 lines — virtually identical to the pre-cycle 1013. The new
|
||||
Phase-1 host build and `Plugin.X` bridge wiring trade out exactly the service and window
|
||||
allocations that previously lived in `LoadAsync`.
|
||||
- Cross-plugin baseline confirms no performance penalty against Chat 2: HellionChat
|
||||
first-frame HITCH 77 ms median, Chat 2 74 ms median. Lightless and XIVInstantMessenger sit
|
||||
around 7 ms by deferring their font-atlas build past `Finished loading` — that pattern is
|
||||
the v1.5.1 follow-up item.
|
||||
- 18 instance-class services migrate to `ILogger<T>` via constructor injection across four slices:
|
||||
data layer (`MessageStore`, `MessageManager`, `AutoTellTabsService`), IPC and integrations
|
||||
(`HonorificService`, `IpcManager`, `TypingIpc`, `ExtraChat`, three `GameFunctions` classes), UI
|
||||
window layer (`ChatLogWindow`, `DbViewer`, `Popout`, three settings tabs), and root (`Commands`,
|
||||
`ThemeRegistry`, `PayloadHandler`).
|
||||
- `Plugin.LogProxy` stays in place for the eight buckets ctor injection cannot reach: static helpers
|
||||
(`EmoteCache`, `AutoTranslate`, `MemoryUtil`, `WrapperUtil`), Dalamud-reflected types
|
||||
(`Configuration`), the `Message` data class, and instance classes that only log from static
|
||||
methods (`FontManager`, one `GameFunctions` site).
|
||||
- Plugin.cs finishes at 1012 lines — virtually identical to the pre-cycle 1013. The new Phase-1 host
|
||||
build and `Plugin.X` bridge wiring trade out exactly the service and window allocations that
|
||||
previously lived in `LoadAsync`.
|
||||
- Cross-plugin baseline confirms no performance penalty against Chat 2: HellionChat first-frame
|
||||
HITCH 77 ms median, Chat 2 74 ms median. Lightless and XIVInstantMessenger sit around 7 ms by
|
||||
deferring their font-atlas build past `Finished loading` — that pattern is the v1.5.1 follow-up
|
||||
item.
|
||||
|
||||
### User-visible
|
||||
|
||||
- Slash-command insert fix: pasting a slash command into the chat input (Friend List
|
||||
"/tell" action, plugin-driven inserts from Artisan, AllaganTools etc.) now replaces the
|
||||
existing input instead of concatenating onto whatever the user was typing. Cherry-picked
|
||||
from ChatTwo upstream `ee7768ac` with namespace adaptation.
|
||||
- Slash-command insert fix: pasting a slash command into the chat input (Friend List "/tell" action,
|
||||
plugin-driven inserts from Artisan, AllaganTools etc.) now replaces the existing input instead of
|
||||
concatenating onto whatever the user was typing. Cherry-picked from ChatTwo upstream `ee7768ac`
|
||||
with namespace adaptation.
|
||||
|
||||
Migration v17 stays (no schema bump).
|
||||
|
||||
@@ -52,23 +52,27 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.10 — Symbol-Picker and Tell-History Fix (2026-05-16)
|
||||
|
||||
Eleventh and final sub-patch of the v1.4.x Polish-Sweep series. Symbol picker for the chat input, a tell-history reload fix
|
||||
for users with many active partners, and a closing cleanup sweep before v1.5.0 picks up the DI-container adoption.
|
||||
Eleventh and final sub-patch of the v1.4.x Polish-Sweep series. Symbol picker for the chat input, a
|
||||
tell-history reload fix for users with many active partners, and a closing cleanup sweep before
|
||||
v1.5.0 picks up the DI-container adoption.
|
||||
|
||||
- Symbol picker for the chat input: smile-icon button left of the channel indicator opens a popup with two tabs —
|
||||
161 FFXIV PUA glyphs (Dalamud's SeIconChar enum) and 97 server-verified BMP symbols round-tripped through `/echo` and
|
||||
`/say` in a four-round probe. Cursor-aware splice, multi-insert keeps the popup open, recent-used strip floats the last
|
||||
sixteen picks across both tabs. Toggle in Settings → Chat → Message behaviour, default on.
|
||||
- Pinned auto-tell tabs reload their full history again. PreloadHistory had a hidden 500-row scan cap that overrode the
|
||||
user-configurable `AutoTellTabsHistoryPreload` setting whenever you chatted with many partners; less-frequent pinned
|
||||
partners lost their backlog. The cap is removed.
|
||||
- Slash-command teardown cleanup: `/hellion`, `/hellionView`, `/hellionDebugger` (and `#if DEBUG /hellionSeString`) wrappers
|
||||
are now cached as private fields so plugin teardown detaches the live registration instead of re-Register'ing with
|
||||
identical args (latent maintenance hazard from v1.4.9).
|
||||
- v1.4.x Polish-Sweep wraps up here. The ImGuiListClipper render refactor that was on the v1.4.10 reserve list got dropped
|
||||
after cross-platform smoke showed the scroll rubber-band is a Wine/Linux render-pipeline quirk, not universal — Windows
|
||||
users never saw it. It will get its own platform-targeted spike in a later patch. Next major cycle is v1.5.0 with the
|
||||
DI-container adoption (Microsoft.Extensions.Hosting + ILogger<T>) modelled on Lightless.
|
||||
- Symbol picker for the chat input: smile-icon button left of the channel indicator opens a popup
|
||||
with two tabs — 161 FFXIV PUA glyphs (Dalamud's SeIconChar enum) and 97 server-verified BMP
|
||||
symbols round-tripped through `/echo` and `/say` in a four-round probe. Cursor-aware splice,
|
||||
multi-insert keeps the popup open, recent-used strip floats the last sixteen picks across both
|
||||
tabs. Toggle in Settings → Chat → Message behaviour, default on.
|
||||
- Pinned auto-tell tabs reload their full history again. PreloadHistory had a hidden 500-row scan
|
||||
cap that overrode the user-configurable `AutoTellTabsHistoryPreload` setting whenever you chatted
|
||||
with many partners; less-frequent pinned partners lost their backlog. The cap is removed.
|
||||
- Slash-command teardown cleanup: `/hellion`, `/hellionView`, `/hellionDebugger` (and
|
||||
`#if DEBUG /hellionSeString`) wrappers are now cached as private fields so plugin teardown
|
||||
detaches the live registration instead of re-Register'ing with identical args (latent maintenance
|
||||
hazard from v1.4.9).
|
||||
- v1.4.x Polish-Sweep wraps up here. The ImGuiListClipper render refactor that was on the v1.4.10
|
||||
reserve list got dropped after cross-platform smoke showed the scroll rubber-band is a Wine/Linux
|
||||
render-pipeline quirk, not universal — Windows users never saw it. It will get its own
|
||||
platform-targeted spike in a later patch. Next major cycle is v1.5.0 with the DI-container
|
||||
adoption (Microsoft.Extensions.Hosting + ILogger<T>) modelled on Lightless.
|
||||
- Migration v17 stays (no schema bump).
|
||||
|
||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
@@ -77,35 +81,39 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.9 — Plugin-Load Render Polish (2026-05-15)
|
||||
|
||||
Tenth sub-patch of the v1.4.x polish-sweep series. First-frame render cost drops from ~127 ms median down to
|
||||
~76 ms median — comfortably under Dalamud's 100 ms HITCH warning threshold. The remaining ~13 ms gap to ChatTwo
|
||||
upstream (~63 ms median) is the cost of HellionChat-only features (sidebar tab view, custom status bar,
|
||||
Honorific integration).
|
||||
Tenth sub-patch of the v1.4.x polish-sweep series. First-frame render cost drops from ~127 ms median
|
||||
down to ~76 ms median — comfortably under Dalamud's 100 ms HITCH warning threshold. The remaining
|
||||
~13 ms gap to ChatTwo upstream (~63 ms median) is the cost of HellionChat-only features (sidebar tab
|
||||
view, custom status bar, Honorific integration).
|
||||
|
||||
- First-frame defer: six non-essential rendering sections inside `ChatLogWindow` skip their first Draw and run
|
||||
one frame later. Covered sections are the bottom status bar, channel-name SeString chunks, window bounds
|
||||
check, v0.6.1 hint banner, autocomplete and input-preview calculation. At 60 fps the user sees those sections
|
||||
~17 ms after plugin reload — invisible inside the ~2.5 s font-atlas build window every reload runs through
|
||||
anyway. Frame 1 stays well under 100 ms too (~40 ms), so no secondary HITCH warning appears.
|
||||
- Slash-command centralisation: `/hellion`, `/hellionView`, `/hellionSeString` and `/hellionDebugger` are now
|
||||
registered during `LoadAsync` instead of inside the corresponding window constructors. The commands work
|
||||
before their target window is opened the first time, and Dalamud's plugin-manager configuration / open
|
||||
buttons (`UiBuilder.OpenConfigUi` / `OpenMainUi`) hang on the same path.
|
||||
- Plugin-load profiling logs stay on: `MessageStore.Connect`, `MessageStore.Migrate`, `FilterAllTabs` and the
|
||||
auto-translate warm-up timing log are now Information level rather than Debug. They serve as a tripwire so a
|
||||
future regression past 100 ms shows up directly in `/xllog` without re-enabling Debug.
|
||||
- First-frame defer: six non-essential rendering sections inside `ChatLogWindow` skip their first
|
||||
Draw and run one frame later. Covered sections are the bottom status bar, channel-name SeString
|
||||
chunks, window bounds check, v0.6.1 hint banner, autocomplete and input-preview calculation. At 60
|
||||
fps the user sees those sections ~17 ms after plugin reload — invisible inside the ~2.5 s
|
||||
font-atlas build window every reload runs through anyway. Frame 1 stays well under 100 ms too (~40
|
||||
ms), so no secondary HITCH warning appears.
|
||||
- Slash-command centralisation: `/hellion`, `/hellionView`, `/hellionSeString` and
|
||||
`/hellionDebugger` are now registered during `LoadAsync` instead of inside the corresponding
|
||||
window constructors. The commands work before their target window is opened the first time, and
|
||||
Dalamud's plugin-manager configuration / open buttons (`UiBuilder.OpenConfigUi` / `OpenMainUi`)
|
||||
hang on the same path.
|
||||
- Plugin-load profiling logs stay on: `MessageStore.Connect`, `MessageStore.Migrate`,
|
||||
`FilterAllTabs` and the auto-translate warm-up timing log are now Information level rather than
|
||||
Debug. They serve as a tripwire so a future regression past 100 ms shows up directly in `/xllog`
|
||||
without re-enabling Debug.
|
||||
- ChatTwo IPC compatibility layer: HellionChat now mirrors ChatTwo's full IPC surface
|
||||
(`GetChatInputState`, `ChatInputStateChanged`, `Register`, `Unregister`, `Available`, `Invoke`) under the
|
||||
`ChatTwo.*` namespace in addition to our existing `HellionChat.*` provider gates. Third-party
|
||||
integrations that historically only subscribe to ChatTwo's IPC — for example Artisan's and AllaganTools'
|
||||
context-menu hooks — keep working without requiring a code change on their side. Conflict detection
|
||||
prevents ChatTwo from loading in parallel with HellionChat, so there is no slot-collision risk at
|
||||
runtime.
|
||||
(`GetChatInputState`, `ChatInputStateChanged`, `Register`, `Unregister`, `Available`, `Invoke`)
|
||||
under the `ChatTwo.*` namespace in addition to our existing `HellionChat.*` provider gates.
|
||||
Third-party integrations that historically only subscribe to ChatTwo's IPC — for example Artisan's
|
||||
and AllaganTools' context-menu hooks — keep working without requiring a code change on their side.
|
||||
Conflict detection prevents ChatTwo from loading in parallel with HellionChat, so there is no
|
||||
slot-collision risk at runtime.
|
||||
- Migration v17 stays (no schema bump).
|
||||
- Internal: hypothesis-triage during the R2 cycle falsified three of the four candidate root causes
|
||||
(font-atlas sync, theme-apply ABGR-cache init, multiple-window render). Actual cause is `DrawList` setup
|
||||
cost distributed across ~10 ImGui sections inside ChatLogWindow (5-20 ms each). The six selective defers
|
||||
above are the pragmatic fix — a clean structural rewrite would belong in the v1.5.x DI-container cycle.
|
||||
(font-atlas sync, theme-apply ABGR-cache init, multiple-window render). Actual cause is `DrawList`
|
||||
setup cost distributed across ~10 ImGui sections inside ChatLogWindow (5-20 ms each). The six
|
||||
selective defers above are the pragmatic fix — a clean structural rewrite would belong in the
|
||||
v1.5.x DI-container cycle.
|
||||
|
||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
@@ -113,28 +121,31 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.8 — Hook-Layer and Polish Quick-Wins (2026-05-14)
|
||||
|
||||
Ninth sub-patch of the v1.4.x polish-sweep series. Hook-layer cluster (FTS5 full-text search, ad-block foundation
|
||||
investigation) plus three polish quick-wins.
|
||||
Ninth sub-patch of the v1.4.x polish-sweep series. Hook-layer cluster (FTS5 full-text search,
|
||||
ad-block foundation investigation) plus three polish quick-wins.
|
||||
|
||||
- DbViewer full-text search: optional FTS5 index across the full chat history. Built asynchronously on first run after
|
||||
the update with a progress toast (UI stays responsive, the toggle is disabled until the build completes). The local
|
||||
page-filter remains the default mode. Multi-word queries match as exact phrases; power users can opt into raw FTS5
|
||||
`MATCH` syntax by wrapping their own double-quotes around the term.
|
||||
- Custom theme files now auto-reload when edited while the theme is active. Save the JSON in your editor and the live
|
||||
render picks up the change within a second — no need to re-click the theme in the picker. Disk-stat is throttled to
|
||||
1 Hz so per-frame cost stays free.
|
||||
- DbViewer full-text search: optional FTS5 index across the full chat history. Built asynchronously
|
||||
on first run after the update with a progress toast (UI stays responsive, the toggle is disabled
|
||||
until the build completes). The local page-filter remains the default mode. Multi-word queries
|
||||
match as exact phrases; power users can opt into raw FTS5 `MATCH` syntax by wrapping their own
|
||||
double-quotes around the term.
|
||||
- Custom theme files now auto-reload when edited while the theme is active. Save the JSON in your
|
||||
editor and the live render picks up the change within a second — no need to re-click the theme in
|
||||
the picker. Disk-stat is throttled to 1 Hz so per-frame cost stays free.
|
||||
- Retention sweep no longer blocks the framework thread. `Framework.Run(...).Wait()` is replaced by
|
||||
`Framework.RunOnTick(...)`, which removes the ~194 ms hitch the sweep used to add per run.
|
||||
- Status bar height is derived from `GetTextLineHeightWithSpacing()` plus a DPI-aware spacer so the bar renders
|
||||
correctly at Windows display scaling above 100 %. Linux/Wayland default of 100 % is unaffected.
|
||||
- Receive-suppressed-tells routing was investigated this cycle and **postponed to v1.5.x**. When other plugins suppress
|
||||
tells via `CheckMessageHandled`, FFXIV's chat pipeline skips the `RaptureLogModule.AddMsgSourceEntry` path, which means
|
||||
HellionChat's `ContentIdResolverHook` does not fire and tell-partner identification breaks for AutoTellTab routing.
|
||||
The proper fix sits next to the planned ad-block hook layer (`RaptureLogModule.ShowMiniTalkPlayer` and friends) where
|
||||
the same patch surface comes up anyway.
|
||||
- Internal: storage form of `messages.Id` clarified (declared BLOB but Microsoft.Data.Sqlite stores Guid parameters as
|
||||
TEXT). FTS bulk insert and `LoadByGuids` join now match the TEXT storage form on both sides. Migration v17 stays
|
||||
(no schema bump).
|
||||
- Status bar height is derived from `GetTextLineHeightWithSpacing()` plus a DPI-aware spacer so the
|
||||
bar renders correctly at Windows display scaling above 100 %. Linux/Wayland default of 100 % is
|
||||
unaffected.
|
||||
- Receive-suppressed-tells routing was investigated this cycle and **postponed to v1.5.x**. When
|
||||
other plugins suppress tells via `CheckMessageHandled`, FFXIV's chat pipeline skips the
|
||||
`RaptureLogModule.AddMsgSourceEntry` path, which means HellionChat's `ContentIdResolverHook` does
|
||||
not fire and tell-partner identification breaks for AutoTellTab routing. The proper fix sits next
|
||||
to the planned ad-block hook layer (`RaptureLogModule.ShowMiniTalkPlayer` and friends) where the
|
||||
same patch surface comes up anyway.
|
||||
- Internal: storage form of `messages.Id` clarified (declared BLOB but Microsoft.Data.Sqlite stores
|
||||
Guid parameters as TEXT). FTS bulk insert and `LoadByGuids` join now match the TEXT storage form
|
||||
on both sides. Migration v17 stays (no schema bump).
|
||||
|
||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
@@ -142,38 +153,43 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## 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.
|
||||
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 <pinned-partner>` 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,
|
||||
- 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 <pinned-partner>` 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
|
||||
- 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).
|
||||
|
||||
@@ -181,45 +197,51 @@ 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
|
||||
upstream-inherited bugs from ChatTwo `f35b7d3`, and prepares the code for the v1.4.7 backlog cleanup.
|
||||
Maintenance patch. No user-visible behaviour changes; tightens the development feedback loop, fixes
|
||||
two upstream-inherited bugs from ChatTwo `f35b7d3`, and prepares the code for the v1.4.7 backlog
|
||||
cleanup.
|
||||
|
||||
- `scripts/preflight.sh` gains Block E (`dotnet csharpier check`) and Block F (`markdownlint-cli2`) so reflow drift and
|
||||
markdown violations are caught at the pre-push gate. `.markdownlint.json` adds `MD024 siblings_only` and disables
|
||||
`MD036` so the bilingual forge-post bold-emphasis headings pass linting; the `.claude/` directory is excluded from the
|
||||
scan
|
||||
- `FontManager.AddFontWithFallback` catch-filter now covers `InvalidOperationException` and `ArgumentException` on top
|
||||
of the existing IO triad. The warning log carries the exception type name, so the diagnostic path knows which class of
|
||||
atlas-toolkit throw triggered the NotoSansCjkRegular fallback
|
||||
- `BrandingLinks` (5 URLs) and `Integrations/IntegrationLinks` (2 URLs) validate themselves on first module load via
|
||||
`[ModuleInitializer]` + a shared `UrlValidation.ValidateAll` helper. A malformed URL now throws
|
||||
`InvalidOperationException` at plugin load with the source class and the broken URL in the message
|
||||
- Cherry-picked from ChatTwo upstream `f35b7d3`: `Chat.SetChannel` no longer leaks the native `Utf8String` when the
|
||||
linkshell check rejects the channel. The validity check is now wrapped around the `ChangeChatChannel` call instead of
|
||||
short-circuiting before `Dtor`. `ValidAnyLinkshell` is renamed to `IsChannelOrExistingLinkshell` and the
|
||||
`ChatLogWindow` call-site follows the rename
|
||||
- Cherry-picked from ChatTwo upstream `f35b7d3`: `Tab.Clone` now deep-clones `UsedChannel` and `TellTarget`. The old
|
||||
`CurrentChannel = CurrentChannel` was a reference copy, so PopOut and Temp tabs mutated each other's channel state
|
||||
(incl. tell target). `TellTarget.From(t)` static factory is replaced with an instance `Clone()`; `UsedChannel.Clone()`
|
||||
is new and runs deep-clone on both TellTarget references
|
||||
- `ChatLogWindow` active-tab underline pill now scales with `ImGuiHelpers.GlobalScale` and rounds its DrawList
|
||||
coordinates to physical pixels via `MathF.Round`, so the 2 px line stays crisp on 125 % and 150 % DPI setups instead
|
||||
of bleeding into a sub-pixel blur
|
||||
- `ImGuiUtil.IconButton` width parameter no longer subtracts HUD-scaled `CellPadding.X * 2` from the raw `int` width.
|
||||
`ImGui.Button` handles its own frame padding internally, so the measured `buttonWidth` now passes through verbatim
|
||||
(inspired-by upstream `f35b7d3`, but our two call-sites need the parameter, so the param itself stays)
|
||||
- Internal: `HellionStyle` ChildBgAlpha threshold logic extracted to `HellionStyleHelpers.ResolveChildBgAlpha` with a
|
||||
build-suite mirror test that pins the 0.999f cutoff. `Plugin.SaveConfig` clones only the temp-tab subset in the
|
||||
pre-serialization snapshot instead of the full tab list. `SettingsOverview` caches `ImGui.GetWindowDrawList()` once
|
||||
per frame and passes the pointer down to `DrawCard`
|
||||
- Internal: `Dalamud.Utility.Util` static surface (`IsWine`, `OpenLink`) routed through a new `IPlatformUtil`
|
||||
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
|
||||
- `scripts/preflight.sh` gains Block E (`dotnet csharpier check`) and Block F (`markdownlint-cli2`)
|
||||
so reflow drift and markdown violations are caught at the pre-push gate. `.markdownlint.json` adds
|
||||
`MD024 siblings_only` and disables `MD036` so the bilingual forge-post bold-emphasis headings pass
|
||||
linting; the `.claude/` directory is excluded from the scan
|
||||
- `FontManager.AddFontWithFallback` catch-filter now covers `InvalidOperationException` and
|
||||
`ArgumentException` on top of the existing IO triad. The warning log carries the exception type
|
||||
name, so the diagnostic path knows which class of atlas-toolkit throw triggered the
|
||||
NotoSansCjkRegular fallback
|
||||
- `BrandingLinks` (5 URLs) and `Integrations/IntegrationLinks` (2 URLs) validate themselves on first
|
||||
module load via `[ModuleInitializer]` + a shared `UrlValidation.ValidateAll` helper. A malformed
|
||||
URL now throws `InvalidOperationException` at plugin load with the source class and the broken URL
|
||||
in the message
|
||||
- Cherry-picked from ChatTwo upstream `f35b7d3`: `Chat.SetChannel` no longer leaks the native
|
||||
`Utf8String` when the linkshell check rejects the channel. The validity check is now wrapped
|
||||
around the `ChangeChatChannel` call instead of short-circuiting before `Dtor`. `ValidAnyLinkshell`
|
||||
is renamed to `IsChannelOrExistingLinkshell` and the `ChatLogWindow` call-site follows the rename
|
||||
- Cherry-picked from ChatTwo upstream `f35b7d3`: `Tab.Clone` now deep-clones `UsedChannel` and
|
||||
`TellTarget`. The old `CurrentChannel = CurrentChannel` was a reference copy, so PopOut and Temp
|
||||
tabs mutated each other's channel state (incl. tell target). `TellTarget.From(t)` static factory
|
||||
is replaced with an instance `Clone()`; `UsedChannel.Clone()` is new and runs deep-clone on both
|
||||
TellTarget references
|
||||
- `ChatLogWindow` active-tab underline pill now scales with `ImGuiHelpers.GlobalScale` and rounds
|
||||
its DrawList coordinates to physical pixels via `MathF.Round`, so the 2 px line stays crisp on 125
|
||||
% and 150 % DPI setups instead of bleeding into a sub-pixel blur
|
||||
- `ImGuiUtil.IconButton` width parameter no longer subtracts HUD-scaled `CellPadding.X * 2` from the
|
||||
raw `int` width. `ImGui.Button` handles its own frame padding internally, so the measured
|
||||
`buttonWidth` now passes through verbatim (inspired-by upstream `f35b7d3`, but our two call-sites
|
||||
need the parameter, so the param itself stays)
|
||||
- Internal: `HellionStyle` ChildBgAlpha threshold logic extracted to
|
||||
`HellionStyleHelpers.ResolveChildBgAlpha` with a build-suite mirror test that pins the 0.999f
|
||||
cutoff. `Plugin.SaveConfig` clones only the temp-tab subset in the pre-serialization snapshot
|
||||
instead of the full tab list. `SettingsOverview` caches `ImGui.GetWindowDrawList()` once per frame
|
||||
and passes the pointer down to `DrawCard`
|
||||
- Internal: `Dalamud.Utility.Util` static surface (`IsWine`, `OpenLink`) routed through a new
|
||||
`IPlatformUtil` 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 — <https://discord.gg/X9V7Kcv5gR>
|
||||
|
||||
@@ -229,26 +251,31 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.5 — UX and Robustness (2026-05-12)
|
||||
|
||||
Sixth sub-patch of the v1.4.x polish-sweep series. User-visible robustness fixes plus two doc/test polish items from the
|
||||
audit backlog. No schema bump, no migration.
|
||||
Sixth sub-patch of the v1.4.x polish-sweep series. User-visible robustness fixes plus two doc/test
|
||||
polish items from the audit backlog. No schema bump, no migration.
|
||||
|
||||
- `ChatLogWindow.Draw` now surfaces a one-shot warning notification when the draw path throws. The stack trace still
|
||||
goes to `/xllog` via `Plugin.Log.Error`; the notification is suppressed for the rest of the plugin session so a
|
||||
recurring failure can't spam the notification stack frame-by-frame. Pattern-match to the existing `Plugin.cs:505-516`
|
||||
migration-blocker notification
|
||||
- `FirstRunWizard` splits accept from close. `OnClose` no longer silently sets `FirstRunCompleted`, so closing the X
|
||||
leaves the wizard pending and it reopens on the next plugin load. A new footer "Later — keep defaults" button is the
|
||||
explicit path to dismiss without picking a profile. Bilingual strings (EN + DE) plus a tooltip
|
||||
- `InputHistoryService.Reset` is wired into `Plugin.DisposeAsync` alongside the existing pure-memory cleanups. Static
|
||||
state used to survive a plugin reload — the next load now starts with an empty history
|
||||
- `FontManager.GetHellionFontBytes` becomes `TryGetHellionFontBytes` with a nullable return. On miss (broken csproj,
|
||||
hand-rolled dev build) the caller falls back to the system-font path that `UseHellionFont=false` already uses, plus a
|
||||
`Plugin.Log.Warning`. The whole UiBuilder no longer throws if the embedded font resource is absent
|
||||
- `Plugin.cs:167-168` gets a 4-line reasoning comment around the session-only `RemoveAll(IsTempTab)`: tells are usually
|
||||
privacy-filtered, resurrecting an empty crashed-session tab would trigger DB reconstruction on the next load.
|
||||
`TempTabCounter.InitFromList` mirrors the post-strip semantic in the Build-Suite with a pinning test
|
||||
- `StatusBar.cs` drops the version slot when the chat window's content width minus the version text is below 200 px. The
|
||||
right-aligned version used to clip into the four left-side slots in narrow windows
|
||||
- `ChatLogWindow.Draw` now surfaces a one-shot warning notification when the draw path throws. The
|
||||
stack trace still goes to `/xllog` via `Plugin.Log.Error`; the notification is suppressed for the
|
||||
rest of the plugin session so a recurring failure can't spam the notification stack
|
||||
frame-by-frame. Pattern-match to the existing `Plugin.cs:505-516` migration-blocker notification
|
||||
- `FirstRunWizard` splits accept from close. `OnClose` no longer silently sets `FirstRunCompleted`,
|
||||
so closing the X leaves the wizard pending and it reopens on the next plugin load. A new footer
|
||||
"Later — keep defaults" button is the explicit path to dismiss without picking a profile.
|
||||
Bilingual strings (EN + DE) plus a tooltip
|
||||
- `InputHistoryService.Reset` is wired into `Plugin.DisposeAsync` alongside the existing pure-memory
|
||||
cleanups. Static state used to survive a plugin reload — the next load now starts with an empty
|
||||
history
|
||||
- `FontManager.GetHellionFontBytes` becomes `TryGetHellionFontBytes` with a nullable return. On miss
|
||||
(broken csproj, hand-rolled dev build) the caller falls back to the system-font path that
|
||||
`UseHellionFont=false` already uses, plus a `Plugin.Log.Warning`. The whole UiBuilder no longer
|
||||
throws if the embedded font resource is absent
|
||||
- `Plugin.cs:167-168` gets a 4-line reasoning comment around the session-only
|
||||
`RemoveAll(IsTempTab)`: tells are usually privacy-filtered, resurrecting an empty crashed-session
|
||||
tab would trigger DB reconstruction on the next load. `TempTabCounter.InitFromList` mirrors the
|
||||
post-strip semantic in the Build-Suite with a pinning test
|
||||
- `StatusBar.cs` drops the version slot when the chat window's content width minus the version text
|
||||
is below 200 px. The right-aligned version used to clip into the four left-side slots in narrow
|
||||
windows
|
||||
|
||||
Modding & support: join Hellion Forge — <https://discord.gg/X9V7Kcv5gR>
|
||||
|
||||
@@ -258,29 +285,32 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.4 — Threading and IPC Safety Polish (2026-05-12)
|
||||
|
||||
Fifth sub-patch of the v1.4.x polish-sweep series. Threading assumptions are documented per-method, a hot-path lock
|
||||
falls away in `AutoTellTabsService`, IPC-cleanup failures become visible, and the privacy filter now speaks up when an
|
||||
unknown ChatType shows up.
|
||||
Fifth sub-patch of the v1.4.x polish-sweep series. Threading assumptions are documented per-method,
|
||||
a hot-path lock falls away in `AutoTellTabsService`, IPC-cleanup failures become visible, and the
|
||||
privacy filter now speaks up when an unknown ChatType shows up.
|
||||
|
||||
- `AutoTellTabsService.ActiveTempTabCount` switches from a lock-protected LINQ `Count` to an `Interlocked` counter kept
|
||||
in sync with `Config.Tabs` from inside the existing mutation paths. `Initialize()` seeds the counter from the
|
||||
persisted Tabs list, and `SaveConfig`'s snapshot-restore path calls a new `ResyncTempTabCounter()` so the mid-step
|
||||
`RemoveAll` doesn't leave the counter drifting. Pure-helper test mirror lives in the Build-Suite repo
|
||||
- `HonorificService` per-method threading banners replace the block comment at the bottom of the file. Each IPC callback
|
||||
(`TryInitialPull`, `OnTitleChanged`, `OnReady`, `OnDisposing`, `TryUnsubscribe`) and the `CurrentTitle` field carry a
|
||||
one-line `// Thread:` annotation so the framework-thread invariant is visible at the call site
|
||||
- `TryUnsubscribe` log-level upgraded from `Debug` to `Warning`. A silent unsubscribe failure leaks a live subscription
|
||||
across plugin reloads, which is exactly the kind of issue that should not be at Debug
|
||||
- `AutoTranslate.PreloadCache` thread now has `IsBackground = true` and a thread name. Without `IsBackground` the warmup
|
||||
blocks plugin unload (typically 100-300 ms). Pattern-match to `MessageManager` (F6.1) and `Plugin.RetentionSweep`
|
||||
(F9.3), both since v1.4.0
|
||||
- `Configuration.IsAllowedForStorage` adds a one-line `Plugin.Log.Warning` for the first occurrence of any ChatType that
|
||||
isn't in `PrivacyPersistChannels`. Dedup via a `NonSerialized` `HashSet<ChatType>`, so the warning fires once per
|
||||
runtime — not once per frame, not once per install. Failsafe routing through `PrivacyPersistUnknownChannels` is
|
||||
unchanged
|
||||
- `PrivacyPersistUnknownChannels` field default flipped from `false` to `true` for new installs via a constant in
|
||||
`PrivacyDefaults`. Existing configs keep their explicit choice — the deserializer overrides the initializer. No schema
|
||||
bump, no migration, no first-run banner
|
||||
- `AutoTellTabsService.ActiveTempTabCount` switches from a lock-protected LINQ `Count` to an
|
||||
`Interlocked` counter kept in sync with `Config.Tabs` from inside the existing mutation paths.
|
||||
`Initialize()` seeds the counter from the persisted Tabs list, and `SaveConfig`'s snapshot-restore
|
||||
path calls a new `ResyncTempTabCounter()` so the mid-step `RemoveAll` doesn't leave the counter
|
||||
drifting. Pure-helper test mirror lives in the Build-Suite repo
|
||||
- `HonorificService` per-method threading banners replace the block comment at the bottom of the
|
||||
file. Each IPC callback (`TryInitialPull`, `OnTitleChanged`, `OnReady`, `OnDisposing`,
|
||||
`TryUnsubscribe`) and the `CurrentTitle` field carry a one-line `// Thread:` annotation so the
|
||||
framework-thread invariant is visible at the call site
|
||||
- `TryUnsubscribe` log-level upgraded from `Debug` to `Warning`. A silent unsubscribe failure leaks
|
||||
a live subscription across plugin reloads, which is exactly the kind of issue that should not be
|
||||
at Debug
|
||||
- `AutoTranslate.PreloadCache` thread now has `IsBackground = true` and a thread name. Without
|
||||
`IsBackground` the warmup blocks plugin unload (typically 100-300 ms). Pattern-match to
|
||||
`MessageManager` (F6.1) and `Plugin.RetentionSweep` (F9.3), both since v1.4.0
|
||||
- `Configuration.IsAllowedForStorage` adds a one-line `Plugin.Log.Warning` for the first occurrence
|
||||
of any ChatType that isn't in `PrivacyPersistChannels`. Dedup via a `NonSerialized`
|
||||
`HashSet<ChatType>`, so the warning fires once per runtime — not once per frame, not once per
|
||||
install. Failsafe routing through `PrivacyPersistUnknownChannels` is unchanged
|
||||
- `PrivacyPersistUnknownChannels` field default flipped from `false` to `true` for new installs via
|
||||
a constant in `PrivacyDefaults`. Existing configs keep their explicit choice — the deserializer
|
||||
overrides the initializer. No schema bump, no migration, no first-run banner
|
||||
|
||||
Modding & support: join Hellion Forge — <https://discord.gg/X9V7Kcv5gR>
|
||||
|
||||
@@ -290,22 +320,26 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.3 — Plugin-Load Async-Init + Repo-Cutover (2026-05-08)
|
||||
|
||||
Plugin lifecycle migrated to Dalamud's `IAsyncDalamudPlugin` API. The constructor now does only the bootstrap-essentials
|
||||
(config load, language init, conflict detection); migrations, service allocations, window construction and hook
|
||||
subscription move to `LoadAsync`. Dalamud can keep its UI responsive while the heavy work runs.
|
||||
Plugin lifecycle migrated to Dalamud's `IAsyncDalamudPlugin` API. The constructor now does only the
|
||||
bootstrap-essentials (config load, language init, conflict detection); migrations, service
|
||||
allocations, window construction and hook subscription move to `LoadAsync`. Dalamud can keep its UI
|
||||
responsive while the heavy work runs.
|
||||
|
||||
- `IAsyncDalamudPlugin` two-phase load with per-line `CaptureFailure` in `DisposeAsync` (mirrors LightlessSync's
|
||||
pattern); idempotency guard protects against reload races
|
||||
- Schema-gate replaces the v9 → v16 migration chain. Configs on schema v16+ load directly; older configs trigger an
|
||||
"install v1.4.2 first" error so the historic migration path stays intact
|
||||
- `AutoTranslate.PreloadCache` moved off the load path. First use may have a sub-second hitch instead of every-load; the
|
||||
upstream chose differently, we accept first-use latency
|
||||
- `FontManager.BuildFonts` is called sync at the start of `LoadAsync`; Dalamud rebuilds the font atlas on its own
|
||||
pipeline so the custom Hellion-Exo2 font appears with a brief font-pop after load (matches ChatTwo's behaviour)
|
||||
- Custom-repo URL moved to `gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat`. GitHub repo stays as a frozen
|
||||
v1.4.2 snapshot; new releases ship from Gitea. Existing testers need to update the custom-repo URL once
|
||||
- Plugin-load time in this release sits at ~3.7 s median (5 reloads), comparable to v1.4.2. Async migration is
|
||||
foundational for v1.4.4 Lazy-Init optimisations rather than an immediate user-perceived win
|
||||
- `IAsyncDalamudPlugin` two-phase load with per-line `CaptureFailure` in `DisposeAsync` (mirrors
|
||||
LightlessSync's pattern); idempotency guard protects against reload races
|
||||
- Schema-gate replaces the v9 → v16 migration chain. Configs on schema v16+ load directly; older
|
||||
configs trigger an "install v1.4.2 first" error so the historic migration path stays intact
|
||||
- `AutoTranslate.PreloadCache` moved off the load path. First use may have a sub-second hitch
|
||||
instead of every-load; the upstream chose differently, we accept first-use latency
|
||||
- `FontManager.BuildFonts` is called sync at the start of `LoadAsync`; Dalamud rebuilds the font
|
||||
atlas on its own pipeline so the custom Hellion-Exo2 font appears with a brief font-pop after load
|
||||
(matches ChatTwo's behaviour)
|
||||
- Custom-repo URL moved to `gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat`. GitHub repo
|
||||
stays as a frozen v1.4.2 snapshot; new releases ship from Gitea. Existing testers need to update
|
||||
the custom-repo URL once
|
||||
- Plugin-load time in this release sits at ~3.7 s median (5 reloads), comparable to v1.4.2. Async
|
||||
migration is foundational for v1.4.4 Lazy-Init optimisations rather than an immediate
|
||||
user-perceived win
|
||||
|
||||
Modding & support: join Hellion Forge — <https://discord.gg/X9V7Kcv5gR>
|
||||
|
||||
@@ -315,17 +349,21 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.2 — ChatLog Frame-Hot-Path
|
||||
|
||||
Third sub-patch of the v1.4.x Polish Sweep series. Per-frame allocations from the chat-log render path eliminated.
|
||||
Third sub-patch of the v1.4.x Polish Sweep series. Per-frame allocations from the chat-log render
|
||||
path eliminated.
|
||||
|
||||
- `DrawMessages` card-mode hoists `theme`/`drawList`/`winLeft`/`winRight`/`borderColorAbgr` out of the per-message loop.
|
||||
About 500 redundant calls per frame at 100 visible messages, multiplied by every pop-out window
|
||||
- Auto-tell tab tint and icon use a per-tab cache. Hash computation and string allocation only happen when the tell
|
||||
target name or world drifts. `AutoTellTabTint` stays a pure hash helper; cache lives in a thin `TabTintCache` wrapper
|
||||
- Status bar gates its tab aggregation behind the same one-second cache it already used for the format strings. LINQ
|
||||
`Sum` and `Count` replaced with a single `foreach` pass that runs on roughly 1 % of frames
|
||||
- `DrawMessages` card-mode hoists `theme`/`drawList`/`winLeft`/`winRight`/`borderColorAbgr` out of
|
||||
the per-message loop. About 500 redundant calls per frame at 100 visible messages, multiplied by
|
||||
every pop-out window
|
||||
- Auto-tell tab tint and icon use a per-tab cache. Hash computation and string allocation only
|
||||
happen when the tell target name or world drifts. `AutoTellTabTint` stays a pure hash helper;
|
||||
cache lives in a thin `TabTintCache` wrapper
|
||||
- Status bar gates its tab aggregation behind the same one-second cache it already used for the
|
||||
format strings. LINQ `Sum` and `Count` replaced with a single `foreach` pass that runs on roughly
|
||||
1 % of frames
|
||||
|
||||
Realistic frame-time recovery: 2-5 % in typical scenes, more on pop-out-heavy setups because the card-border hoist
|
||||
scales per window.
|
||||
Realistic frame-time recovery: 2-5 % in typical scenes, more on pop-out-heavy setups because the
|
||||
card-border hoist scales per window.
|
||||
|
||||
Modding & support: join Hellion Forge — <https://discord.gg/X9V7Kcv5gR>
|
||||
|
||||
@@ -335,23 +373,24 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.1 — Theme Engine Performance
|
||||
|
||||
Second sub-patch of the v1.4.x Polish Sweep series. Heap pressure from the theme engine's per-frame render path removed,
|
||||
plus a tenth built-in theme and hardening for the custom-theme hot-reload.
|
||||
Second sub-patch of the v1.4.x Polish Sweep series. Heap pressure from the theme engine's per-frame
|
||||
render path removed, plus a tenth built-in theme and hardening for the custom-theme hot-reload.
|
||||
|
||||
- Theme records carry a pre-computed ABGR-packed cache for every color slot; cache is filled when the theme is
|
||||
registered and refreshed defensively on every `Switch()`
|
||||
- `HellionStyle.PushGlobal` reads ABGR values from the cache instead of calling `ColourUtil.RgbaToAbgr` per slot per
|
||||
frame; ~13 % render-time recovery measured in typical scenes (plan estimate was 2–6 %, real ~10–15 %)
|
||||
- `ThemeRegistry` custom-theme reload distinguishes a recoverable file lock (editor mid-save) from a permanent IO
|
||||
failure; locked themes keep their last-known-good snapshot and retry on the next lookup instead of dropping out of the
|
||||
picker
|
||||
- New built-in: **Synthwave Sunset** — Hot Magenta + Cyan on midnight violet, 80s neon-grid vibes; tenth theme in the
|
||||
picker
|
||||
- Author credits refreshed: brand themes are credited as "Hellion Forge"; **Mint Grove** and **Forge Merchantman** now
|
||||
credited to **Carla Beleandis** as a community thanks
|
||||
- Theme records carry a pre-computed ABGR-packed cache for every color slot; cache is filled when
|
||||
the theme is registered and refreshed defensively on every `Switch()`
|
||||
- `HellionStyle.PushGlobal` reads ABGR values from the cache instead of calling
|
||||
`ColourUtil.RgbaToAbgr` per slot per frame; ~13 % render-time recovery measured in typical scenes
|
||||
(plan estimate was 2–6 %, real ~10–15 %)
|
||||
- `ThemeRegistry` custom-theme reload distinguishes a recoverable file lock (editor mid-save) from a
|
||||
permanent IO failure; locked themes keep their last-known-good snapshot and retry on the next
|
||||
lookup instead of dropping out of the picker
|
||||
- New built-in: **Synthwave Sunset** — Hot Magenta + Cyan on midnight violet, 80s neon-grid vibes;
|
||||
tenth theme in the picker
|
||||
- Author credits refreshed: brand themes are credited as "Hellion Forge"; **Mint Grove** and **Forge
|
||||
Merchantman** now credited to **Carla Beleandis** as a community thanks
|
||||
|
||||
No schema bump, no user-visible behaviour change other than smoother frames on GC-sensitive setups and one additional
|
||||
colour option.
|
||||
No schema bump, no user-visible behaviour change other than smoother frames on GC-sensitive setups
|
||||
and one additional colour option.
|
||||
|
||||
Modding & support: join Hellion Forge — <https://discord.gg/X9V7Kcv5gR>
|
||||
|
||||
@@ -361,20 +400,20 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.4.0 — Critical Lifecycle Fixes
|
||||
|
||||
First sub-patch of the v1.4.x Polish Sweep series. Seven known lifecycle and race bugs eliminated before any performance
|
||||
refactor sits on top.
|
||||
First sub-patch of the v1.4.x Polish Sweep series. Seven known lifecycle and race bugs eliminated
|
||||
before any performance refactor sits on top.
|
||||
|
||||
- MessageStore disposal no longer triggers GC.Collect globally; Pooling=false on the SQLite connection means there's
|
||||
nothing left to clean up by hand
|
||||
- PendingMessage and RetentionSweep worker threads are explicitly marked IsBackground=true so the plugin domain can
|
||||
unload during XIVLauncher reload without waiting for them
|
||||
- EmoteCache image and gif loaders moved from async-void to async Task with a shared task tracker, draining on Dispose
|
||||
so an in-flight load can no longer write to a disposed EmoteImages entry
|
||||
- MessageStore disposal no longer triggers GC.Collect globally; Pooling=false on the SQLite
|
||||
connection means there's nothing left to clean up by hand
|
||||
- PendingMessage and RetentionSweep worker threads are explicitly marked IsBackground=true so the
|
||||
plugin domain can unload during XIVLauncher reload without waiting for them
|
||||
- EmoteCache image and gif loaders moved from async-void to async Task with a shared task tracker,
|
||||
draining on Dispose so an in-flight load can no longer write to a disposed EmoteImages entry
|
||||
- DisposeAsync 10s timeout now warns loudly instead of silently leaving the worker behind
|
||||
- Plugin.Dispose flushes any pending DeferredSaveFrames before tearing services down, so settings changes made in the
|
||||
last few frames before disable are no longer lost
|
||||
- The v13→v14 config migration now reads the pre-v13 backup and carries HellionThemeWindowOpacity into the new
|
||||
WindowOpacity field instead of falling back to the default 0.85
|
||||
- Plugin.Dispose flushes any pending DeferredSaveFrames before tearing services down, so settings
|
||||
changes made in the last few frames before disable are no longer lost
|
||||
- The v13→v14 config migration now reads the pre-v13 backup and carries HellionThemeWindowOpacity
|
||||
into the new WindowOpacity field instead of falling back to the default 0.85
|
||||
|
||||
Modding & support: join Hellion Forge — <https://discord.gg/X9V7Kcv5gR>
|
||||
|
||||
@@ -384,14 +423,14 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
## Hellion Chat 1.3.0 — Plugin Integrations: Honorific
|
||||
|
||||
First step on the plugin-integration roadmap. HellionChat now listens to Honorific and shows your custom title in the
|
||||
chat header. The slot auto-hides when Honorific is not installed, when no custom title is active, or when you are using
|
||||
the original FFXIV title.
|
||||
First step on the plugin-integration roadmap. HellionChat now listens to Honorific and shows your
|
||||
custom title in the chat header. The slot auto-hides when Honorific is not installed, when no custom
|
||||
title is active, or when you are using the original FFXIV title.
|
||||
|
||||
- New "Integrations" settings tab
|
||||
- Honorific integration with auto-detection and live updates
|
||||
- "Coming soon" preview of the next five planned integrations: context menu actions, smart notifications, RP status
|
||||
block, ExtraChat channels, and quick DM compose
|
||||
- "Coming soon" preview of the next five planned integrations: context menu actions, smart
|
||||
notifications, RP status block, ExtraChat channels, and quick DM compose
|
||||
- Maintainer attribution buttons for Honorific repo and Caraxi
|
||||
- New service-class pattern under HellionChat/Integrations/
|
||||
|
||||
@@ -406,20 +445,22 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
### Added
|
||||
|
||||
- Four new built-in themes:
|
||||
- **Night Blue** — Royal Blue on deep marine, cool tech-dashboard mood
|
||||
- **Indigo Violet** — Royal Violet on deep indigo with a turquoise-mint counter (aurora glitter feel)
|
||||
- **Forge Merchantman** — Patina bronze on workshop slate with warm amber counter (Hellion Forge identity)
|
||||
- **Hellion Spectrum** — Deuteran/Protan-safe channel colours using Wong/Okabe-Ito palette tones; channel identity
|
||||
(Tell pink, Yell yellow, Shout orange, Party blue, FC green) preserved while keeping every channel separable under
|
||||
red-green colour vision deficiency
|
||||
- **Night Blue** — Royal Blue on deep marine, cool tech-dashboard mood
|
||||
- **Indigo Violet** — Royal Violet on deep indigo with a turquoise-mint counter (aurora glitter
|
||||
feel)
|
||||
- **Forge Merchantman** — Patina bronze on workshop slate with warm amber counter (Hellion Forge
|
||||
identity)
|
||||
- **Hellion Spectrum** — Deuteran/Protan-safe channel colours using Wong/Okabe-Ito palette tones;
|
||||
channel identity (Tell pink, Yell yellow, Shout orange, Party blue, FC green) preserved while
|
||||
keeping every channel separable under red-green colour vision deficiency
|
||||
- Built-in theme catalogue grown from five to nine
|
||||
|
||||
### Notes
|
||||
|
||||
- No engine changes, no settings touched, no migration
|
||||
- Default theme unchanged (Hellion Arctic). Existing custom themes keep working.
|
||||
- Hellion Spectrum covers the ~99 % of CVD cases that are red-green; a Tritan-safe variant could follow in a later cycle
|
||||
if there is demand.
|
||||
- Hellion Spectrum covers the ~99 % of CVD cases that are red-green; a Tritan-safe variant could
|
||||
follow in a later cycle if there is demand.
|
||||
|
||||
---
|
||||
|
||||
@@ -427,21 +468,23 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
### Changed
|
||||
|
||||
- Settings cards re-sorted thematically: 9 cards remain, each card has one clear job and a one-line subtitle.
|
||||
- **Theme & Layout** (new) collects the theme picker, window frame style (title bar, sidebar, hide button, pop-out title
|
||||
bar) and the timestamp style options.
|
||||
- Settings cards re-sorted thematically: 9 cards remain, each card has one clear job and a one-line
|
||||
subtitle.
|
||||
- **Theme & Layout** (new) collects the theme picker, window frame style (title bar, sidebar, hide
|
||||
button, pop-out title bar) and the timestamp style options.
|
||||
- **Fonts & Colours** (new) houses font choice, font size and per-channel chat colours.
|
||||
- **Data Management** (new) collects retention windows, cleanup, export, the database viewer and the shift-click
|
||||
advanced tools.
|
||||
- **Data Management** (new) collects retention windows, cleanup, export, the database viewer and the
|
||||
shift-click advanced tools.
|
||||
- **Privacy** is now focused on the privacy filter alone.
|
||||
- **Chat** absorbs the Auto-Tell-Tabs history-preload slider that used to live under Privacy.
|
||||
- **General** groups the keybind mode under Input.
|
||||
|
||||
### Removed
|
||||
|
||||
- Legacy "Style override" option and the unused style-name field — both obsolete since the v1.1.0 themes engine.
|
||||
- Legacy `WindowAlpha` slider — if you had it set, the value is auto-migrated to Theme & Layout → Window Style → Window
|
||||
Transparency.
|
||||
- Legacy "Style override" option and the unused style-name field — both obsolete since the v1.1.0
|
||||
themes engine.
|
||||
- Legacy `WindowAlpha` slider — if you had it set, the value is auto-migrated to Theme & Layout →
|
||||
Window Style → Window Transparency.
|
||||
- Unused `ShowThemeQuickPicker` schema field.
|
||||
|
||||
### Migration
|
||||
@@ -456,82 +499,93 @@ Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
### Added
|
||||
|
||||
- Sidebar tab modernization: icon-only at fixed 44 px, tooltip on hover, vertical accent pill for active tab
|
||||
- Sidebar tab modernization: icon-only at fixed 44 px, tooltip on hover, vertical accent pill for
|
||||
active tab
|
||||
- Top tabs: accent underline pill replaces background fill on active tab
|
||||
- Per-tab custom icons in Settings → Tabs (15-glyph FontAwesome picker)
|
||||
- Bottom status bar (22 px): channel indicator, privacy badge, counters, tells, version — updates 1×/sec
|
||||
- Bottom status bar (22 px): channel indicator, privacy badge, counters, tells, version — updates
|
||||
1×/sec
|
||||
- Card rows as default message render: sender header in channel color, subtle border between cards
|
||||
- Compact-Density toggle in Appearance: switches back to single-line `[HH:mm] Sender: Text` layout
|
||||
- Auto-Tell tabs: per-partner hashed icon (7-glyph pool: envelope/star/heart/bell/bookmark/flag/fire) plus hashed color
|
||||
(12-color palette) — 84 distinct icon+color combinations
|
||||
- Unread indicator: pulsing red dot in the top-right corner of any sidebar tab icon with unread messages, 2-second
|
||||
sine-wave pulse, respects `Configuration.ReduceMotion`
|
||||
- Auto-Tell tabs: per-partner hashed icon (7-glyph pool:
|
||||
envelope/star/heart/bell/bookmark/flag/fire) plus hashed color (12-color palette) — 84 distinct
|
||||
icon+color combinations
|
||||
- Unread indicator: pulsing red dot in the top-right corner of any sidebar tab icon with unread
|
||||
messages, 2-second sine-wave pulse, respects `Configuration.ReduceMotion`
|
||||
|
||||
### Changed
|
||||
|
||||
- Migration v14 → v15: deprecated Configuration fields `HellionThemeEnabled` and `HellionThemeWindowOpacity` removed
|
||||
- Appearance settings cleaned: legacy theme-engine bindings replaced by Themes tab (introduced in v1.1.0)
|
||||
- Migration v14 → v15: deprecated Configuration fields `HellionThemeEnabled` and
|
||||
`HellionThemeWindowOpacity` removed
|
||||
- Appearance settings cleaned: legacy theme-engine bindings replaced by Themes tab (introduced in
|
||||
v1.1.0)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Settings save no longer wipes chat history by default — the heavy `ClearAllTabs + FilterAllTabsAsync` cycle now only
|
||||
runs when a filter-relevant setting actually changed (Privacy filter, persisted channels, per-tab channel selection).
|
||||
Cosmetic changes keep the in-session chat intact
|
||||
- Settings save no longer wipes chat history by default — the heavy
|
||||
`ClearAllTabs + FilterAllTabsAsync` cycle now only runs when a filter-relevant setting actually
|
||||
changed (Privacy filter, persisted channels, per-tab channel selection). Cosmetic changes keep the
|
||||
in-session chat intact
|
||||
- Identifier-based `MessageList` restore in `Configuration.UpdateFrom` plus TempTab skip in
|
||||
`ClearAllTabs`/`FilterAllTabs` ensure persistent tabs and Auto-Tell tabs both survive the save
|
||||
- Sidebar buttons now align vertically with the first message row (top padding mirrors the chat header toolbar height)
|
||||
- Sidebar buttons now align vertically with the first message row (top padding mirrors the chat
|
||||
header toolbar height)
|
||||
- Sidebar child window no longer paints the top padding area with its frame background
|
||||
- Status bar version slot (`vX.Y.Z · Hellion`) no longer clips its rightmost character
|
||||
|
||||
### Notes
|
||||
|
||||
- Polish phase (animations, theme crossfade, header quick-picker) follows in v1.3.0
|
||||
- Top-Tab icon prefixes were considered but dropped: Dalamud's default font atlas does not include FontAwesome
|
||||
codepoints, so mixed-font in a single TabItem label renders as tofu. Underline pill alone is the v1.2.0 visual
|
||||
treatment for top tabs. Resolution would require Font-Atlas merge at FontManager level — out of scope.
|
||||
- Top-Tab icon prefixes were considered but dropped: Dalamud's default font atlas does not include
|
||||
FontAwesome codepoints, so mixed-font in a single TabItem label renders as tofu. Underline pill
|
||||
alone is the v1.2.0 visual treatment for top tabs. Resolution would require Font-Atlas merge at
|
||||
FontManager level — out of scope.
|
||||
|
||||
---
|
||||
|
||||
## [1.1.0] — 2026-05-05 — Theme Foundation
|
||||
|
||||
First major UI cycle after v1.0.0. Theme engine, five built-in themes, custom themes via JSON, settings card grid.
|
||||
First major UI cycle after v1.0.0. Theme engine, five built-in themes, custom themes via JSON,
|
||||
settings card grid.
|
||||
|
||||
### Added
|
||||
|
||||
- **Theme engine** with five built-in themes: Hellion Arctic (default), Chat 2 Classic, Event Horizon, Moonlit Bloom,
|
||||
Mint Grove.
|
||||
- **Settings → Themes** with mini mockup preview per theme. Clicking a card instantly switches the entire plugin (chat,
|
||||
settings, pop-outs).
|
||||
- **Custom themes via JSON** in `pluginConfigs/HellionChat/themes/`. On first start, `example-theme.json` is placed
|
||||
there as a template.
|
||||
- **Optional theme chat channel colours**: themes can ship their own channel colours. On switch, a banner appears with
|
||||
_Apply / Keep current_ — never applied automatically.
|
||||
- **Settings card grid**: new overview on open, clicking a card navigates into the section's detail view. Breadcrumb +
|
||||
ESC navigate back.
|
||||
- **Theme engine** with five built-in themes: Hellion Arctic (default), Chat 2 Classic, Event
|
||||
Horizon, Moonlit Bloom, Mint Grove.
|
||||
- **Settings → Themes** with mini mockup preview per theme. Clicking a card instantly switches the
|
||||
entire plugin (chat, settings, pop-outs).
|
||||
- **Custom themes via JSON** in `pluginConfigs/HellionChat/themes/`. On first start,
|
||||
`example-theme.json` is placed there as a template.
|
||||
- **Optional theme chat channel colours**: themes can ship their own channel colours. On switch, a
|
||||
banner appears with _Apply / Keep current_ — never applied automatically.
|
||||
- **Settings card grid**: new overview on open, clicking a card navigates into the section's detail
|
||||
view. Breadcrumb + ESC navigate back.
|
||||
- **`docs/THEME-AUTHORING.md`** as a guide for writing custom themes, with Hellion Forge branding.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Plugin icon** updated to Hellion Forge hammer (previously a ChatTwo derivative).
|
||||
- **Settings detail view** uses the full width — the second tab list on the left is gone because the card overview
|
||||
handles navigation.
|
||||
- **`HellionStyle.PushGlobal`** is now theme-driven (`PushGlobal(theme, opacity)`) instead of const-palette-driven.
|
||||
- **Configuration v13 → v14**: all users land on `hellion-arctic`. Those who prefer the upstream look can select
|
||||
`chat2-classic` in Settings → Themes.
|
||||
- **Settings detail view** uses the full width — the second tab list on the left is gone because the
|
||||
card overview handles navigation.
|
||||
- **`HellionStyle.PushGlobal`** is now theme-driven (`PushGlobal(theme, opacity)`) instead of
|
||||
const-palette-driven.
|
||||
- **Configuration v13 → v14**: all users land on `hellion-arctic`. Those who prefer the upstream
|
||||
look can select `chat2-classic` in Settings → Themes.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `Configuration.HellionThemeEnabled` and `HellionThemeWindowOpacity` remain readable for one release as a safety net
|
||||
but are no longer evaluated. Removal planned for v1.2.0.
|
||||
- `Configuration.HellionThemeEnabled` and `HellionThemeWindowOpacity` remain readable for one
|
||||
release as a safety net but are no longer evaluated. Removal planned for v1.2.0.
|
||||
|
||||
### Security
|
||||
|
||||
- Custom theme JSON loader validates `schemaVersion`, required fields and hex format. Invalid themes are skipped with a
|
||||
warning; the plugin continues loading with built-ins.
|
||||
- Custom theme JSON loader validates `schemaVersion`, required fields and hex format. Invalid themes
|
||||
are skipped with a warning; the plugin continues loading with built-ins.
|
||||
|
||||
### Internal
|
||||
|
||||
- 51 local unit tests (theme records, registry, JSON round-trip, sanity per built-in theme). Tests are gitignored.
|
||||
- 51 local unit tests (theme records, registry, JSON round-trip, sanity per built-in theme). Tests
|
||||
are gitignored.
|
||||
|
||||
---
|
||||
|
||||
@@ -539,18 +593,20 @@ First major UI cycle after v1.0.0. Theme engine, five built-in themes, custom th
|
||||
|
||||
Four small polish items from the backlog bundled together:
|
||||
|
||||
- **Hide on New Game+ menu**: optional global toggle that hides Hellion Chat (and all other plugin windows such as
|
||||
Settings, DB Viewer, pop-outs) while the NG+ menu is open. Settings → Window → Frame, default off. Skips the entire
|
||||
`WindowSystem.Draw()` path analogous to the existing LoadingScreens pattern.
|
||||
- **Channel selector colouring**: optional tinting of the channel-select button (comment icon) next to the input field
|
||||
in the current channel colour. Settings → Appearance → Chat Colours, default on. Consistent with the existing input
|
||||
text colouring; ExtraChat override is carried over.
|
||||
- **(De)buff icon aspect-ratio fix**: `PayloadHandler.InlineIcon` was squashing all hover icons to 32×32. Status icons
|
||||
with non-square dimensions (debuffs with an arrow indicator) are now shrunk aspect-preserving. Standalone float-math
|
||||
implementation with zero-size guard instead of a cherry-pick from the open ChatTwo PR #157 (which had an int-division
|
||||
trap).
|
||||
- **HideState logging sweep**: all HideState transitions (Battle/Cutscene/User/Override plus pop-out mirroring) log at
|
||||
verbose level. Off by default; enable via `/xllog set HellionChat verbose` for bug-report diagnostics.
|
||||
- **Hide on New Game+ menu**: optional global toggle that hides Hellion Chat (and all other plugin
|
||||
windows such as Settings, DB Viewer, pop-outs) while the NG+ menu is open. Settings → Window →
|
||||
Frame, default off. Skips the entire `WindowSystem.Draw()` path analogous to the existing
|
||||
LoadingScreens pattern.
|
||||
- **Channel selector colouring**: optional tinting of the channel-select button (comment icon) next
|
||||
to the input field in the current channel colour. Settings → Appearance → Chat Colours, default
|
||||
on. Consistent with the existing input text colouring; ExtraChat override is carried over.
|
||||
- **(De)buff icon aspect-ratio fix**: `PayloadHandler.InlineIcon` was squashing all hover icons to
|
||||
32×32. Status icons with non-square dimensions (debuffs with an arrow indicator) are now shrunk
|
||||
aspect-preserving. Standalone float-math implementation with zero-size guard instead of a
|
||||
cherry-pick from the open ChatTwo PR #157 (which had an int-division trap).
|
||||
- **HideState logging sweep**: all HideState transitions (Battle/Cutscene/User/Override plus pop-out
|
||||
mirroring) log at verbose level. Off by default; enable via `/xllog set HellionChat verbose` for
|
||||
bug-report diagnostics.
|
||||
|
||||
[Release Notes 1.0.3](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v1.0.3)
|
||||
|
||||
@@ -558,14 +614,14 @@ Four small polish items from the backlog bundled together:
|
||||
|
||||
## [1.0.1] — 2026-05-04 — Window Position Recovery
|
||||
|
||||
Fixes an off-screen-window scenario the user could end up in after a monitor disconnect or display layout change between
|
||||
sessions. An automatic one-shot bounds check on the first draw after plugin load snaps the window back into the visible
|
||||
viewport, and a new "Reset Window Position" button in Settings → Window → Frame serves as the manual escape hatch for
|
||||
edge cases.
|
||||
Fixes an off-screen-window scenario the user could end up in after a monitor disconnect or display
|
||||
layout change between sessions. An automatic one-shot bounds check on the first draw after plugin
|
||||
load snaps the window back into the visible viewport, and a new "Reset Window Position" button in
|
||||
Settings → Window → Frame serves as the manual escape hatch for edge cases.
|
||||
|
||||
Bundled housekeeping since v1.0.0: documentation restructured into `docs/`, stale ChatTwo/\* paths in repo configs
|
||||
cleaned up, Pidgin parser library bumped from 3.3.0 to 3.5.1, GitHub Actions bumps for `actions/setup-dotnet` (4 → 5)
|
||||
and `github/codeql-action` (3 → 4).
|
||||
Bundled housekeeping since v1.0.0: documentation restructured into `docs/`, stale ChatTwo/\* paths
|
||||
in repo configs cleaned up, Pidgin parser library bumped from 3.3.0 to 3.5.1, GitHub Actions bumps
|
||||
for `actions/setup-dotnet` (4 → 5) and `github/codeql-action` (3 → 4).
|
||||
|
||||
[Release Notes 1.0.1](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v1.0.1)
|
||||
|
||||
@@ -573,11 +629,11 @@ and `github/codeql-action` (3 → 4).
|
||||
|
||||
## [1.0.0] — 2026-05-03 — Standalone Major Release
|
||||
|
||||
First fully independent release. Code namespace, IPC channels and source tree structure consolidated under
|
||||
`HellionChat.*`. Plugin refuses to start alongside an active upstream Chat 2 (bilingual conflict message). SQLite native
|
||||
pinned to 3.50.3 (CVE-2025-6965, CVE-2025-7709). Tab layout default for new installs and users on config version 12 or
|
||||
older restructured (5 thematic tabs instead of 6+ kitchen-sink). Sweep of critical and major findings from the codebase
|
||||
audit incorporated.
|
||||
First fully independent release. Code namespace, IPC channels and source tree structure consolidated
|
||||
under `HellionChat.*`. Plugin refuses to start alongside an active upstream Chat 2 (bilingual
|
||||
conflict message). SQLite native pinned to 3.50.3 (CVE-2025-6965, CVE-2025-7709). Tab layout default
|
||||
for new installs and users on config version 12 or older restructured (5 thematic tabs instead of 6+
|
||||
kitchen-sink). Sweep of critical and major findings from the codebase audit incorporated.
|
||||
|
||||
[Release Notes 1.0.0](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v1.0.0)
|
||||
|
||||
@@ -585,9 +641,9 @@ audit incorporated.
|
||||
|
||||
## [0.6.1] — 2026-05-03 — Pop-Out Discoverability & /tell Auto-Pop-Out
|
||||
|
||||
Pop-out button visible in the chat header, one-time hint banner for the pop-out feature. New setting "Open new /tell
|
||||
tabs directly as pop-out". Pop-out input is now active by default. Bug fixes: ghost windows on LRU-drop / logout, dead
|
||||
zone below the input bar when the hint banner is active.
|
||||
Pop-out button visible in the chat header, one-time hint banner for the pop-out feature. New setting
|
||||
"Open new /tell tabs directly as pop-out". Pop-out input is now active by default. Bug fixes: ghost
|
||||
windows on LRU-drop / logout, dead zone below the input bar when the hint banner is active.
|
||||
|
||||
[Release Notes 0.6.1](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v0.6.1)
|
||||
|
||||
@@ -595,9 +651,10 @@ zone below the input bar when the hint banner is active.
|
||||
|
||||
## [0.6.0] — 2026-05-03 — UX Polish: Pop-Out Input + Colour Presets
|
||||
|
||||
Two opt-in UX features. Pop-out windows optionally get a compact input bar with a channel-coloured icon button and an
|
||||
independent text buffer per pop-out. Seven built-in colour presets (Classic, High Contrast, Pastel, Dark Mode Tuned,
|
||||
Hellion, Night Blue, Indigo Violet) for one-click apply. Configuration migration v10 → v11.
|
||||
Two opt-in UX features. Pop-out windows optionally get a compact input bar with a channel-coloured
|
||||
icon button and an independent text buffer per pop-out. Seven built-in colour presets (Classic, High
|
||||
Contrast, Pastel, Dark Mode Tuned, Hellion, Night Blue, Indigo Violet) for one-click apply.
|
||||
Configuration migration v10 → v11.
|
||||
|
||||
[Release Notes 0.6.0](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v0.6.0)
|
||||
|
||||
@@ -605,9 +662,9 @@ Hellion, Night Blue, Indigo Violet) for one-click apply. Configuration migration
|
||||
|
||||
## [0.5.4] — 2026-05-02 — WrapText Hardening
|
||||
|
||||
`ImGuiUtil.WrapText` rewritten from pointer arithmetic to Span- and index-based control flow. Permanently closes the
|
||||
recurring CodeQL critical alert "unvalidated local pointer arithmetic". No user-visible behaviour change — word-wrap
|
||||
output is byte-identical to 0.5.3.
|
||||
`ImGuiUtil.WrapText` rewritten from pointer arithmetic to Span- and index-based control flow.
|
||||
Permanently closes the recurring CodeQL critical alert "unvalidated local pointer arithmetic". No
|
||||
user-visible behaviour change — word-wrap output is byte-identical to 0.5.3.
|
||||
|
||||
[Release Notes 0.5.4](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v0.5.4)
|
||||
|
||||
@@ -615,8 +672,8 @@ output is byte-identical to 0.5.3.
|
||||
|
||||
## [0.5.3] — 2026-05-02 — Pointer Arithmetic Hardening
|
||||
|
||||
First attempt at closing the CodeQL critical alert in `ImGuiUtil.WrapText`. Encoded byte buffer length is validated via
|
||||
`GetByteCount` before pointer arithmetic.
|
||||
First attempt at closing the CodeQL critical alert in `ImGuiUtil.WrapText`. Encoded byte buffer
|
||||
length is validated via `GetByteCount` before pointer arithmetic.
|
||||
|
||||
[Release Notes 0.5.3](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v0.5.3)
|
||||
|
||||
@@ -624,7 +681,8 @@ First attempt at closing the CodeQL critical alert in `ImGuiUtil.WrapText`. Enco
|
||||
|
||||
## Earlier Versions
|
||||
|
||||
Releases before 0.5.3 (bootstrap phase 0.1.0 to 0.5.2) are available directly on the Gitea release stream:
|
||||
Releases before 0.5.3 (bootstrap phase 0.1.0 to 0.5.2) are available directly on the Gitea release
|
||||
stream:
|
||||
|
||||
[All Releases](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases)
|
||||
|
||||
@@ -632,6 +690,7 @@ Releases before 0.5.3 (bootstrap phase 0.1.0 to 0.5.2) are available directly on
|
||||
|
||||
## Maintenance Note
|
||||
|
||||
The source of truth for the user-facing changelog is the `changelog:` block in `HellionChat/HellionChat.yaml`.
|
||||
`repo.json` and the GitHub release body are fed from there. This file (`docs/CHANGELOG.md`) is a curated summary with
|
||||
links to the release pages and is updated manually on each version bump.
|
||||
The source of truth for the user-facing changelog is the `changelog:` block in
|
||||
`HellionChat/HellionChat.yaml`. `repo.json` and the GitHub release body are fed from there. This
|
||||
file (`docs/CHANGELOG.md`) is a curated summary with links to the release pages and is updated
|
||||
manually on each version bump.
|
||||
|
||||
+47
-37
@@ -1,11 +1,12 @@
|
||||
# Contributors — Hellion Chat
|
||||
|
||||
Hellion Chat is a one-person project on the code side. But without the people on this page, the bug fixes and UX
|
||||
improvements that have landed since the early versions would not exist. Every entry here has made the plugin concretely
|
||||
better.
|
||||
Hellion Chat is a one-person project on the code side. But without the people on this page, the bug
|
||||
fixes and UX improvements that have landed since the early versions would not exist. Every entry
|
||||
here has made the plugin concretely better.
|
||||
|
||||
Attribution for the upstream Chat 2 authors (Infi and Anna) is intentionally in [`../NOTICE.md`](../NOTICE.md), not
|
||||
here. This file covers contributions to the Hellion Chat side specifically.
|
||||
Attribution for the upstream Chat 2 authors (Infi and Anna) is intentionally in
|
||||
[`../NOTICE.md`](../NOTICE.md), not here. This file covers contributions to the Hellion Chat side
|
||||
specifically.
|
||||
|
||||
---
|
||||
|
||||
@@ -13,13 +14,14 @@ here. This file covers contributions to the Hellion Chat side specifically.
|
||||
|
||||
### JonKazama (Florian Wathling) — Maintainer
|
||||
|
||||
Hellion Chat is my first FFXIV plugin and my first larger C#/Dalamud project. My professional background is web
|
||||
development (Next.js, React, TypeScript, Prisma). Plugin development in an unfamiliar codebase, ImGui, FFXIV game hooks
|
||||
and the entire Dalamud stack were new territory.
|
||||
Hellion Chat is my first FFXIV plugin and my first larger C#/Dalamud project. My professional
|
||||
background is web development (Next.js, React, TypeScript, Prisma). Plugin development in an
|
||||
unfamiliar codebase, ImGui, FFXIV game hooks and the entire Dalamud stack were new territory.
|
||||
|
||||
Privacy-first defaults, per-channel retention, Auto-Tell-Tabs, pop-out input, ChatColours presets, the Hellion theme
|
||||
plus Exo 2 font, and the v1.0.0 standalone cut are the Hellion-specific surface areas I built on top of the Chat 2
|
||||
foundation. The learning story behind that is in [`LEARNING-JOURNEY.md`](LEARNING-JOURNEY.md).
|
||||
Privacy-first defaults, per-channel retention, Auto-Tell-Tabs, pop-out input, ChatColours presets,
|
||||
the Hellion theme plus Exo 2 font, and the v1.0.0 standalone cut are the Hellion-specific surface
|
||||
areas I built on top of the Chat 2 foundation. The learning story behind that is in
|
||||
[`LEARNING-JOURNEY.md`](LEARNING-JOURNEY.md).
|
||||
|
||||
Hellion Chat is part of [Hellion Online Media](https://hellion-media.de).
|
||||
|
||||
@@ -27,38 +29,45 @@ Hellion Chat is part of [Hellion Online Media](https://hellion-media.de).
|
||||
|
||||
## Testers
|
||||
|
||||
A quick note: I do not test this plugin alone. The people listed here reported bugs before they hit more users, raised
|
||||
UX problems I had gone blind to, and brought in feature requests that pushed the plugin in directions I would not have
|
||||
gone on my own. That is not a given. External testers are worth their time.
|
||||
A quick note: I do not test this plugin alone. The people listed here reported bugs before they hit
|
||||
more users, raised UX problems I had gone blind to, and brought in feature requests that pushed the
|
||||
plugin in directions I would not have gone on my own. That is not a given. External testers are
|
||||
worth their time.
|
||||
|
||||
### Carl Beleandis (Carla) — Beta Tester
|
||||
|
||||
Carl has been testing since the bootstrap phase and has shaped both the pop-out mechanics and the theme direction.
|
||||
Feedback comes direct and without detours, which is exactly what I need when testing.
|
||||
Carl has been testing since the bootstrap phase and has shaped both the pop-out mechanics and the
|
||||
theme direction. Feedback comes direct and without detours, which is exactly what I need when
|
||||
testing.
|
||||
|
||||
Concrete contributions:
|
||||
|
||||
- **Pop-out discoverability** — pointing out that pop-outs were only reachable via right-click triggered the header
|
||||
button and the one-time hint banner in v0.6.1. I knew the right-click path by heart and had stopped seeing that new
|
||||
users could not find the feature at all.
|
||||
- **/tell pop-out mode** — the request to open /tell tabs directly as a pop-out instead of going through the tab sidebar
|
||||
landed in v0.6.1 as an opt-in settings toggle. Bonus: during implementation an old ghost-window bug surfaced (LRU drop
|
||||
left pop-out windows as ghosts), which got fixed at the same time.
|
||||
- **Theme variants with brightness gradations** — the request for a green family shifted my thinking from "one theme =
|
||||
one colour" to "theme families with mood variants". On the [roadmap](ROADMAP.md) for a later cycle.
|
||||
- **Pop-out discoverability** — pointing out that pop-outs were only reachable via right-click
|
||||
triggered the header button and the one-time hint banner in v0.6.1. I knew the right-click path by
|
||||
heart and had stopped seeing that new users could not find the feature at all.
|
||||
- **/tell pop-out mode** — the request to open /tell tabs directly as a pop-out instead of going
|
||||
through the tab sidebar landed in v0.6.1 as an opt-in settings toggle. Bonus: during
|
||||
implementation an old ghost-window bug surfaced (LRU drop left pop-out windows as ghosts), which
|
||||
got fixed at the same time.
|
||||
- **Theme variants with brightness gradations** — the request for a green family shifted my thinking
|
||||
from "one theme = one colour" to "theme families with mood variants". On the [roadmap](ROADMAP.md)
|
||||
for a later cycle.
|
||||
|
||||
### Jin (Jingliu) — Alpha Tester
|
||||
|
||||
Jin is the active tester from day one and pushed the pop-out workflow architecture in a different direction.
|
||||
Jin is the active tester from day one and pushed the pop-out workflow architecture in a different
|
||||
direction.
|
||||
|
||||
Concrete contributions:
|
||||
|
||||
- **Pop-out tab with input bar** — the suggestion to be able to type in a pop-out (instead of just reading) triggered
|
||||
the v0.6.0 pop-out input bar. That was a larger refactor: the input layer from `ChatLogWindow` had to be opened up so
|
||||
it could also live in `Popout.cs`, with an independent text buffer and history cursor per pop-out. It dominated the
|
||||
cycle because the design had to be clean before any code could happen.
|
||||
- **TempTell persistence** — the request for /tell tabs to survive a relog via a pin toggle is on the
|
||||
[roadmap](ROADMAP.md) for a later cycle. It touches the tab system architecturally and needs its own design work.
|
||||
- **Pop-out tab with input bar** — the suggestion to be able to type in a pop-out (instead of just
|
||||
reading) triggered the v0.6.0 pop-out input bar. That was a larger refactor: the input layer from
|
||||
`ChatLogWindow` had to be opened up so it could also live in `Popout.cs`, with an independent text
|
||||
buffer and history cursor per pop-out. It dominated the cycle because the design had to be clean
|
||||
before any code could happen.
|
||||
- **TempTell persistence** — the request for /tell tabs to survive a relog via a pin toggle is on
|
||||
the [roadmap](ROADMAP.md) for a later cycle. It touches the tab system architecturally and needs
|
||||
its own design work.
|
||||
|
||||
---
|
||||
|
||||
@@ -69,15 +78,16 @@ Hellion-specific UI strings are maintained in `HellionChat/Resources/HellionStri
|
||||
- **German (DE):** JonKazama (native speaker, primary project language)
|
||||
|
||||
Upstream language files (`Language.<lang>.resx`) are not covered here. They are maintained via the
|
||||
[Chat 2 Crowdin project](https://github.com/Infiziert90/ChatTwo); Crowdin translators are listed in the plugin settings
|
||||
under **Info → "Chat 2 community translators"**.
|
||||
[Chat 2 Crowdin project](https://github.com/Infiziert90/ChatTwo); Crowdin translators are listed in
|
||||
the plugin settings under **Info → "Chat 2 community translators"**.
|
||||
|
||||
---
|
||||
|
||||
## How to Contribute
|
||||
|
||||
Bug reports, feature requests and feedback are welcome — the best place to reach me is the Hellion Forge Discord:
|
||||
[discord.gg/X9V7Kcv5gR](https://discord.gg/X9V7Kcv5gR). Join and ping me in the Hellion Chat channel.
|
||||
Bug reports, feature requests and feedback are welcome — the best place to reach me is the Hellion
|
||||
Forge Discord: [discord.gg/X9V7Kcv5gR](https://discord.gg/X9V7Kcv5gR). Join and ping me in the
|
||||
Hellion Chat channel.
|
||||
|
||||
For pull requests and contribution guidelines see [`../CONTRIBUTING.md`](../CONTRIBUTING.md), Code of Conduct in
|
||||
[`../CODE_OF_CONDUCT.md`](../CODE_OF_CONDUCT.md).
|
||||
For pull requests and contribution guidelines see [`../CONTRIBUTING.md`](../CONTRIBUTING.md), Code
|
||||
of Conduct in [`../CODE_OF_CONDUCT.md`](../CODE_OF_CONDUCT.md).
|
||||
|
||||
+47
-38
@@ -1,28 +1,31 @@
|
||||
# Hellion Chat IPC Integration Guide
|
||||
|
||||
This document describes the inter-plugin-communication (IPC) channels that Hellion Chat exposes to other Dalamud
|
||||
plugins. Two integration surfaces are covered: the **Context Menu IPC** for adding custom items to Hellion Chat's
|
||||
right-click menus, and the **Typing State IPC** for reacting to the user's input-box activity.
|
||||
This document describes the inter-plugin-communication (IPC) channels that Hellion Chat exposes to
|
||||
other Dalamud plugins. Two integration surfaces are covered: the **Context Menu IPC** for adding
|
||||
custom items to Hellion Chat's right-click menus, and the **Typing State IPC** for reacting to the
|
||||
user's input-box activity.
|
||||
|
||||
---
|
||||
|
||||
## Compatibility with Chat 2
|
||||
|
||||
Hellion Chat is a standalone fork of [Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2). The IPC surface is one
|
||||
of the parts the fork inherits directly: the same call shapes, the same tuple payloads, the same call semantics, the
|
||||
same lifecycle. We did not redesign the API, we re-published it under our own plugin name.
|
||||
Hellion Chat is a standalone fork of [Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2).
|
||||
The IPC surface is one of the parts the fork inherits directly: the same call shapes, the same tuple
|
||||
payloads, the same call semantics, the same lifecycle. We did not redesign the API, we re-published
|
||||
it under our own plugin name.
|
||||
|
||||
Concretely, this means:
|
||||
|
||||
- **Tuple shapes are identical.** A subscriber that worked against Chat 2's `ChatTwo.Invoke` works against Hellion
|
||||
Chat's `HellionChat.Invoke` without any code change beyond the channel string.
|
||||
- **Lifecycle is identical.** The `Available` ping fires when the plugin becomes ready, your subscriber re-registers,
|
||||
and the registration ID is returned by the same `Register` call as before.
|
||||
- **Channel-name prefix changed in v1.0.0.** Every `ChatTwo.*` channel name is now `HellionChat.*`. Existing third-party
|
||||
integrations need a one-line rename per channel string and nothing else.
|
||||
- **Tuple shapes are identical.** A subscriber that worked against Chat 2's `ChatTwo.Invoke` works
|
||||
against Hellion Chat's `HellionChat.Invoke` without any code change beyond the channel string.
|
||||
- **Lifecycle is identical.** The `Available` ping fires when the plugin becomes ready, your
|
||||
subscriber re-registers, and the registration ID is returned by the same `Register` call as
|
||||
before.
|
||||
- **Channel-name prefix changed in v1.0.0.** Every `ChatTwo.*` channel name is now `HellionChat.*`.
|
||||
Existing third-party integrations need a one-line rename per channel string and nothing else.
|
||||
|
||||
If your plugin already supports Chat 2 and you want to add Hellion Chat support, the cleanest path is to bind both
|
||||
prefixes and treat whichever one becomes available first as the active host.
|
||||
If your plugin already supports Chat 2 and you want to add Hellion Chat support, the cleanest path
|
||||
is to bind both prefixes and treat whichever one becomes available first as the active host.
|
||||
|
||||
---
|
||||
|
||||
@@ -41,18 +44,20 @@ prefixes and treat whichever one becomes available first as the active host.
|
||||
|
||||
## Context Menu IPC
|
||||
|
||||
Use this surface to draw your own selectables inside Hellion Chat's right-click context menus. All registrations are
|
||||
called inside an ImGui `BeginMenu`, so anything you draw appears as a regular menu entry.
|
||||
Use this surface to draw your own selectables inside Hellion Chat's right-click context menus. All
|
||||
registrations are called inside an ImGui `BeginMenu`, so anything you draw appears as a regular menu
|
||||
entry.
|
||||
|
||||
### Lifecycle
|
||||
|
||||
1. Subscribe to `HellionChat.Available`. The host fires this once when it loads or reloads, so your plugin can
|
||||
re-register without polling.
|
||||
2. Call `HellionChat.Register` to obtain a registration ID. Save it. You need it to filter `Invoke` callbacks that
|
||||
target your registration and to call `Unregister` later.
|
||||
3. Subscribe to `HellionChat.Invoke` and draw your menu items inside the handler when the `id` matches your saved
|
||||
registration ID.
|
||||
4. On plugin disable or unload, call `HellionChat.Unregister` with your saved ID and unsubscribe from `Invoke`.
|
||||
1. Subscribe to `HellionChat.Available`. The host fires this once when it loads or reloads, so your
|
||||
plugin can re-register without polling.
|
||||
2. Call `HellionChat.Register` to obtain a registration ID. Save it. You need it to filter `Invoke`
|
||||
callbacks that target your registration and to call `Unregister` later.
|
||||
3. Subscribe to `HellionChat.Invoke` and draw your menu items inside the handler when the `id`
|
||||
matches your saved registration ID.
|
||||
4. On plugin disable or unload, call `HellionChat.Unregister` with your saved ID and unsubscribe
|
||||
from `Invoke`.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -137,12 +142,14 @@ If your plugin already integrates with `ChatTwo.*`, the rename is the only requi
|
||||
|
||||
## Typing State IPC
|
||||
|
||||
Use this surface when you need to know whether the player is currently interacting with Hellion Chat's input box. Useful
|
||||
for typing indicators, keyboard-shortcut suppression, or HUD elements that hide while the user is typing.
|
||||
Use this surface when you need to know whether the player is currently interacting with Hellion
|
||||
Chat's input box. Useful for typing indicators, keyboard-shortcut suppression, or HUD elements that
|
||||
hide while the user is typing.
|
||||
|
||||
### Tuple Payload
|
||||
|
||||
Both `HellionChat.GetChatInputState` (poll) and `HellionChat.ChatInputStateChanged` (event) return the same tuple:
|
||||
Both `HellionChat.GetChatInputState` (poll) and `HellionChat.ChatInputStateChanged` (event) return
|
||||
the same tuple:
|
||||
|
||||
```cs
|
||||
(bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType)
|
||||
@@ -159,17 +166,19 @@ Both `HellionChat.GetChatInputState` (poll) and `HellionChat.ChatInputStateChang
|
||||
|
||||
### Where `ChannelType` comes from
|
||||
|
||||
`ChannelType` is the `HellionChat.Code.ChatType` enum value representing the target channel for the current submission.
|
||||
It is sourced from the active tab's `UsedChannel` (`HellionChat/Configuration.cs`), which the plugin keeps in sync by
|
||||
hooking the in-game shell (`HellionChat/GameFunctions/Chat.cs`) and by resolving temporary overrides inside the chat UI
|
||||
(`HellionChat/Ui/ChatLogWindow.cs:597`). `InputChannel` values are converted into the exported `ChatType` via
|
||||
`HellionChat/Code/InputChannelExt.ToChatType`.
|
||||
`ChannelType` is the `HellionChat.Code.ChatType` enum value representing the target channel for the
|
||||
current submission. It is sourced from the active tab's `UsedChannel`
|
||||
(`HellionChat/Configuration.cs`), which the plugin keeps in sync by hooking the in-game shell
|
||||
(`HellionChat/GameFunctions/Chat.cs`) and by resolving temporary overrides inside the chat UI
|
||||
(`HellionChat/Ui/ChatLogWindow.cs:597`). `InputChannel` values are converted into the exported
|
||||
`ChatType` via `HellionChat/Code/InputChannelExt.ToChatType`.
|
||||
|
||||
### Behavior
|
||||
|
||||
- `ChatInputStateChanged` fires once immediately after subscribe so you do not need a separate `GetChatInputState` poll
|
||||
for the initial snapshot.
|
||||
- After that it fires only when one or more fields actually change, so it is safe to subscribe without rate-limiting.
|
||||
- `ChatInputStateChanged` fires once immediately after subscribe so you do not need a separate
|
||||
`GetChatInputState` poll for the initial snapshot.
|
||||
- After that it fires only when one or more fields actually change, so it is safe to subscribe
|
||||
without rate-limiting.
|
||||
- `GetChatInputState` is available for one-shot polls, e.g. on plugin enable.
|
||||
|
||||
### Example 2
|
||||
@@ -223,7 +232,7 @@ Same shape as the Context Menu surface — only the channel-name prefix needs th
|
||||
|
||||
## License & Attribution
|
||||
|
||||
This guide and the IPC surface it documents derive directly from the Chat 2 codebase. Hellion Chat is licensed under
|
||||
[EUPL-1.2](LICENSE), and credit for the original IPC design and implementation goes to
|
||||
**[Infiziert90 (Infi)](https://github.com/Infiziert90)** and **[Anna](https://github.com/anna-is-cute)**,— see
|
||||
[`NOTICE.md`](NOTICE.md) for full attribution.
|
||||
This guide and the IPC surface it documents derive directly from the Chat 2 codebase. Hellion Chat
|
||||
is licensed under [EUPL-1.2](LICENSE), and credit for the original IPC design and implementation
|
||||
goes to **[Infiziert90 (Infi)](https://github.com/Infiziert90)** and
|
||||
**[Anna](https://github.com/anna-is-cute)**,— see [`NOTICE.md`](NOTICE.md) for full attribution.
|
||||
|
||||
+189
-159
@@ -2,40 +2,43 @@
|
||||
|
||||
## Background
|
||||
|
||||
I am self-taught. Hellion Chat is my first FFXIV plugin and my first larger C# project. My professional background is
|
||||
web development (Next.js, React, TypeScript, Prisma, MySQL) — browser world with a JavaScript toolchain. I knew C# only
|
||||
superficially before this project, ImGui not at all, and Dalamud only as an end user through other plugins.
|
||||
I am self-taught. Hellion Chat is my first FFXIV plugin and my first larger C# project. My
|
||||
professional background is web development (Next.js, React, TypeScript, Prisma, MySQL) — browser
|
||||
world with a JavaScript toolchain. I knew C# only superficially before this project, ImGui not at
|
||||
all, and Dalamud only as an end user through other plugins.
|
||||
|
||||
When I get stuck somewhere, I use AI tools like Claude Code as a pair assistant. What that looks like exactly and which
|
||||
classification I use is documented transparently in [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md).
|
||||
When I get stuck somewhere, I use AI tools like Claude Code as a pair assistant. What that looks
|
||||
like exactly and which classification I use is documented transparently in
|
||||
[`AI_DISCLOSURE.md`](AI_DISCLOSURE.md).
|
||||
|
||||
---
|
||||
|
||||
## Why a chat plugin at all?
|
||||
|
||||
Hellion Chat is not meant to replace Chat 2. Chat 2 delivers a complete chat experience with full history, filters,
|
||||
search and replay. For most users that is exactly the right thing.
|
||||
Hellion Chat is not meant to replace Chat 2. Chat 2 delivers a complete chat experience with full
|
||||
history, filters, search and replay. For most users that is exactly the right thing.
|
||||
|
||||
### Two million messages in two years
|
||||
|
||||
My desire for a tighter default was honestly personal at first. After two years with Chat 2 my database had grown to
|
||||
over two million messages, the majority of them /say, /shout and /yell from complete strangers in Limsa. That is exactly
|
||||
what makes Chat 2's full history useful, and most users are happy to keep it. My own preference wanted a smaller
|
||||
default. So I built this fork.
|
||||
My desire for a tighter default was honestly personal at first. After two years with Chat 2 my
|
||||
database had grown to over two million messages, the majority of them /say, /shout and /yell from
|
||||
complete strangers in Limsa. That is exactly what makes Chat 2's full history useful, and most users
|
||||
are happy to keep it. My own preference wanted a smaller default. So I built this fork.
|
||||
|
||||
### Greeter in several clubs
|
||||
|
||||
There was a second use case: I am active as a greeter in several FFXIV clubs. The vanilla chat interface is not enough
|
||||
for greeter work. Parallel /tell conversations write into a single tab at the same time, and I constantly lose track of
|
||||
who wrote what. Auto-Tell-Tabs (one of the early Hellion Chat features) came directly from this workflow: one tab per
|
||||
conversation partner, automatically spawned, with a manual greeted status. The privacy hygiene benefit was a nice bonus,
|
||||
There was a second use case: I am active as a greeter in several FFXIV clubs. The vanilla chat
|
||||
interface is not enough for greeter work. Parallel /tell conversations write into a single tab at
|
||||
the same time, and I constantly lose track of who wrote what. Auto-Tell-Tabs (one of the early
|
||||
Hellion Chat features) came directly from this workflow: one tab per conversation partner,
|
||||
automatically spawned, with a manual greeted status. The privacy hygiene benefit was a nice bonus,
|
||||
not the trigger.
|
||||
|
||||
### Hellion Online Media
|
||||
|
||||
The privacy defaults also reflect a position from my main work. Hellion Online Media is my sole proprietorship, and data
|
||||
protection toward clients is not a marketing slogan there but operationally relevant. This fork is the plugin form of
|
||||
the same stance.
|
||||
The privacy defaults also reflect a position from my main work. Hellion Online Media is my sole
|
||||
proprietorship, and data protection toward clients is not a marketing slogan there but operationally
|
||||
relevant. This fork is the plugin form of the same stance.
|
||||
|
||||
---
|
||||
|
||||
@@ -45,90 +48,99 @@ Three reasons, in descending order of importance.
|
||||
|
||||
### Defaults are not negotiable, including mine
|
||||
|
||||
Privacy-first as a default is a minority position. Chat 2 rightly serves the broad majority with full history as the
|
||||
default. Changing those defaults upstream would have been wrong. I would have flipped the standard for a large user base
|
||||
that wanted it as it was. A clean separation through a dedicated plugin slot was the more respectful path.
|
||||
Privacy-first as a default is a minority position. Chat 2 rightly serves the broad majority with
|
||||
full history as the default. Changing those defaults upstream would have been wrong. I would have
|
||||
flipped the standard for a large user base that wanted it as it was. A clean separation through a
|
||||
dedicated plugin slot was the more respectful path.
|
||||
|
||||
### The web interface had to go
|
||||
|
||||
It is a central Chat 2 feature for remote access from a second device. A PR removing it has no chance in a
|
||||
well-maintained upstream project, and that is correct. But exactly that web interface conflicts with the privacy-first
|
||||
premise of this fork: a chat plugin that starts a local HTTP server is too large an attack surface for my threat model.
|
||||
So out it went.
|
||||
It is a central Chat 2 feature for remote access from a second device. A PR removing it has no
|
||||
chance in a well-maintained upstream project, and that is correct. But exactly that web interface
|
||||
conflicts with the privacy-first premise of this fork: a chat plugin that starts a local HTTP server
|
||||
is too large an attack surface for my threat model. So out it went.
|
||||
|
||||
### Velocity
|
||||
|
||||
A solo-maintainer project with a small tester pool can iterate faster than an established plugin with a large user base.
|
||||
That is not a criticism of upstream but a different optimization. I do not need roadmap alignment, reviewer
|
||||
availability, or to spread audit consequences like the web interface removal across multiple releases.
|
||||
A solo-maintainer project with a small tester pool can iterate faster than an established plugin
|
||||
with a large user base. That is not a criticism of upstream but a different optimization. I do not
|
||||
need roadmap alignment, reviewer availability, or to spread audit consequences like the web
|
||||
interface removal across multiple releases.
|
||||
|
||||
EUPL-1.2 explicitly allows all of this with clear attribution. The code is open under the same license as Chat 2. Infi,
|
||||
Anna, or anyone else can look in, take ideas, ask questions, or simply ignore the fork. All three are fine with me.
|
||||
EUPL-1.2 explicitly allows all of this with clear attribution. The code is open under the same
|
||||
license as Chat 2. Infi, Anna, or anyone else can look in, take ideas, ask questions, or simply
|
||||
ignore the fork. All three are fine with me.
|
||||
|
||||
---
|
||||
|
||||
## How I release this fast
|
||||
|
||||
Anyone looking at the repo sees a lot of releases and a high commit count in a short time. Both tend to read as red
|
||||
flags from the outside: AI slop, salami tactics, code spam. In Hellion Chat both are deliberate decisions, and I would
|
||||
rather explain them once than justify them later.
|
||||
Anyone looking at the repo sees a lot of releases and a high commit count in a short time. Both tend
|
||||
to read as red flags from the outside: AI slop, salami tactics, code spam. In Hellion Chat both are
|
||||
deliberate decisions, and I would rather explain them once than justify them later.
|
||||
|
||||
### Groundwork, long before the fork existed
|
||||
|
||||
Before I typed the first line into `HellionChat/`, I spent weeks as a reader. Using Chat 2 in-game and playing around
|
||||
with it. Going through issues in the upstream tracker, especially the closed ones, because that is where you see how
|
||||
Infi and Anna narrow down bugs. Reading commits, including older ones, to understand _why_ an architecture decision was
|
||||
made, not just _that_ it was made. If I know today where things live in the codebase, it is not because I navigate
|
||||
codebases particularly fast but because I read the code beforehand.
|
||||
Before I typed the first line into `HellionChat/`, I spent weeks as a reader. Using Chat 2 in-game
|
||||
and playing around with it. Going through issues in the upstream tracker, especially the closed
|
||||
ones, because that is where you see how Infi and Anna narrow down bugs. Reading commits, including
|
||||
older ones, to understand _why_ an architecture decision was made, not just _that_ it was made. If I
|
||||
know today where things live in the codebase, it is not because I navigate codebases particularly
|
||||
fast but because I read the code beforehand.
|
||||
|
||||
That sounds obvious. It is not. The usual order for solo forks is fork first, understand later. I did it the other way
|
||||
around.
|
||||
That sounds obvious. It is not. The usual order for solo forks is fork first, understand later. I
|
||||
did it the other way around.
|
||||
|
||||
One thing I noticed reading the codebase closely: some patterns felt familiar in ways I had not expected, structural
|
||||
choices and comment styles that show up across a lot of modern plugin and tooling code regardless of how it was written.
|
||||
Nothing worth reading into. Coding workflows have changed a lot in the last few years across the board, and the traces
|
||||
of that show up everywhere. It did make me less self-conscious about my own workflow.
|
||||
One thing I noticed reading the codebase closely: some patterns felt familiar in ways I had not
|
||||
expected, structural choices and comment styles that show up across a lot of modern plugin and
|
||||
tooling code regardless of how it was written. Nothing worth reading into. Coding workflows have
|
||||
changed a lot in the last few years across the board, and the traces of that show up everywhere. It
|
||||
did make me less self-conscious about my own workflow.
|
||||
|
||||
### Infi and Anna's codebase
|
||||
|
||||
Hellion Chat builds on a foundation that is already flat. Chat 2 is cleanly structured, naming conventions are
|
||||
consistent, and the separation between layers (storage, UI, game hooks, IPC) is clearly drawn. That is not a given in
|
||||
open-source plugin land, and it is the main reason Hellion-specific features often slot in "almost natively". I do not
|
||||
have to untangle spaghetti before I can put something of my own next to it.
|
||||
Hellion Chat builds on a foundation that is already flat. Chat 2 is cleanly structured, naming
|
||||
conventions are consistent, and the separation between layers (storage, UI, game hooks, IPC) is
|
||||
clearly drawn. That is not a given in open-source plugin land, and it is the main reason
|
||||
Hellion-specific features often slot in "almost natively". I do not have to untangle spaghetti
|
||||
before I can put something of my own next to it.
|
||||
|
||||
Side note: even during the first codebase walkthrough with Claude, the comment came up several times that the
|
||||
architecture is unusually tidy and has several extension points prepared. That carries weight because it comes from
|
||||
outside, but the actual credit goes to Infi and Anna, not Claude.
|
||||
Side note: even during the first codebase walkthrough with Claude, the comment came up several times
|
||||
that the architecture is unusually tidy and has several extension points prepared. That carries
|
||||
weight because it comes from outside, but the actual credit goes to Infi and Anna, not Claude.
|
||||
|
||||
### Atomic work, small commits
|
||||
|
||||
One commit, one logical change. If I fix a bug, rename a variable and add a comment at the same time, that is three
|
||||
commits, not one. Sounds like micro-management, it is not. If a bug surfaces in six months and I need `git bisect`, I
|
||||
find the broken change in two minutes instead of two hours. With a 4000-line mega-commit I get to guess which of the
|
||||
hundred changes is the broken one.
|
||||
One commit, one logical change. If I fix a bug, rename a variable and add a comment at the same
|
||||
time, that is three commits, not one. Sounds like micro-management, it is not. If a bug surfaces in
|
||||
six months and I need `git bisect`, I find the broken change in two minutes instead of two hours.
|
||||
With a 4000-line mega-commit I get to guess which of the hundred changes is the broken one.
|
||||
|
||||
I kept this style deliberately also because Infi works the same way upstream. Sometimes a six-line commit, sometimes
|
||||
just a typo fix. That is not a weakness, it is a decision for readable Git history. Keeping the style in the fork is a
|
||||
respect move: anyone comparing both repos should have the same reading rhythm.
|
||||
I kept this style deliberately also because Infi works the same way upstream. Sometimes a six-line
|
||||
commit, sometimes just a typo fix. That is not a weakness, it is a decision for readable Git
|
||||
history. Keeping the style in the fork is a respect move: anyone comparing both repos should have
|
||||
the same reading rhythm.
|
||||
|
||||
Personal bonus: small commits force me to think through and name each step individually. If I cannot explain what a
|
||||
commit does in two sentences, the change is probably not clear enough yet. At beginner level that is a built-in sanity
|
||||
check I would not have with a big-bang commit.
|
||||
Personal bonus: small commits force me to think through and name each step individually. If I cannot
|
||||
explain what a commit does in two sentences, the change is probably not clear enough yet. At
|
||||
beginner level that is a built-in sanity check I would not have with a big-bang commit.
|
||||
|
||||
### AI as an accelerator, honestly
|
||||
|
||||
Yes, AI helps with velocity, and not a little. Without CodeRabbit I would not have found critical bugs like
|
||||
`Equals/GetHashCode` anti-patterns, hook subscription leaks and TOCTOU races. I am simply too inexperienced for that
|
||||
class of findings, and I write that exactly as it is.
|
||||
Yes, AI helps with velocity, and not a little. Without CodeRabbit I would not have found critical
|
||||
bugs like `Equals/GetHashCode` anti-patterns, hook subscription leaks and TOCTOU races. I am simply
|
||||
too inexperienced for that class of findings, and I write that exactly as it is.
|
||||
|
||||
What I do not do: blindly take code because a tool marked it as a fix. On several CodeRabbit findings, the original
|
||||
commits from Infi or Anna even included a Stack Overflow link explaining why a particular spot looks the way it does. I
|
||||
read those before touching anything. Understand first, then change, then commit. That is the difference between "AI
|
||||
gives me code, I push" and "AI shows me where it breaks, I decide".
|
||||
What I do not do: blindly take code because a tool marked it as a fix. On several CodeRabbit
|
||||
findings, the original commits from Infi or Anna even included a Stack Overflow link explaining why
|
||||
a particular spot looks the way it does. I read those before touching anything. Understand first,
|
||||
then change, then commit. That is the difference between "AI gives me code, I push" and "AI shows me
|
||||
where it breaks, I decide".
|
||||
|
||||
Classification and concrete examples of AI usage are in [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md). This section was only
|
||||
about the velocity aspect: research plus a clean codebase plus atomic commits plus AI-assisted review sparring are the
|
||||
four factors together. No single one explains the pace on its own.
|
||||
Classification and concrete examples of AI usage are in [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md). This
|
||||
section was only about the velocity aspect: research plus a clean codebase plus atomic commits plus
|
||||
AI-assisted review sparring are the four factors together. No single one explains the pace on its
|
||||
own.
|
||||
|
||||
---
|
||||
|
||||
@@ -136,48 +148,53 @@ four factors together. No single one explains the pace on its own.
|
||||
|
||||
### Type system? Less of a shock than expected
|
||||
|
||||
C# after TypeScript was more comfortable than expected. Properties instead of getters/setters are clean, nullable
|
||||
reference types feel like `strict: true` in TypeScript. What was unfamiliar was having to think explicitly about value
|
||||
types versus reference types (`struct` vs. `class` with real behavioural consequences), and generics with constraints
|
||||
are syntactically different enough that I stumble on them while reading. `async`/`await` is semantically similar, but
|
||||
threading models are more explicit in C#: `Task.Run`, `ConfigureAwait`, synchronization contexts. That cost me several
|
||||
bugs before I understood when the main thread (in plugin land: the framework tick) is actually critical.
|
||||
C# after TypeScript was more comfortable than expected. Properties instead of getters/setters are
|
||||
clean, nullable reference types feel like `strict: true` in TypeScript. What was unfamiliar was
|
||||
having to think explicitly about value types versus reference types (`struct` vs. `class` with real
|
||||
behavioural consequences), and generics with constraints are syntactically different enough that I
|
||||
stumble on them while reading. `async`/`await` is semantically similar, but threading models are
|
||||
more explicit in C#: `Task.Run`, `ConfigureAwait`, synchronization contexts. That cost me several
|
||||
bugs before I understood when the main thread (in plugin land: the framework tick) is actually
|
||||
critical.
|
||||
|
||||
### Build toolchain: similar, but different
|
||||
|
||||
`dotnet` CLI, csproj XML, NuGet are functionally not far from npm and tsconfig. But the XML format of csproj is a
|
||||
different language than JSON configs. The lock file (`packages.lock.json`) had to be actively enabled
|
||||
(`RestorePackagesWithLockFile=true`); that is not the default. In the web stack, lock-file-first is standard, in the
|
||||
.NET stack apparently not. That was a real surprise.
|
||||
`dotnet` CLI, csproj XML, NuGet are functionally not far from npm and tsconfig. But the XML format
|
||||
of csproj is a different language than JSON configs. The lock file (`packages.lock.json`) had to be
|
||||
actively enabled (`RestorePackagesWithLockFile=true`); that is not the default. In the web stack,
|
||||
lock-file-first is standard, in the .NET stack apparently not. That was a real surprise.
|
||||
|
||||
### ImGui is a different world
|
||||
|
||||
Immediate-mode rendering has nothing in common with React component trees. There is no virtual DOM, no reconciliation,
|
||||
no "component state". Every frame the code redraws the UI from scratch, and state lives either in local variables I
|
||||
manage myself or in ImGui's own ID stack logic.
|
||||
Immediate-mode rendering has nothing in common with React component trees. There is no virtual DOM,
|
||||
no reconciliation, no "component state". Every frame the code redraws the UI from scratch, and state
|
||||
lives either in local variables I manage myself or in ImGui's own ID stack logic.
|
||||
|
||||
What is two lines of `useState` in React is a member field plus manual ID stamps on widgets in ImGui, otherwise two
|
||||
selectables in the same loop collide because they fall back to the same ID. The ID stack collision in `SearchSelector`
|
||||
(fixed in v1.0.0) was exactly that symptom: all selectables fell back to the same ambiguous ID until I mixed the row
|
||||
index into the PushID. Classic "why is the wrong entry getting clicked" bug that you only find once you understand how
|
||||
ImGui handles IDs internally.
|
||||
What is two lines of `useState` in React is a member field plus manual ID stamps on widgets in
|
||||
ImGui, otherwise two selectables in the same loop collide because they fall back to the same ID. The
|
||||
ID stack collision in `SearchSelector` (fixed in v1.0.0) was exactly that symptom: all selectables
|
||||
fell back to the same ambiguous ID until I mixed the row index into the PushID. Classic "why is the
|
||||
wrong entry getting clicked" bug that you only find once you understand how ImGui handles IDs
|
||||
internally.
|
||||
|
||||
### Dalamud specifics
|
||||
|
||||
Plugin lifecycle, IPC subscriber pattern, hook system for game functions, game object threading. Much of that was only
|
||||
understandable through reading the upstream codebase and through [dalamud.dev](https://dalamud.dev). Search results for
|
||||
"Dalamud" often turn up outdated API examples from old versions. dalamud.dev is the reliable source. If someone is just
|
||||
starting out: go there, not to Stack Overflow.
|
||||
Plugin lifecycle, IPC subscriber pattern, hook system for game functions, game object threading.
|
||||
Much of that was only understandable through reading the upstream codebase and through
|
||||
[dalamud.dev](https://dalamud.dev). Search results for "Dalamud" often turn up outdated API examples
|
||||
from old versions. dalamud.dev is the reliable source. If someone is just starting out: go there,
|
||||
not to Stack Overflow.
|
||||
|
||||
### The day DalamudPackager cost me a day
|
||||
|
||||
Dalamud SDK 15 ships its own default packager that writes icons and image URLs into the manifest. I had carried over a
|
||||
`DalamudPackager.targets` file from the upstream repo with a `HandleImages` override, and it was overriding the SDK
|
||||
default. Result: the manifest had no `IconUrl` anymore, and the plugin appeared in the plugin list without an icon.
|
||||
Dalamud SDK 15 ships its own default packager that writes icons and image URLs into the manifest. I
|
||||
had carried over a `DalamudPackager.targets` file from the upstream repo with a `HandleImages`
|
||||
override, and it was overriding the SDK default. Result: the manifest had no `IconUrl` anymore, and
|
||||
the plugin appeared in the plugin list without an icon.
|
||||
|
||||
The symptom was easy to spot, the cause cost a day. I had treated the override file as mandatory when it was not.
|
||||
Removed in v0.5.2, SDK default running since then. Lesson: start with defaults, add overrides only when the default
|
||||
demonstrably does not fit.
|
||||
The symptom was easy to spot, the cause cost a day. I had treated the override file as mandatory
|
||||
when it was not. Removed in v0.5.2, SDK default running since then. Lesson: start with defaults, add
|
||||
overrides only when the default demonstrably does not fit.
|
||||
|
||||
---
|
||||
|
||||
@@ -185,73 +202,82 @@ demonstrably does not fit.
|
||||
|
||||
### Refactoring in an unfamiliar codebase
|
||||
|
||||
The standalone cut in v1.0.0 migrated the entire `ChatTwo.*` identity to `HellionChat.*`. That sounds like find and
|
||||
replace. It was not.
|
||||
The standalone cut in v1.0.0 migrated the entire `ChatTwo.*` identity to `HellionChat.*`. That
|
||||
sounds like find and replace. It was not.
|
||||
|
||||
In concrete terms: code namespace across all 80 source files plus 100 using directives plus two FQN aliases plus the
|
||||
resource designer strings. Six IPC channels renamed (breaking change for third-party plugins, no known integrations).
|
||||
Repo folder structure (`ChatTwo/` -> `HellionChat/`) including csproj, sln, all GitHub workflows and dependabot.yml.
|
||||
Public-facing branding in README, repo.json and yaml reformulated to standalone framing.
|
||||
In concrete terms: code namespace across all 80 source files plus 100 using directives plus two FQN
|
||||
aliases plus the resource designer strings. Six IPC channels renamed (breaking change for
|
||||
third-party plugins, no known integrations). Repo folder structure (`ChatTwo/` -> `HellionChat/`)
|
||||
including csproj, sln, all GitHub workflows and dependabot.yml. Public-facing branding in README,
|
||||
repo.json and yaml reformulated to standalone framing.
|
||||
|
||||
It was not a solo find-and-replace because Unicode string paths in workflow YAMLs need different quoting than C#
|
||||
strings. Because resource designer files have generated content that not every toolchain tracks. And because the
|
||||
`ChatTwo.*` IPC channel names are strings in `GetIpcSubscriber` calls: no symbol, no compile error if you miss one. That
|
||||
is when you find out what stays quiet.
|
||||
It was not a solo find-and-replace because Unicode string paths in workflow YAMLs need different
|
||||
quoting than C# strings. Because resource designer files have generated content that not every
|
||||
toolchain tracks. And because the `ChatTwo.*` IPC channel names are strings in `GetIpcSubscriber`
|
||||
calls: no symbol, no compile error if you miss one. That is when you find out what stays quiet.
|
||||
|
||||
### Security is no longer abstract
|
||||
|
||||
Before this project, supply chain security was academic for me. Three concrete lessons changed that.
|
||||
|
||||
**SQLite native binary.** I had to pin to 3.50.3 (`SQLitePCLRaw.lib.e_sqlite3` override) because `Microsoft.Data.Sqlite`
|
||||
was pulling in a transitively referenced library at a version containing CVE-2025-6965 (memory corruption via aggregate
|
||||
term overflow) and CVE-2025-7709. The managed wrapper was new; the native library was not. Lesson: transitive
|
||||
dependencies do not audit themselves, you have to look.
|
||||
**SQLite native binary.** I had to pin to 3.50.3 (`SQLitePCLRaw.lib.e_sqlite3` override) because
|
||||
`Microsoft.Data.Sqlite` was pulling in a transitively referenced library at a version containing
|
||||
CVE-2025-6965 (memory corruption via aggregate term overflow) and CVE-2025-7709. The managed wrapper
|
||||
was new; the native library was not. Lesson: transitive dependencies do not audit themselves, you
|
||||
have to look.
|
||||
|
||||
**Lock file drift.** `packages.lock.json` honoured via `RestorePackagesWithLockFile=true` in the csproj prevents
|
||||
transitive versions from silently drifting between my machine and CI. I only understood why this is not the default
|
||||
after a build output mismatch between local and GitHub Actions.
|
||||
**Lock file drift.** `packages.lock.json` honoured via `RestorePackagesWithLockFile=true` in the
|
||||
csproj prevents transitive versions from silently drifting between my machine and CI. I only
|
||||
understood why this is not the default after a build output mismatch between local and GitHub
|
||||
Actions.
|
||||
|
||||
**WrapText and the CodeQL alert that cost three releases.** CodeQL flagged a critical alert in `ImGuiUtil.WrapText` for
|
||||
unvalidated local pointer arithmetic. v0.5.2 validated an edge case. Alert came back. v0.5.3 checked buffer length via
|
||||
`GetByteCount` before the pointer math. Alert came back. v0.5.4 rebuilt the whole algorithm on `Span` and int offsets
|
||||
with a 16 KiB cap on the ArrayPool rent. Only then did it go quiet.
|
||||
**WrapText and the CodeQL alert that cost three releases.** CodeQL flagged a critical alert in
|
||||
`ImGuiUtil.WrapText` for unvalidated local pointer arithmetic. v0.5.2 validated an edge case. Alert
|
||||
came back. v0.5.3 checked buffer length via `GetByteCount` before the pointer math. Alert came back.
|
||||
v0.5.4 rebuilt the whole algorithm on `Span` and int offsets with a 16 KiB cap on the ArrayPool
|
||||
rent. Only then did it go quiet.
|
||||
|
||||
Lesson: when a static analyser complains three times in a row, the analyser is not oversensitive. The data flow logic
|
||||
is.
|
||||
Lesson: when a static analyser complains three times in a row, the analyser is not oversensitive.
|
||||
The data flow logic is.
|
||||
|
||||
### CodeRabbit as an external code reviewer
|
||||
|
||||
The v1.0.0 sweep surfaced 3 critical and 21 major findings. Three classes were particularly instructive:
|
||||
The v1.0.0 sweep surfaced 3 critical and 21 major findings. Three classes were particularly
|
||||
instructive:
|
||||
|
||||
- **`Equals` methods comparing `GetHashCode()`.** Classic hash collision anti-pattern. Sounds like "if hashes are equal
|
||||
the objects are equal", which is exactly backwards. Hashes can collide; the objects are not equal.
|
||||
- **`Dispose` methods that only unsubscribe part of their subscriptions.** Leak on every plugin reload. In normal use
|
||||
you do not notice it immediately; in a long-running test you do.
|
||||
- **TOCTOU races.** Between a bounds check and a read another thread can swap out the array underneath you
|
||||
(`GlobalParametersCache`, `AutoTranslate`).
|
||||
- **`Equals` methods comparing `GetHashCode()`.** Classic hash collision anti-pattern. Sounds like
|
||||
"if hashes are equal the objects are equal", which is exactly backwards. Hashes can collide; the
|
||||
objects are not equal.
|
||||
- **`Dispose` methods that only unsubscribe part of their subscriptions.** Leak on every plugin
|
||||
reload. In normal use you do not notice it immediately; in a long-running test you do.
|
||||
- **TOCTOU races.** Between a bounds check and a read another thread can swap out the array
|
||||
underneath you (`GlobalParametersCache`, `AutoTranslate`).
|
||||
|
||||
I had at best read the theory on all of these before, never diagnosed them in my own code. CodeRabbit was the moment
|
||||
where "academic knowledge" became "okay, that is my code, that is my bug".
|
||||
I had at best read the theory on all of these before, never diagnosed them in my own code.
|
||||
CodeRabbit was the moment where "academic knowledge" became "okay, that is my code, that is my bug".
|
||||
|
||||
### External testers are worth their weight
|
||||
|
||||
Carla's feedback on pop-out discoverability triggered the header button in v0.6.1. That pop-outs were only reachable via
|
||||
right-click was something I as maintainer had stopped seeing; I knew the path by heart. Carl's request for theme
|
||||
variants with brightness gradations shifted my thinking from "one theme = one colour" to "theme families with mood
|
||||
variants". Jingliu asked for TempTell persistence, which puts the tab system architecturally into question.
|
||||
Carla's feedback on pop-out discoverability triggered the header button in v0.6.1. That pop-outs
|
||||
were only reachable via right-click was something I as maintainer had stopped seeing; I knew the
|
||||
path by heart. Carl's request for theme variants with brightness gradations shifted my thinking from
|
||||
"one theme = one colour" to "theme families with mood variants". Jingliu asked for TempTell
|
||||
persistence, which puts the tab system architecturally into question.
|
||||
|
||||
Solo I would not have seen any of those three things. Full stop.
|
||||
|
||||
### release.yml and the YAML rabbit hole
|
||||
|
||||
The `release.yml` workflow simply did not fire on the first v0.6.0 tag push. I dug through permissions, secret scopes
|
||||
and tag trigger configuration for hours before I understood what was actually happening: the PowerShell heredoc footer
|
||||
in the "Generate release body" step contained a `---` Markdown horizontal rule at column 1, and that terminated the YAML
|
||||
block scalar of `run: |`. GitHub could not parse the workflow file, so the push-tag trigger never registered.
|
||||
The `release.yml` workflow simply did not fire on the first v0.6.0 tag push. I dug through
|
||||
permissions, secret scopes and tag trigger configuration for hours before I understood what was
|
||||
actually happening: the PowerShell heredoc footer in the "Generate release body" step contained a
|
||||
`---` Markdown horizontal rule at column 1, and that terminated the YAML block scalar of `run: |`.
|
||||
GitHub could not parse the workflow file, so the push-tag trigger never registered.
|
||||
|
||||
Fix: extracted the footer into an external `.github/release-footer.md`, workflow reads it via `Get-Content`. Lesson: if
|
||||
a workflow does not trigger, verify first that GitHub can even parse the file. That was one of the bugs where I laughed
|
||||
briefly after the fix and then asked myself how many other YAML files I had that might have the same trap in them.
|
||||
Fix: extracted the footer into an external `.github/release-footer.md`, workflow reads it via
|
||||
`Get-Content`. Lesson: if a workflow does not trigger, verify first that GitHub can even parse the
|
||||
file. That was one of the bugs where I laughed briefly after the fix and then asked myself how many
|
||||
other YAML files I had that might have the same trap in them.
|
||||
|
||||
---
|
||||
|
||||
@@ -259,29 +285,31 @@ briefly after the fix and then asked myself how many other YAML files I had that
|
||||
|
||||
### Performance profiling in a game context
|
||||
|
||||
The FPS drop bug from upstream Chat 2 ([#145](https://github.com/Infiziert90/ChatTwo/issues/145)) has not been
|
||||
reproduced or verified in Hellion Chat. v1.0.0 applied several fixes on the suspected paths (DbViewer O(N²) -> O(N),
|
||||
AutoTranslate lock serialisation, EmoteCache HttpClient reuse), but systematic measurement under load is missing. I
|
||||
still need to learn how to properly measure what is actually consuming the frame budget in a plugin context.
|
||||
The FPS drop bug from upstream Chat 2 ([#145](https://github.com/Infiziert90/ChatTwo/issues/145))
|
||||
has not been reproduced or verified in Hellion Chat. v1.0.0 applied several fixes on the suspected
|
||||
paths (DbViewer O(N²) -> O(N), AutoTranslate lock serialisation, EmoteCache HttpClient reuse), but
|
||||
systematic measurement under load is missing. I still need to learn how to properly measure what is
|
||||
actually consuming the frame budget in a plugin context.
|
||||
|
||||
### Native interop and pointer math
|
||||
|
||||
Even after the WrapText Span refactor in v0.5.4, pointer math makes me uneasy. ImGui forces you into `unsafe` code in
|
||||
several places, and the safety margin from the "unbounded ArrayPool allocation" class of bugs is narrower than I would
|
||||
like. I want to get better at that before touching deeper ImGui custom drawing.
|
||||
Even after the WrapText Span refactor in v0.5.4, pointer math makes me uneasy. ImGui forces you into
|
||||
`unsafe` code in several places, and the safety margin from the "unbounded ArrayPool allocation"
|
||||
class of bugs is narrower than I would like. I want to get better at that before touching deeper
|
||||
ImGui custom drawing.
|
||||
|
||||
### Test discipline for plugin code
|
||||
|
||||
The repo currently has no test project. That is a deliberate decision, not a forgotten one. Testing plugin code with
|
||||
FFXIV hooks and Dalamud lifecycle cleanly is non-trivial, and I had not found an approach that made sense without a
|
||||
large mocking scaffold. Privacy filter and configuration migration would be good test candidates because they are
|
||||
isolated. On the list, but not a quick win.
|
||||
The repo currently has no test project. That is a deliberate decision, not a forgotten one. Testing
|
||||
plugin code with FFXIV hooks and Dalamud lifecycle cleanly is non-trivial, and I had not found an
|
||||
approach that made sense without a large mocking scaffold. Privacy filter and configuration
|
||||
migration would be good test candidates because they are isolated. On the list, but not a quick win.
|
||||
|
||||
### Linux quirks under Wine
|
||||
|
||||
XDG compliance, libnotify integration, WireGuard network detection, all on the [roadmap](ROADMAP.md), and all
|
||||
technically still unclear. Wine and sandboxed plugin code do not share all system APIs, and I do not know where the
|
||||
pitfalls are until I have found them.
|
||||
XDG compliance, libnotify integration, WireGuard network detection, all on the
|
||||
[roadmap](ROADMAP.md), and all technically still unclear. Wine and sandboxed plugin code do not
|
||||
share all system APIs, and I do not know where the pitfalls are until I have found them.
|
||||
|
||||
---
|
||||
|
||||
@@ -303,10 +331,12 @@ I use Claude Code as an assistant, not as a replacement for my own work.
|
||||
- Tester communication and roadmap prioritisation
|
||||
- Reviewing, verifying, pushing
|
||||
|
||||
Classification and concrete examples are in [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md). It matters to me that users and
|
||||
potential contributors understand how the code came together, especially for a plugin that handles user data.
|
||||
Classification and concrete examples are in [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md). It matters to me
|
||||
that users and potential contributors understand how the code came together, especially for a plugin
|
||||
that handles user data.
|
||||
|
||||
Yes, AI. Yes, alone. Both mentioned more than strictly necessary. Welcome to the open-source plugin climate.
|
||||
Yes, AI. Yes, alone. Both mentioned more than strictly necessary. Welcome to the open-source plugin
|
||||
climate.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+238
-202
@@ -1,251 +1,281 @@
|
||||
# Hellion Chat — Roadmap
|
||||
|
||||
Planned work after the v1.0.0 standalone cut. This list is intentionally high-level: concrete specs, size estimates and
|
||||
repro steps live in the internal backlog. External tracking runs via
|
||||
[Gitea Issues](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues) with the `roadmap` label once an
|
||||
item is scheduled for a cycle.
|
||||
Planned work after the v1.0.0 standalone cut. This list is intentionally high-level: concrete specs,
|
||||
size estimates and repro steps live in the internal backlog. External tracking runs via
|
||||
[Gitea Issues](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues) with the
|
||||
`roadmap` label once an item is scheduled for a cycle.
|
||||
|
||||
Order reflects priority, not a guarantee. Items may shift or be dropped entirely if they turn out to be a poor fit for
|
||||
the plugin's privacy-first scope during brainstorming.
|
||||
Order reflects priority, not a guarantee. Items may shift or be dropped entirely if they turn out to
|
||||
be a poor fit for the plugin's privacy-first scope during brainstorming.
|
||||
|
||||
---
|
||||
|
||||
## Next Cycle (v1.5.1)
|
||||
|
||||
**Honorific Full Gradient Port plus FontAtlas-Defer for a 10× HITCH cut.** v1.5.0 closed the DI-container cycle with
|
||||
no performance penalty against Chat 2 (77 ms vs 74 ms median first-frame HITCH), but the cross-plugin baseline against
|
||||
Lightless Sync and XIVInstantMessenger surfaced a clean optimisation: both plugins defer their font-atlas build until
|
||||
after `Finished loading` and sit at 6-7 ms HITCH, an order of magnitude below the ~75 ms floor that Chat 2 and HellionChat
|
||||
share. v1.5.1 ports that pattern. Plus the Honorific gradient render path — DTO is gradient-ready since v1.4.7, only the
|
||||
Wave / Pulse animation port remains. After that, First-Run-Wizard rework with curated defaults beyond the three privacy
|
||||
profiles, then FR localisation (Hezcal native-speaker review confirmed), then the Plugin Integrations Wave 2-6
|
||||
(Context-Menu, NotificationMaster, Moodles, ExtraChat, XIVIM Quick-DM). Wine/Linux scroll-rubber-band spike sits as a
|
||||
low-priority Linux-only investigation at the tail.
|
||||
**Honorific Full Gradient Port plus FontAtlas-Defer for a 10× HITCH cut.** v1.5.0 closed the
|
||||
DI-container cycle with no performance penalty against Chat 2 (77 ms vs 74 ms median first-frame
|
||||
HITCH), but the cross-plugin baseline against Lightless Sync and XIVInstantMessenger surfaced a
|
||||
clean optimisation: both plugins defer their font-atlas build until after `Finished loading` and sit
|
||||
at 6-7 ms HITCH, an order of magnitude below the ~75 ms floor that Chat 2 and HellionChat share.
|
||||
v1.5.1 ports that pattern. Plus the Honorific gradient render path — DTO is gradient-ready since
|
||||
v1.4.7, only the Wave / Pulse animation port remains. After that, First-Run-Wizard rework with
|
||||
curated defaults beyond the three privacy profiles, then FR localisation (Hezcal native-speaker
|
||||
review confirmed), then the Plugin Integrations Wave 2-6 (Context-Menu, NotificationMaster, Moodles,
|
||||
ExtraChat, XIVIM Quick-DM). Wine/Linux scroll-rubber-band spike sits as a low-priority Linux-only
|
||||
investigation at the tail.
|
||||
|
||||
---
|
||||
|
||||
## v1.5.0 — DI Foundation and Service Refactor (released 2026-05-17)
|
||||
|
||||
Major architecture cycle. Plugin bootstrap moves to a generic-host DI container
|
||||
(`Microsoft.Extensions.Hosting` + `IServiceCollection`) modelled on Lightless Sync's `PluginHostFactory`. Service
|
||||
logging migrates from the static `Plugin.LogProxy` locator (the F12.2 shim from v1.4.7) to typed
|
||||
`Microsoft.Extensions.Logging.ILogger<T>` via constructor injection, bridged over Dalamud's `IPluginLog` by a custom
|
||||
`DalamudLogger` trio. 18 instance-class services move to ctor-injected loggers across four slices: data layer,
|
||||
IPC/integrations, UI window layer, and root. `Plugin.LogProxy` stays for the eight buckets ctor injection cannot
|
||||
reach — static helpers (`EmoteCache`, `AutoTranslate`, `MemoryUtil`, `WrapperUtil`), Dalamud-reflected types
|
||||
(`Configuration`), the `Message` data class, and instance classes that only log from static methods (`FontManager`,
|
||||
one `GameFunctions` site). Plugin.cs finishes at 1012 lines, virtually identical to the pre-cycle 1013 (-1 netto): the
|
||||
new Phase-1 host build and `Plugin.X` bridge wiring trade out exactly the service and window allocations that previously
|
||||
lived in `LoadAsync`. Cross-plugin baseline (10 reload-stress runs, 51 active plugins): HellionChat first-frame HITCH
|
||||
77 ms median, Chat 2 v1.40.2 74 ms median — no DI penalty. The deferred-font-atlas pattern from Lightless and
|
||||
XIVInstantMessenger is the v1.5.1 follow-up. User-visible: slash-command insert fix cherry-picked from ChatTwo upstream
|
||||
`ee7768ac` — pasting a slash command into the chat input now replaces existing input instead of concatenating.
|
||||
Migration v17 stays.
|
||||
(`Microsoft.Extensions.Hosting` + `IServiceCollection`) modelled on Lightless Sync's
|
||||
`PluginHostFactory`. Service logging migrates from the static `Plugin.LogProxy` locator (the F12.2
|
||||
shim from v1.4.7) to typed `Microsoft.Extensions.Logging.ILogger<T>` via constructor injection,
|
||||
bridged over Dalamud's `IPluginLog` by a custom `DalamudLogger` trio. 18 instance-class services
|
||||
move to ctor-injected loggers across four slices: data layer, IPC/integrations, UI window layer, and
|
||||
root. `Plugin.LogProxy` stays for the eight buckets ctor injection cannot reach — static helpers
|
||||
(`EmoteCache`, `AutoTranslate`, `MemoryUtil`, `WrapperUtil`), Dalamud-reflected types
|
||||
(`Configuration`), the `Message` data class, and instance classes that only log from static methods
|
||||
(`FontManager`, one `GameFunctions` site). Plugin.cs finishes at 1012 lines, virtually identical to
|
||||
the pre-cycle 1013 (-1 netto): the new Phase-1 host build and `Plugin.X` bridge wiring trade out
|
||||
exactly the service and window allocations that previously lived in `LoadAsync`. Cross-plugin
|
||||
baseline (10 reload-stress runs, 51 active plugins): HellionChat first-frame HITCH 77 ms median,
|
||||
Chat 2 v1.40.2 74 ms median — no DI penalty. The deferred-font-atlas pattern from Lightless and
|
||||
XIVInstantMessenger is the v1.5.1 follow-up. User-visible: slash-command insert fix cherry-picked
|
||||
from ChatTwo upstream `ee7768ac` — pasting a slash command into the chat input now replaces existing
|
||||
input instead of concatenating. Migration v17 stays.
|
||||
|
||||
---
|
||||
|
||||
## v1.4.10 — Symbol-Picker and Tell-History Fix (released 2026-05-16)
|
||||
|
||||
Eleventh and final sub-patch of the v1.4.x Polish Sweep series. Symbol picker for the chat input — popup with two tabs
|
||||
(161 FFXIV PUA glyphs via Dalamud's SeIconChar plus 97 server-verified BMP symbols probed through `/echo` and `/say` in
|
||||
a four-round whitelist build) — cursor-aware splice, multi-insert, recent-used strip across both tabs, Settings toggle
|
||||
in Chat → Message behaviour. Mid-cycle hotfix for pinned auto-tell tabs: PreloadHistory used to cap the SQL scan at
|
||||
500 rows regardless of the user's `AutoTellTabsHistoryPreload` setting, so active users with many partners lost the
|
||||
backlog of less-frequent pinned partners; the cap is gone, the `(Receiver, Date)` index keeps SQL fast, the client-side
|
||||
loop respects the user setting as the upper bound. Slash-command teardown cleanup wires the v1.4.9 wrappers through
|
||||
private fields so dispose detaches the live registration instead of re-registering with identical args. The original
|
||||
Reserve-A `ImGuiListClipper` refactor for `DrawMessages` was cancelled after cross-platform smoke showed the scroll
|
||||
rubber-band is a Wine/Linux render-pipeline quirk, not universal — Windows-side testing on v1.4.9 confirmed no lag.
|
||||
Migration v17 stays.
|
||||
Eleventh and final sub-patch of the v1.4.x Polish Sweep series. Symbol picker for the chat input —
|
||||
popup with two tabs (161 FFXIV PUA glyphs via Dalamud's SeIconChar plus 97 server-verified BMP
|
||||
symbols probed through `/echo` and `/say` in a four-round whitelist build) — cursor-aware splice,
|
||||
multi-insert, recent-used strip across both tabs, Settings toggle in Chat → Message behaviour.
|
||||
Mid-cycle hotfix for pinned auto-tell tabs: PreloadHistory used to cap the SQL scan at 500 rows
|
||||
regardless of the user's `AutoTellTabsHistoryPreload` setting, so active users with many partners
|
||||
lost the backlog of less-frequent pinned partners; the cap is gone, the `(Receiver, Date)` index
|
||||
keeps SQL fast, the client-side loop respects the user setting as the upper bound. Slash-command
|
||||
teardown cleanup wires the v1.4.9 wrappers through private fields so dispose detaches the live
|
||||
registration instead of re-registering with identical args. The original Reserve-A
|
||||
`ImGuiListClipper` refactor for `DrawMessages` was cancelled after cross-platform smoke showed the
|
||||
scroll rubber-band is a Wine/Linux render-pipeline quirk, not universal — Windows-side testing on
|
||||
v1.4.9 confirmed no lag. Migration v17 stays.
|
||||
|
||||
---
|
||||
|
||||
## v1.4.9 — Plugin-Load Render Polish (released 2026-05-15)
|
||||
|
||||
Tenth sub-patch of the v1.4.x Polish Sweep series. First-frame HITCH drops from ~127 ms median to ~76 ms median (4-reload
|
||||
sample), comfortably under Dalamud's 100 ms warning threshold. Mechanism: a single `_firstFrameDone` flag inside
|
||||
`ChatLogWindow` defers six non-essential rendering sections (bottom status bar, channel-name SeString chunks, window
|
||||
bounds check, v0.6.1 hint banner, autocomplete, input-preview calculation) from frame 0 to frame 1. User sees those
|
||||
sections ~17 ms (60 fps) later, invisible inside the ~2.5 s font-atlas build window after every reload. Slash-command
|
||||
registration moved from individual window constructors to a central `SetupCommands` / `TearDownCommands` pair in
|
||||
`Plugin.cs` — `/hellion`, `/hellionView`, `/hellionSeString` and `/hellionDebugger` work before their target windows are
|
||||
opened the first time, and Dalamud's plugin-manager `OpenConfigUi` / `OpenMainUi` buttons hang on the same path.
|
||||
Plugin-load profiling logs (auto-translate warmup, `MessageStore.Connect`, `MessageStore.Migrate`, `FilterAllTabs`) stay
|
||||
on at Information level as a regression tripwire. The release also ships a ChatTwo IPC compatibility layer: HellionChat
|
||||
mirrors ChatTwo's full IPC surface (`GetChatInputState`, `ChatInputStateChanged`, `Register`, `Unregister`, `Available`,
|
||||
`Invoke`) under the `ChatTwo.*` namespace in addition to our existing `HellionChat.*` provider gates, so third-party
|
||||
integrations that only subscribe to ChatTwo's IPC (Artisan, AllaganTools) keep working without a code change on their
|
||||
side. Conflict detection prevents ChatTwo from loading in parallel, so there is no slot-collision risk at runtime.
|
||||
Migration v17 stays (no schema bump). Hypothesis-triage falsified
|
||||
three of four candidate root causes (font-atlas sync fallback, theme-apply ABGR-cache init, multiple-window render via
|
||||
lazy-init) — actual cost distributes evenly across ~10 ImGui sections inside ChatLogWindow, so structural rewrite is
|
||||
deferred to v1.5.x DI-container cycle.
|
||||
Tenth sub-patch of the v1.4.x Polish Sweep series. First-frame HITCH drops from ~127 ms median to
|
||||
~76 ms median (4-reload sample), comfortably under Dalamud's 100 ms warning threshold. Mechanism: a
|
||||
single `_firstFrameDone` flag inside `ChatLogWindow` defers six non-essential rendering sections
|
||||
(bottom status bar, channel-name SeString chunks, window bounds check, v0.6.1 hint banner,
|
||||
autocomplete, input-preview calculation) from frame 0 to frame 1. User sees those sections ~17 ms
|
||||
(60 fps) later, invisible inside the ~2.5 s font-atlas build window after every reload.
|
||||
Slash-command registration moved from individual window constructors to a central `SetupCommands` /
|
||||
`TearDownCommands` pair in `Plugin.cs` — `/hellion`, `/hellionView`, `/hellionSeString` and
|
||||
`/hellionDebugger` work before their target windows are opened the first time, and Dalamud's
|
||||
plugin-manager `OpenConfigUi` / `OpenMainUi` buttons hang on the same path. Plugin-load profiling
|
||||
logs (auto-translate warmup, `MessageStore.Connect`, `MessageStore.Migrate`, `FilterAllTabs`) stay
|
||||
on at Information level as a regression tripwire. The release also ships a ChatTwo IPC compatibility
|
||||
layer: HellionChat mirrors ChatTwo's full IPC surface (`GetChatInputState`, `ChatInputStateChanged`,
|
||||
`Register`, `Unregister`, `Available`, `Invoke`) under the `ChatTwo.*` namespace in addition to our
|
||||
existing `HellionChat.*` provider gates, so third-party integrations that only subscribe to
|
||||
ChatTwo's IPC (Artisan, AllaganTools) keep working without a code change on their side. Conflict
|
||||
detection prevents ChatTwo from loading in parallel, so there is no slot-collision risk at runtime.
|
||||
Migration v17 stays (no schema bump). Hypothesis-triage falsified three of four candidate root
|
||||
causes (font-atlas sync fallback, theme-apply ABGR-cache init, multiple-window render via lazy-init)
|
||||
— actual cost distributes evenly across ~10 ImGui sections inside ChatLogWindow, so structural
|
||||
rewrite is deferred to v1.5.x DI-container cycle.
|
||||
|
||||
## v1.4.8 — Hook-Layer and Polish Quick-Wins (released 2026-05-14)
|
||||
|
||||
Ninth sub-patch of the v1.4.x Polish Sweep series. Database Viewer gains an optional FTS5 full-text search across the
|
||||
full chat history, built asynchronously on first run after the update with a progress toast; the local page-filter
|
||||
remains the default mode. Custom theme files auto-reload when edited while the theme is active (1 Hz disk-stat throttle,
|
||||
so per-frame cost is free). Retention sweep no longer blocks the framework thread — `Framework.Run(...).Wait()` is
|
||||
replaced by `Framework.RunOnTick(...)`, removing the ~194 ms hitch per sweep. Status-bar height is now derived from
|
||||
`GetTextLineHeightWithSpacing()` plus a DPI-aware spacer so the bar renders correctly at Windows display scaling above
|
||||
100 %. Receive-suppressed-tells routing was investigated and **postponed to v1.5.x**: when other plugins suppress tells
|
||||
via `CheckMessageHandled`, FFXIV's chat-pipeline skips the `RaptureLogModule.AddMsgSourceEntry` path, which means the
|
||||
`ContentIdResolverHook` does not fire and tell-partner identification breaks. The fix belongs next to the planned ad-block
|
||||
hook layer where the same patch surface comes up anyway. Migration v17 stays (no schema bump). H3 leaves a foundation
|
||||
note in the Vault (`Projekte/FFXIV/Hellion Chat/v1.5.x Ad-Block Foundation.md`) covering the NoSoliciting filter +
|
||||
Ninth sub-patch of the v1.4.x Polish Sweep series. Database Viewer gains an optional FTS5 full-text
|
||||
search across the full chat history, built asynchronously on first run after the update with a
|
||||
progress toast; the local page-filter remains the default mode. Custom theme files auto-reload when
|
||||
edited while the theme is active (1 Hz disk-stat throttle, so per-frame cost is free). Retention
|
||||
sweep no longer blocks the framework thread — `Framework.Run(...).Wait()` is replaced by
|
||||
`Framework.RunOnTick(...)`, removing the ~194 ms hitch per sweep. Status-bar height is now derived
|
||||
from `GetTextLineHeightWithSpacing()` plus a DPI-aware spacer so the bar renders correctly at
|
||||
Windows display scaling above 100 %. Receive-suppressed-tells routing was investigated and
|
||||
**postponed to v1.5.x**: when other plugins suppress tells via `CheckMessageHandled`, FFXIV's
|
||||
chat-pipeline skips the `RaptureLogModule.AddMsgSourceEntry` path, which means the
|
||||
`ContentIdResolverHook` does not fire and tell-partner identification breaks. The fix belongs next
|
||||
to the planned ad-block hook layer where the same patch surface comes up anyway. Migration v17 stays
|
||||
(no schema bump). H3 leaves a foundation note in the Vault
|
||||
(`Projekte/FFXIV/Hellion Chat/v1.5.x Ad-Block Foundation.md`) covering the NoSoliciting filter +
|
||||
bubble-layer hook pattern as a ready-made template for the v1.5.x cycle.
|
||||
|
||||
---
|
||||
|
||||
## 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 <pinned-partner>` 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).
|
||||
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 <pinned-partner>` 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
|
||||
development feedback loop and pulls in two ChatTwo upstream bugfixes. `scripts/preflight.sh` gains a csharpier reflow
|
||||
check (Block E) and a markdownlint pass (Block F), so style drift and markdown violations are blocked at the pre-push
|
||||
gate. `FontManager.AddFontWithFallback` catch-filter now spans `InvalidOperationException` and `ArgumentException` on
|
||||
top of the existing IO triad, with the exception type name in the warning log so the diagnostic path can see which
|
||||
atlas-toolkit throw triggered the fallback. `BrandingLinks` and `IntegrationLinks` run a `[ModuleInitializer]` URL
|
||||
validation pass on plugin load; a typo in a future URL rotation now 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 (rename to `IsChannelOrExistingLinkshell` plus
|
||||
wrap-not-return), 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 `ChatLogWindow` active-tab underline pill scales with
|
||||
`ImGuiHelpers.GlobalScale` 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 in the
|
||||
snapshot path, `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. 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 (Block E) and a markdownlint pass (Block F),
|
||||
so style drift and markdown violations are blocked at the pre-push gate.
|
||||
`FontManager.AddFontWithFallback` catch-filter now spans `InvalidOperationException` and
|
||||
`ArgumentException` on top of the existing IO triad, with the exception type name in the warning log
|
||||
so the diagnostic path can see which atlas-toolkit throw triggered the fallback. `BrandingLinks` and
|
||||
`IntegrationLinks` run a `[ModuleInitializer]` URL validation pass on plugin load; a typo in a
|
||||
future URL rotation now 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 (rename to `IsChannelOrExistingLinkshell`
|
||||
plus wrap-not-return), 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 `ChatLogWindow`
|
||||
active-tab underline pill scales with `ImGuiHelpers.GlobalScale` 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 in the snapshot path, `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.
|
||||
|
||||
## v1.4.5 — UX and Robustness (released 2026-05-12)
|
||||
|
||||
Sixth sub-patch of the v1.4.x Polish Sweep series. User-visible robustness polish plus two doc/test polish items from
|
||||
the audit backlog. Chat-log draw failures now surface as a one-shot notification instead of failing silently. The
|
||||
first-run wizard splits accept from close: `OnClose` no longer silently sets `FirstRunCompleted`, and a new footer
|
||||
"Later — keep defaults" button is the explicit path to dismiss without picking a profile. `InputHistoryService` clears
|
||||
on plugin dispose so the previous session's typed commands don't bleed into the next load. `FontManager` falls back to
|
||||
the system font path if the embedded Hellion font resource is missing (broken-csproj / dev-build only). The status bar
|
||||
hides the version slot when the chat window is too narrow to fit all five slots without overlap. Plus
|
||||
`Plugin.cs:167-168` gains an explicit session-only Auto-Tell-Tab invariant comment with a `TempTabCounter.InitFromList`
|
||||
pin in the Build-Suite. No schema bump, no migration.
|
||||
Sixth sub-patch of the v1.4.x Polish Sweep series. User-visible robustness polish plus two doc/test
|
||||
polish items from the audit backlog. Chat-log draw failures now surface as a one-shot notification
|
||||
instead of failing silently. The first-run wizard splits accept from close: `OnClose` no longer
|
||||
silently sets `FirstRunCompleted`, and a new footer "Later — keep defaults" button is the explicit
|
||||
path to dismiss without picking a profile. `InputHistoryService` clears on plugin dispose so the
|
||||
previous session's typed commands don't bleed into the next load. `FontManager` falls back to the
|
||||
system font path if the embedded Hellion font resource is missing (broken-csproj / dev-build only).
|
||||
The status bar hides the version slot when the chat window is too narrow to fit all five slots
|
||||
without overlap. Plus `Plugin.cs:167-168` gains an explicit session-only Auto-Tell-Tab invariant
|
||||
comment with a `TempTabCounter.InitFromList` pin in the Build-Suite. No schema bump, no migration.
|
||||
|
||||
## v1.4.4 — Threading and IPC Safety Polish (released 2026-05-12)
|
||||
|
||||
Fifth sub-patch of the v1.4.x Polish Sweep series. `AutoTellTabsService.ActiveTempTabCount` switches from a
|
||||
lock-protected LINQ `Count` to an `Interlocked` counter kept in sync from inside the existing mutation paths;
|
||||
`Initialize()` seeds from the persisted Tabs list and `SaveConfig`'s snapshot-restore path calls a new
|
||||
`ResyncTempTabCounter()` after the mid-step `RemoveAll`. `HonorificService` carries per-method threading banners and
|
||||
`TryUnsubscribe`'s log level moves from Debug to Warning. `AutoTranslate.PreloadCache` is marked `IsBackground = true`
|
||||
so plugin unload no longer waits for it. `Configuration.IsAllowedForStorage` logs once per unknown ChatType via a
|
||||
`NonSerialized` `HashSet`, and `PrivacyPersistUnknownChannels` default flips to `true` for new installs. No schema bump,
|
||||
no migration.
|
||||
Fifth sub-patch of the v1.4.x Polish Sweep series. `AutoTellTabsService.ActiveTempTabCount` switches
|
||||
from a lock-protected LINQ `Count` to an `Interlocked` counter kept in sync from inside the existing
|
||||
mutation paths; `Initialize()` seeds from the persisted Tabs list and `SaveConfig`'s
|
||||
snapshot-restore path calls a new `ResyncTempTabCounter()` after the mid-step `RemoveAll`.
|
||||
`HonorificService` carries per-method threading banners and `TryUnsubscribe`'s log level moves from
|
||||
Debug to Warning. `AutoTranslate.PreloadCache` is marked `IsBackground = true` so plugin unload no
|
||||
longer waits for it. `Configuration.IsAllowedForStorage` logs once per unknown ChatType via a
|
||||
`NonSerialized` `HashSet`, and `PrivacyPersistUnknownChannels` default flips to `true` for new
|
||||
installs. No schema bump, no migration.
|
||||
|
||||
## v1.4.3 — Plugin-Load Async-Init + Repo-Cutover (released 2026-05-08)
|
||||
|
||||
Fourth and largest sub-patch of the v1.4.x Polish Sweep series. Plugin migrated to Dalamud's `IAsyncDalamudPlugin` API:
|
||||
the constructor handles only bootstrap essentials (config load, language init, conflict detection); migrations, service
|
||||
allocations, window construction and hook subscription move to `LoadAsync`. Schema gate replaces the v9 → v16 migration
|
||||
chain; configs on schema v16+ load directly, older configs trigger an "install v1.4.2 first" error.
|
||||
`AutoTranslate.PreloadCache` moved off the load path. `FontManager.BuildFonts` runs sync at the start of `LoadAsync`;
|
||||
Dalamud rebuilds the font atlas on its own pipeline. Custom-repo URL cut over to `gitea.hellion-forge.cloud`; the GitHub
|
||||
repo remains as a frozen v1.4.2 snapshot. Plugin load time sits at ~3.7 s median (5 reloads), comparable to v1.4.2 — the
|
||||
async migration is a foundation for v1.4.4 lazy-init optimisations rather than an immediate user-perceived win.
|
||||
Fourth and largest sub-patch of the v1.4.x Polish Sweep series. Plugin migrated to Dalamud's
|
||||
`IAsyncDalamudPlugin` API: the constructor handles only bootstrap essentials (config load, language
|
||||
init, conflict detection); migrations, service allocations, window construction and hook
|
||||
subscription move to `LoadAsync`. Schema gate replaces the v9 → v16 migration chain; configs on
|
||||
schema v16+ load directly, older configs trigger an "install v1.4.2 first" error.
|
||||
`AutoTranslate.PreloadCache` moved off the load path. `FontManager.BuildFonts` runs sync at the
|
||||
start of `LoadAsync`; Dalamud rebuilds the font atlas on its own pipeline. Custom-repo URL cut over
|
||||
to `gitea.hellion-forge.cloud`; the GitHub repo remains as a frozen v1.4.2 snapshot. Plugin load
|
||||
time sits at ~3.7 s median (5 reloads), comparable to v1.4.2 — the async migration is a foundation
|
||||
for v1.4.4 lazy-init optimisations rather than an immediate user-perceived win.
|
||||
|
||||
## v1.4.2 — ChatLog Frame-Hot-Path (released 2026-05-08)
|
||||
|
||||
Third sub-patch of the v1.4.x Polish Sweep series. Per-frame allocations eliminated from the ChatLogWindow render path
|
||||
and the settings status bar. Card-mode border loop in `DrawMessages` hoists five invariants into a pre-loop hoist;
|
||||
`AutoTellTabTint` gets a per-tab cache via `TabTintCache` (separate validation keys per cache, no cross-invalidation);
|
||||
status bar moves the cache-gate check before the aggregation and replaces LINQ `Sum`+`Count` with a single-pass foreach.
|
||||
Third sub-patch of the v1.4.x Polish Sweep series. Per-frame allocations eliminated from the
|
||||
ChatLogWindow render path and the settings status bar. Card-mode border loop in `DrawMessages`
|
||||
hoists five invariants into a pre-loop hoist; `AutoTellTabTint` gets a per-tab cache via
|
||||
`TabTintCache` (separate validation keys per cache, no cross-invalidation); status bar moves the
|
||||
cache-gate check before the aggregation and replaces LINQ `Sum`+`Count` with a single-pass foreach.
|
||||
|
||||
## v1.4.1 — Theme Engine Performance (released 2026-05-08)
|
||||
|
||||
Second sub-patch of the v1.4.x Polish Sweep series. ABGR cache pre-computed on theme records; `HellionStyle.PushGlobal`
|
||||
reads from the cache instead of converting per slot per frame. **~13 % render-time recovery** in smoke tests (plan
|
||||
estimate of 2–6 % was conservative; real result ~10–15 %). Custom-theme hot-reload survives transient file locks via
|
||||
last-known-good snapshot. Plus: Synthwave Sunset as the tenth built-in, author credits consolidated under Hellion Forge,
|
||||
Mint Grove + Forge Merchantman credited to Carla Beleandis as a community thanks.
|
||||
Second sub-patch of the v1.4.x Polish Sweep series. ABGR cache pre-computed on theme records;
|
||||
`HellionStyle.PushGlobal` reads from the cache instead of converting per slot per frame. **~13 %
|
||||
render-time recovery** in smoke tests (plan estimate of 2–6 % was conservative; real result ~10–15
|
||||
%). Custom-theme hot-reload survives transient file locks via last-known-good snapshot. Plus:
|
||||
Synthwave Sunset as the tenth built-in, author credits consolidated under Hellion Forge, Mint
|
||||
Grove + Forge Merchantman credited to Carla Beleandis as a community thanks.
|
||||
|
||||
## v1.4.0 — Critical Lifecycle Fixes (released 2026-05-07)
|
||||
|
||||
First sub-patch of the v1.4.x Polish Sweep series. Seven P0 findings from audit passes 3 and 4 resolved: async-void
|
||||
loads, missing `IsBackground` flags, `GC.Collect` in Dispose, deferred-save race and pre-v13 backup lookup for
|
||||
`WindowOpacity`. No schema bumps, no user-facing behaviour changes other than reload and shutdown running noticeably
|
||||
cleaner.
|
||||
First sub-patch of the v1.4.x Polish Sweep series. Seven P0 findings from audit passes 3 and 4
|
||||
resolved: async-void loads, missing `IsBackground` flags, `GC.Collect` in Dispose, deferred-save
|
||||
race and pre-v13 backup lookup for `WindowOpacity`. No schema bumps, no user-facing behaviour
|
||||
changes other than reload and shutdown running noticeably cleaner.
|
||||
|
||||
## v1.3.0 — Plugin Integrations: Honorific (released 2026-05-07)
|
||||
|
||||
First cycle of the plugin integrations roadmap. Honorific custom titles displayed in the chat header with auto-detect
|
||||
and silent fallback. New Integrations settings tab. Pattern-setter for the five following cycles (Context Menu,
|
||||
NotificationMaster, RP Status Block, ExtraChat, XIVIM).
|
||||
First cycle of the plugin integrations roadmap. Honorific custom titles displayed in the chat header
|
||||
with auto-detect and silent fallback. New Integrations settings tab. Pattern-setter for the five
|
||||
following cycles (Context Menu, NotificationMaster, RP Status Block, ExtraChat, XIVIM).
|
||||
|
||||
Spec: [Plugin Integrations Overview](../Hellion%20Chat%20Plugin-Integrationen.md)
|
||||
|
||||
## v1.2.3 — Theme Expansion (released 2026-05-06)
|
||||
|
||||
Four new built-in themes: Night Blue, Indigo Violet, Forge Merchantman, Hellion Spectrum (Deuteran/Protan-safe). No
|
||||
engine changes. See `docs/CHANGELOG.md`.
|
||||
Four new built-in themes: Night Blue, Indigo Violet, Forge Merchantman, Hellion Spectrum
|
||||
(Deuteran/Protan-safe). No engine changes. See `docs/CHANGELOG.md`.
|
||||
|
||||
(v1.2.2 was burned because the `repo.json` manifest was not bumped in sync on the first push — re-released as v1.2.3
|
||||
with full manifest synchronisation.)
|
||||
(v1.2.2 was burned because the `repo.json` manifest was not bumped in sync on the first push —
|
||||
re-released as v1.2.3 with full manifest synchronisation.)
|
||||
|
||||
## v1.2.1 — Settings Cleanup (released 2026-05-06)
|
||||
|
||||
Settings re-sorted thematically (9 cards), 4 dead settings removed, auto-migration v15 → v16 without data loss.
|
||||
Settings re-sorted thematically (9 cards), 4 dead settings removed, auto-migration v15 → v16 without
|
||||
data loss.
|
||||
|
||||
## v1.2.0 — Layout Refresh (released 2026-05-05)
|
||||
|
||||
Top tabs refresh, sidebar tab icons, bottom status bar, card rows as default message render, auto-tell tab hashing.
|
||||
Top tabs refresh, sidebar tab icons, bottom status bar, card rows as default message render,
|
||||
auto-tell tab hashing.
|
||||
|
||||
## v1.1.0 — Theme Foundation (released 2026-05-05)
|
||||
|
||||
Theme engine with five built-in themes, settings card grid, custom themes via JSON, theme authoring docs. Plugin icon
|
||||
updated to Hellion Forge hammer. See `docs/CHANGELOG.md` for details.
|
||||
Theme engine with five built-in themes, settings card grid, custom themes via JSON, theme authoring
|
||||
docs. Plugin icon updated to Hellion Forge hammer. See `docs/CHANGELOG.md` for details.
|
||||
|
||||
Items from the original v1.1.0 plan (ad-block / spam filter, receive-suppressed-tells toggle) were deferred in favour of
|
||||
the theme engine — both items live on in the mid-term block.
|
||||
Items from the original v1.1.0 plan (ad-block / spam filter, receive-suppressed-tells toggle) were
|
||||
deferred in favour of the theme engine — both items live on in the mid-term block.
|
||||
|
||||
---
|
||||
|
||||
## Mid-Term (v1.4.x+)
|
||||
|
||||
- **Plugin Integrations Roadmap (Cycles 2–6)** — six plugin integrations planned; Honorific (Cycle 1) is live, followed
|
||||
by Context Menu, NotificationMaster, RP Status Block, ExtraChat and XIVIM in their own cycles. Spec and cycle order in
|
||||
- **Plugin Integrations Roadmap (Cycles 2–6)** — six plugin integrations planned; Honorific
|
||||
(Cycle 1) is live, followed by Context Menu, NotificationMaster, RP Status Block, ExtraChat and
|
||||
XIVIM in their own cycles. Spec and cycle order in
|
||||
[Plugin Integrations Overview](../Hellion%20Chat%20Plugin-Integrationen.md).
|
||||
- **Ad-Block / Spam Filter** — hybrid concept combining a lightweight built-in filter with optional `NoSoliciting` IPC
|
||||
integration. Addresses ad-spam in public channels and tells. Deferred from the v1.1.0 plan.
|
||||
- **Receive-Suppressed-Tells Toggle** — auto-tell tabs trigger even when a third-party plugin (e.g. XIVMessenger)
|
||||
globally suppresses /tell display. Same hook layer as ad-block, so they are bundled.
|
||||
- **Database Viewer Inline Search** — full-text search in the DB viewer via SQLite FTS5. Currently only date and channel
|
||||
filters are available.
|
||||
- **TempTell Persistence** — pin toggle on TempTell tabs so selected tells survive a relog. Tester request from Jingliu.
|
||||
- **FontManager Async Refactor** — move `LoadGameSymFontAsync` out of the blocking plugin constructor. Fix cold-start
|
||||
hitching on first plugin load (low severity; plugin is functional).
|
||||
- **Separate Opacity Active vs. Inactive** — second slider for inactive window opacity. Upstream declines this; we can
|
||||
decide differently here.
|
||||
- **Failed-Tell Notification** — visible message on /tell failure (offline, restricted instance, blacklisted,
|
||||
world-mismatch) instead of silent failure.
|
||||
- **Per-Tab Sound Notification** — sound toggle and optionally a custom .wav per tab, with mute-in-combat option.
|
||||
- **Ad-Block / Spam Filter** — hybrid concept combining a lightweight built-in filter with optional
|
||||
`NoSoliciting` IPC integration. Addresses ad-spam in public channels and tells. Deferred from the
|
||||
v1.1.0 plan.
|
||||
- **Receive-Suppressed-Tells Toggle** — auto-tell tabs trigger even when a third-party plugin (e.g.
|
||||
XIVMessenger) globally suppresses /tell display. Same hook layer as ad-block, so they are bundled.
|
||||
- **Database Viewer Inline Search** — full-text search in the DB viewer via SQLite FTS5. Currently
|
||||
only date and channel filters are available.
|
||||
- **TempTell Persistence** — pin toggle on TempTell tabs so selected tells survive a relog. Tester
|
||||
request from Jingliu.
|
||||
- **FontManager Async Refactor** — move `LoadGameSymFontAsync` out of the blocking plugin
|
||||
constructor. Fix cold-start hitching on first plugin load (low severity; plugin is functional).
|
||||
- **Separate Opacity Active vs. Inactive** — second slider for inactive window opacity. Upstream
|
||||
declines this; we can decide differently here.
|
||||
- **Failed-Tell Notification** — visible message on /tell failure (offline, restricted instance,
|
||||
blacklisted, world-mismatch) instead of silent failure.
|
||||
- **Per-Tab Sound Notification** — sound toggle and optionally a custom .wav per tab, with
|
||||
mute-in-combat option.
|
||||
|
||||
---
|
||||
|
||||
@@ -265,27 +295,28 @@ the theme engine — both items live on in the mid-term block.
|
||||
|
||||
### UX and Tab Management
|
||||
|
||||
- **Regex Tab Routing** — route plugin output spam into dedicated tabs, auto-sort tells from specific people. Clearly
|
||||
scoped against ad-block: routing sorts into views, blocking hides globally.
|
||||
- **Regex Tab Routing** — route plugin output spam into dedicated tabs, auto-sort tells from
|
||||
specific people. Clearly scoped against ad-block: routing sorts into views, blocking hides
|
||||
globally.
|
||||
- **Auto-Detect Duties** — tab switch on duty start via condition flag.
|
||||
- **UX Bundle** — vertical tab bar as a layout option, Shift+Mousewheel to scroll tab headers without activating them,
|
||||
global hotkey to close the active tab.
|
||||
- **Configure Tab Title** — configurable tab title format (name / name + abbreviated world / full name / custom),
|
||||
overridable per tab.
|
||||
- **Name Display Options** — analogous to FFXIV vanilla (full name, first name abbreviated, initials), per-channel
|
||||
override possible.
|
||||
- **Item & Flag Linking** — outgoing: Shift-click on an item/flag sends it to the focused plugin input. Incoming: item
|
||||
links and map coordinates are clickable.
|
||||
- **Color Currently Selected Input Channel** — tint the channel-selector button in the input bar with the current
|
||||
channel colour.
|
||||
- **Plugin-Disclosure Pre-Send Filter** — configurable word/regex list blocks sending with a pre-send confirmation.
|
||||
Protects against accidentally mentioning plugins in public channels.
|
||||
- **Chat Clear on Name Change** — on character name change, migrate or wipe local history; default is wipe for maximum
|
||||
privacy.
|
||||
- **UX Bundle** — vertical tab bar as a layout option, Shift+Mousewheel to scroll tab headers
|
||||
without activating them, global hotkey to close the active tab.
|
||||
- **Configure Tab Title** — configurable tab title format (name / name + abbreviated world / full
|
||||
name / custom), overridable per tab.
|
||||
- **Name Display Options** — analogous to FFXIV vanilla (full name, first name abbreviated,
|
||||
initials), per-channel override possible.
|
||||
- **Item & Flag Linking** — outgoing: Shift-click on an item/flag sends it to the focused plugin
|
||||
input. Incoming: item links and map coordinates are clickable.
|
||||
- **Color Currently Selected Input Channel** — tint the channel-selector button in the input bar
|
||||
with the current channel colour.
|
||||
- **Plugin-Disclosure Pre-Send Filter** — configurable word/regex list blocks sending with a
|
||||
pre-send confirmation. Protects against accidentally mentioning plugins in public channels.
|
||||
- **Chat Clear on Name Change** — on character name change, migrate or wipe local history; default
|
||||
is wipe for maximum privacy.
|
||||
- **Hide Plugin Window on NG+ Screen** — extend hide logic to cover additional addon names.
|
||||
- **Kick from Novice Network** — mentor niche; context menu entry with confirmation.
|
||||
- **Text-to-Speech for /tell** — incoming tells via TTS, optionally per sender, with channel filter and mute-in-combat.
|
||||
Low priority.
|
||||
- **Text-to-Speech for /tell** — incoming tells via TTS, optionally per sender, with channel filter
|
||||
and mute-in-combat. Low priority.
|
||||
|
||||
### Distribution and Branding
|
||||
|
||||
@@ -297,24 +328,29 @@ the theme engine — both items live on in the mid-term block.
|
||||
|
||||
## Bug Verifications
|
||||
|
||||
Carried over from the upstream issue tracker; not yet reproduced or verified in Hellion Chat 1.0.0. Will be tested
|
||||
against the current state when opportunity allows.
|
||||
Carried over from the upstream issue tracker; not yet reproduced or verified in Hellion Chat 1.0.0.
|
||||
Will be tested against the current state when opportunity allows.
|
||||
|
||||
- **Right-Click Whisper Error** in Field Ops / Special Instances (Eureka, Bozja, Occult Crescent, DRS) — upstream
|
||||
[#168](https://github.com/Infiziert90/ChatTwo/issues/168). Reply helper appears to swallow the `@World` suffix.
|
||||
- **FPS Drops with Plugin Active** — upstream [#145](https://github.com/Infiziert90/ChatTwo/issues/145). 10–20 % drop
|
||||
since upstream v1.29.19.0. v1.0.0 includes several fixes on the suspected paths; repro test against the current state
|
||||
is open.
|
||||
- **Add Blacklist from Plugin Window** — upstream [#140](https://github.com/Infiziert90/ChatTwo/issues/140). Right-click
|
||||
add-to-blacklist throws "Cannot locate character with that name"; works via vanilla chat.
|
||||
- **DB Viewer Column Sort** — State column sorts lexicographically instead of numerically (10 before 2). XIVIM
|
||||
[#82](https://github.com/NightmareXIV/XIVInstantMessenger/issues/82); repro in Hellion Chat open.
|
||||
- **Right-Click Whisper Error** in Field Ops / Special Instances (Eureka, Bozja, Occult Crescent,
|
||||
DRS) — upstream [#168](https://github.com/Infiziert90/ChatTwo/issues/168). Reply helper appears to
|
||||
swallow the `@World` suffix.
|
||||
- **FPS Drops with Plugin Active** — upstream
|
||||
[#145](https://github.com/Infiziert90/ChatTwo/issues/145). 10–20 % drop since upstream v1.29.19.0.
|
||||
v1.0.0 includes several fixes on the suspected paths; repro test against the current state is
|
||||
open.
|
||||
- **Add Blacklist from Plugin Window** — upstream
|
||||
[#140](https://github.com/Infiziert90/ChatTwo/issues/140). Right-click add-to-blacklist throws
|
||||
"Cannot locate character with that name"; works via vanilla chat.
|
||||
- **DB Viewer Column Sort** — State column sorts lexicographically instead of numerically (10 before
|
||||
2). XIVIM [#82](https://github.com/NightmareXIV/XIVInstantMessenger/issues/82); repro in Hellion
|
||||
Chat open.
|
||||
|
||||
---
|
||||
|
||||
## Licence Boundary
|
||||
|
||||
Hellion Chat is licensed under EUPL-1.2. Concept imports from AGPL-3.0 plugins (e.g. XIV Instant Messenger) are
|
||||
architectural inspiration only — no code was ported. Code imports from the upstream codebase are complete as of v1.4.x
|
||||
because Chat 2 is undergoing a fundamental rework and selective patches are no longer cleanly portable. Status and
|
||||
rationale in [`UPSTREAM_SYNC.md`](UPSTREAM_SYNC.md).
|
||||
Hellion Chat is licensed under EUPL-1.2. Concept imports from AGPL-3.0 plugins (e.g. XIV Instant
|
||||
Messenger) are architectural inspiration only — no code was ported. Code imports from the upstream
|
||||
codebase are complete as of v1.4.x because Chat 2 is undergoing a fundamental rework and selective
|
||||
patches are no longer cleanly portable. Status and rationale in
|
||||
[`UPSTREAM_SYNC.md`](UPSTREAM_SYNC.md).
|
||||
|
||||
+41
-33
@@ -4,8 +4,9 @@
|
||||
|
||||
# Theme Authoring Guide
|
||||
|
||||
> Built by **Hellion Forge** — the plugin workshop arm of [Hellion Online Media](https://hellion-media.de). HellionChat
|
||||
> ships with nine built-in themes; this guide walks you through writing your own.
|
||||
> Built by **Hellion Forge** — the plugin workshop arm of
|
||||
> [Hellion Online Media](https://hellion-media.de). HellionChat ships with nine built-in themes;
|
||||
> this guide walks you through writing your own.
|
||||
|
||||
## TL;DR
|
||||
|
||||
@@ -23,10 +24,11 @@ That's the whole loop. The rest of this document is reference.
|
||||
%APPDATA%\XIVLauncher\pluginConfigs\HellionChat\themes\
|
||||
```
|
||||
|
||||
(or the equivalent path on Linux/macOS — Settings → Themes → "Open themes folder" opens it directly).
|
||||
(or the equivalent path on Linux/macOS — Settings → Themes → "Open themes folder" opens it
|
||||
directly).
|
||||
|
||||
Each `*.json` file in this folder is loaded as one theme. The `example-theme.json` that HellionChat seeds on first
|
||||
launch is your starting template.
|
||||
Each `*.json` file in this folder is loaded as one theme. The `example-theme.json` that HellionChat
|
||||
seeds on first launch is your starting template.
|
||||
|
||||
## File format
|
||||
|
||||
@@ -60,7 +62,8 @@ Theme JSON has four blocks:
|
||||
|
||||
### Color slots
|
||||
|
||||
All values are 6-digit `#RRGGBB` or 8-digit `#RRGGBBAA` hex strings. Six-digit values get an implicit `FF` alpha.
|
||||
All values are 6-digit `#RRGGBB` or 8-digit `#RRGGBBAA` hex strings. Six-digit values get an
|
||||
implicit `FF` alpha.
|
||||
|
||||
| Slot | Role |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------- |
|
||||
@@ -87,7 +90,8 @@ All values are 6-digit `#RRGGBB` or 8-digit `#RRGGBBAA` hex strings. Six-digit v
|
||||
|
||||
### Layout slots
|
||||
|
||||
All values are floats in pixels. `BorderSize` is 0 or 1 (no thicker borders look right with ImGui's edge anti-aliasing).
|
||||
All values are floats in pixels. `BorderSize` is 0 or 1 (no thicker borders look right with ImGui's
|
||||
edge anti-aliasing).
|
||||
|
||||
| Slot | Typical range | Notes |
|
||||
| ------------------- | ------------- | ------------------------------------------------- |
|
||||
@@ -103,8 +107,8 @@ All values are floats in pixels. `BorderSize` is 0 or 1 (no thicker borders look
|
||||
|
||||
### Optional `chatChannels`
|
||||
|
||||
If present, your theme proposes its own chat-channel colors. Property names are `ChatType` enum values
|
||||
(case-insensitive). Unknown names are skipped silently — safe for forward-compat.
|
||||
If present, your theme proposes its own chat-channel colors. Property names are `ChatType` enum
|
||||
values (case-insensitive). Unknown names are skipped silently — safe for forward-compat.
|
||||
|
||||
```json
|
||||
"chatChannels": {
|
||||
@@ -120,8 +124,9 @@ If present, your theme proposes its own chat-channel colors. Property names are
|
||||
}
|
||||
```
|
||||
|
||||
The user is asked **once per theme switch** whether to apply these colors — never auto-overwriting existing picks. The
|
||||
banner shows up only if your suggested colors differ from the user's current `Configuration.ChatColours`.
|
||||
The user is asked **once per theme switch** whether to apply these colors — never auto-overwriting
|
||||
existing picks. The banner shows up only if your suggested colors differ from the user's current
|
||||
`Configuration.ChatColours`.
|
||||
|
||||
#### Channel-identity rule
|
||||
|
||||
@@ -137,25 +142,26 @@ banner shows up only if your suggested colors differ from the user's current `Co
|
||||
| FreeCompany | cyan-teal | Guild ops. |
|
||||
| NoviceNetwork | lime-green | Mentor channel. |
|
||||
|
||||
A theme can tint these toward its brand family (e.g., a purple theme can shift Tell from `#FF99CC` to `#E090FF`), but
|
||||
**don't** flip them (Tell suddenly green, Yell suddenly cyan). RP groups and combat-spec setups depend on the visual
|
||||
hierarchy.
|
||||
A theme can tint these toward its brand family (e.g., a purple theme can shift Tell from `#FF99CC`
|
||||
to `#E090FF`), but **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, 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.
|
||||
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.
|
||||
|
||||
## Theme families
|
||||
|
||||
Naming convention `<color>-<modifier>` is recommended for theme families. The first member of a family is the
|
||||
lightest/brightest:
|
||||
Naming convention `<color>-<modifier>` is recommended for theme families. The first member of a
|
||||
family is the lightest/brightest:
|
||||
|
||||
- `mint-grove` (current built-in, light mint)
|
||||
- `forest-grove` (planned, dark emerald)
|
||||
- `moss-grove` (planned, mid muted)
|
||||
|
||||
Code-wise families have no special handling — only the slug naming hints at the relationship. The picker may group
|
||||
families later, but that's not required.
|
||||
Code-wise families have no special handling — only the slug naming hints at the relationship. The
|
||||
picker may group families later, but that's not required.
|
||||
|
||||
## Validation and errors
|
||||
|
||||
@@ -164,8 +170,8 @@ When HellionChat loads your theme:
|
||||
- **Schema mismatch** (`schemaVersion != 1`): theme is skipped, warning written to `/xllog`.
|
||||
- **Missing required field** (e.g., no `slug`): theme is skipped, warning written.
|
||||
- **Invalid hex** (e.g., `#GGHHII`): theme is skipped, warning written.
|
||||
- **Unknown channel name** in `chatChannels`: that one channel is skipped silently, the rest of the theme loads
|
||||
normally.
|
||||
- **Unknown channel name** in `chatChannels`: that one channel is skipped silently, the rest of the
|
||||
theme loads normally.
|
||||
|
||||
Check `/xllog` after a plugin reload to see what loaded and what didn't.
|
||||
|
||||
@@ -177,23 +183,25 @@ Check `/xllog` after a plugin reload to see what loaded and what didn't.
|
||||
4. Watch every plugin window (chat, settings, pop-out) and pick something to fix.
|
||||
5. Tweak. Reload. Repeat.
|
||||
|
||||
Tip: the **Settings → Themes** picker shows a mini-mockup per theme — your colors are visible before you switch.
|
||||
Tip: the **Settings → Themes** picker shows a mini-mockup per theme — your colors are visible before
|
||||
you switch.
|
||||
|
||||
## Sharing themes
|
||||
|
||||
Themes are JSON, so sharing is just a file. Drop it into someone's `pluginConfigs/HellionChat/themes/` folder and their
|
||||
plugin picks it up on next reload.
|
||||
Themes are JSON, so sharing is just a file. Drop it into someone's
|
||||
`pluginConfigs/HellionChat/themes/` folder and their plugin picks it up on next reload.
|
||||
|
||||
A community theme repository is on the Hellion Forge roadmap. Until then: share via Discord or any pastebin.
|
||||
A community theme repository is on the Hellion Forge roadmap. Until then: share via Discord or any
|
||||
pastebin.
|
||||
|
||||
## Reference
|
||||
|
||||
- `docs/example-theme.json` (seeded automatically on first launch into `pluginConfigs/HellionChat/themes/`) — minimal
|
||||
valid theme.
|
||||
- The five built-in themes live in source under `HellionChat/Themes/Builtin/`. They are a good reference for Color
|
||||
choices that work.
|
||||
- [Hellion Online Media branding](https://hellion-media.de) — the Arctic Cyan + Ember Glow palette that drives the
|
||||
default Hellion Arctic theme.
|
||||
- `docs/example-theme.json` (seeded automatically on first launch into
|
||||
`pluginConfigs/HellionChat/themes/`) — minimal valid theme.
|
||||
- The five built-in themes live in source under `HellionChat/Themes/Builtin/`. They are a good
|
||||
reference for Color choices that work.
|
||||
- [Hellion Online Media branding](https://hellion-media.de) — the Arctic Cyan + Ember Glow palette
|
||||
that drives the default Hellion Arctic theme.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+22
-20
@@ -1,7 +1,7 @@
|
||||
# Third-party notices
|
||||
|
||||
HellionChat ships and depends on a number of third-party components. This document lists them, their licences and which
|
||||
of them touch the network. It is the inventory referenced by `PRIVACY.md`.
|
||||
HellionChat ships and depends on a number of third-party components. This document lists them, their
|
||||
licences and which of them touch the network. It is the inventory referenced by `PRIVACY.md`.
|
||||
|
||||
Last reviewed: 2026-05-05 (HellionChat v1.1.0).
|
||||
|
||||
@@ -20,11 +20,11 @@ Pinned in `HellionChat/HellionChat.csproj`. Versions reflect the v1.1.0 build.
|
||||
| [SixLabors.ImageSharp](https://github.com/SixLabors/ImageSharp) | 3.1.12 | [Six Labors Split License 1.0](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE) (OSI-approved; free for open-source / non-commercial use, commercial licence required for closed-source commercial use) | no | Image decoding for cached emotes. |
|
||||
| [SQLitePCLRaw.lib.e_sqlite3](https://github.com/ericsink/SQLitePCL.raw) | 3.50.3 | MIT | no | Native SQLite binary, explicitly pinned to override the transitive default for CVE-2025-6965 (memory corruption from aggregate-term overflow) and CVE-2025-7709. |
|
||||
|
||||
Six Labors note: HellionChat is an EUPL-1.2-licensed open-source project distributed at no cost. Use of ImageSharp 3.x
|
||||
under the Six Labors Split License 1.0 is permitted on that basis. Anyone forking HellionChat for closed-source or
|
||||
commercial redistribution should review the
|
||||
[Six Labors licence terms](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE) and obtain a commercial licence if
|
||||
required.
|
||||
Six Labors note: HellionChat is an EUPL-1.2-licensed open-source project distributed at no cost. Use
|
||||
of ImageSharp 3.x under the Six Labors Split License 1.0 is permitted on that basis. Anyone forking
|
||||
HellionChat for closed-source or commercial redistribution should review the
|
||||
[Six Labors licence terms](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE) and obtain a
|
||||
commercial licence if required.
|
||||
|
||||
## SDK and tooling
|
||||
|
||||
@@ -44,23 +44,24 @@ required.
|
||||
|
||||
## Upstream code
|
||||
|
||||
HellionChat is a fork of [Chat 2](https://github.com/Infiziert90/ChatTwo) by Infiziert90 (Infi) and Anna Clemens, also
|
||||
licensed under EUPL-1.2. The bulk of the code, including the message store architecture, the channel logic, the hook
|
||||
system and the ImGui chat window, originates from upstream. See `../NOTICE.md` for the attribution; `UPSTREAM_SYNC.md`
|
||||
documents the upstream-sync history, including the close of active cherry-picking in the v1.4.x cycle.
|
||||
HellionChat is a fork of [Chat 2](https://github.com/Infiziert90/ChatTwo) by Infiziert90 (Infi) and
|
||||
Anna Clemens, also licensed under EUPL-1.2. The bulk of the code, including the message store
|
||||
architecture, the channel logic, the hook system and the ImGui chat window, originates from
|
||||
upstream. See `../NOTICE.md` for the attribution; `UPSTREAM_SYNC.md` documents the upstream-sync
|
||||
history, including the close of active cherry-picking in the v1.4.x cycle.
|
||||
|
||||
---
|
||||
|
||||
## Components that touch the network
|
||||
|
||||
Of everything listed above, **none** of the bundled or NuGet components opens network connections on their own. All
|
||||
outbound traffic is initiated explicitly by HellionChat's own source files and is documented in `PRIVACY.md` under
|
||||
"Outbound network calls":
|
||||
Of everything listed above, **none** of the bundled or NuGet components opens network connections on
|
||||
their own. All outbound traffic is initiated explicitly by HellionChat's own source files and is
|
||||
documented in `PRIVACY.md` under "Outbound network calls":
|
||||
|
||||
- `HellionChat/EmoteCache.cs` → BetterTTV API + CDN (opt-out via setting)
|
||||
|
||||
The earlier Square Enix Lodestone font download (`FontManager.cs`) was removed in v1.0.4 — it was a leftover from
|
||||
upstream's removed webinterface feature and was no longer consumed.
|
||||
The earlier Square Enix Lodestone font download (`FontManager.cs`) was removed in v1.0.4 — it was a
|
||||
leftover from upstream's removed webinterface feature and was no longer consumed.
|
||||
|
||||
---
|
||||
|
||||
@@ -72,8 +73,9 @@ To regenerate the dependency inventory after a version bump:
|
||||
dotnet list HellionChat.sln package --include-transitive
|
||||
```
|
||||
|
||||
The "direct NuGet dependencies" table above only lists direct references. Transitive dependencies pulled in by Dalamud
|
||||
SDK or by the listed packages are covered by the SDK / package licences and documented by their respective maintainers.
|
||||
The "direct NuGet dependencies" table above only lists direct references. Transitive dependencies
|
||||
pulled in by Dalamud SDK or by the listed packages are covered by the SDK / package licences and
|
||||
documented by their respective maintainers.
|
||||
|
||||
To re-audit the network-call inventory:
|
||||
|
||||
@@ -82,5 +84,5 @@ grep -rn -E "HttpClient|HttpRequest|new Uri\(|https?://" \
|
||||
--include="*.cs" HellionChat/
|
||||
```
|
||||
|
||||
Any new hit that is not a click-through (`Util.OpenLink`) or a payload-parsing call must be added to `PRIVACY.md` before
|
||||
release.
|
||||
Any new hit that is not a click-through (`Util.OpenLink`) or a payload-parsing call must be added to
|
||||
`PRIVACY.md` before release.
|
||||
|
||||
+66
-56
@@ -1,93 +1,103 @@
|
||||
# Upstream Sync
|
||||
|
||||
HellionChat is a standalone EUPL-1.2 plugin that originated from [Chat 2](https://github.com/Infiziert90/ChatTwo). Since
|
||||
v1.0.0 it lives under its own namespace, IPC channels and source tree. The active cherry-pick pipeline from upstream
|
||||
Chat 2 is closed since the v1.4.x cycle.
|
||||
HellionChat is a standalone EUPL-1.2 plugin that originated from
|
||||
[Chat 2](https://github.com/Infiziert90/ChatTwo). Since v1.0.0 it lives under its own namespace, IPC
|
||||
channels and source tree. The active cherry-pick pipeline from upstream Chat 2 is closed since the
|
||||
v1.4.x cycle.
|
||||
|
||||
This document covers what that means, why I closed it, and what stays in place.
|
||||
|
||||
## A Word on Intent
|
||||
|
||||
HellionChat is not trying to replace Chat 2. I build it for myself, and maybe for people who want the same things I do:
|
||||
a privacy-first chat plugin with tighter defaults and no remote-access surface. If that is not you, Chat 2 is the better
|
||||
choice and a well-maintained project.
|
||||
HellionChat is not trying to replace Chat 2. I build it for myself, and maybe for people who want
|
||||
the same things I do: a privacy-first chat plugin with tighter defaults and no remote-access
|
||||
surface. If that is not you, Chat 2 is the better choice and a well-maintained project.
|
||||
|
||||
I am available to Infi if he ever has questions about HellionChat or how I have diverged from the upstream code. What I
|
||||
will not do is interfere with Chat 2's direction or push unsolicited opinions into his project.
|
||||
I am available to Infi if he ever has questions about HellionChat or how I have diverged from the
|
||||
upstream code. What I will not do is interfere with Chat 2's direction or push unsolicited opinions
|
||||
into his project.
|
||||
|
||||
Long-term compatibility between Chat 2 and HellionChat is not guaranteed and, frankly, not technically possible. I am
|
||||
building a new UI from scratch and making deliberate architectural decisions that pull in a different direction. Some
|
||||
upstream patches will simply stop applying cleanly and that is expected.
|
||||
Long-term compatibility between Chat 2 and HellionChat is not guaranteed and, frankly, not
|
||||
technically possible. I am building a new UI from scratch and making deliberate architectural
|
||||
decisions that pull in a different direction. Some upstream patches will simply stop applying
|
||||
cleanly and that is expected.
|
||||
|
||||
## Why Cherry-Picking Stopped in v1.4.x
|
||||
|
||||
Two things converged:
|
||||
|
||||
1. **Chat 2 is in a rework cycle.** Infi mentioned directly that parts of ChatTwo are being reworked and "stuff may not
|
||||
be able to be cherry picked anymore." Once the upstream code paths I would pull from no longer exist in the same
|
||||
shape, `git cherry-pick` stops being a meaningful tool — what would land would not be the change Infi wrote, it would
|
||||
be a hand-port of his concept.
|
||||
2. **HellionChat has drifted enough that selective patches require adaptation anyway.** The UI is being rebuilt, the
|
||||
theme engine sits on top of HellionStyle which has no upstream equivalent, the privacy filter changes how messages
|
||||
flow through MessageManager. Even before the rework was announced, more and more upstream patches needed adaptation
|
||||
rather than a clean apply.
|
||||
1. **Chat 2 is in a rework cycle.** Infi mentioned directly that parts of ChatTwo are being reworked
|
||||
and "stuff may not be able to be cherry picked anymore." Once the upstream code paths I would
|
||||
pull from no longer exist in the same shape, `git cherry-pick` stops being a meaningful tool —
|
||||
what would land would not be the change Infi wrote, it would be a hand-port of his concept.
|
||||
2. **HellionChat has drifted enough that selective patches require adaptation anyway.** The UI is
|
||||
being rebuilt, the theme engine sits on top of HellionStyle which has no upstream equivalent, the
|
||||
privacy filter changes how messages flow through MessageManager. Even before the rework was
|
||||
announced, more and more upstream patches needed adaptation rather than a clean apply.
|
||||
|
||||
Together those two points mean continuing to call this an "active cherry-pick pipeline" was no longer honest. So I
|
||||
closed it.
|
||||
Together those two points mean continuing to call this an "active cherry-pick pipeline" was no
|
||||
longer honest. So I closed it.
|
||||
|
||||
## What Closing the Pipeline Means in Practice
|
||||
|
||||
- The `upstream` git remote was removed locally on 2026-05-08. Anyone setting up a fresh clone does **not** add it back.
|
||||
- New commits will not carry `(cherry picked from commit ...)` trailers. Anything that originates from Chat 2 from this
|
||||
point forward will be a hand-port at most, and it gets called out as such in its own commit message and in the
|
||||
relevant source comments.
|
||||
- The existing cherry-pick trail stays in the git history exactly as it is. Every `(cherry picked from commit ...)` line
|
||||
that was added with `-x` in earlier releases remains intact; that is the attribution paper trail and removing it would
|
||||
be wrong.
|
||||
- The `upstream` git remote was removed locally on 2026-05-08. Anyone setting up a fresh clone does
|
||||
**not** add it back.
|
||||
- New commits will not carry `(cherry picked from commit ...)` trailers. Anything that originates
|
||||
from Chat 2 from this point forward will be a hand-port at most, and it gets called out as such in
|
||||
its own commit message and in the relevant source comments.
|
||||
- The existing cherry-pick trail stays in the git history exactly as it is. Every
|
||||
`(cherry picked from commit ...)` line that was added with `-x` in earlier releases remains
|
||||
intact; that is the attribution paper trail and removing it would be wrong.
|
||||
|
||||
## What Does Not Change
|
||||
|
||||
- **EUPL-1.2 anchor lines in source files.** Files that originated from Chat 2 keep their licence headers and any "based
|
||||
on Infiziert90/ChatTwo" notice exactly as they are. The licence obligations under EUPL-1.2 do not lapse because
|
||||
cherry-picking stopped.
|
||||
- **NOTICE.md** stays canonical. Attribution to Infi and Anna for the message store, channel logic, hook system, ImGui
|
||||
chat window and the localisation infrastructure remains the foundation statement of this fork.
|
||||
- **README acknowledgements.** The Acknowledgements section in `README.md`, the maintainer thanks in the About tab, and
|
||||
the `Language.*.resx` Crowdin translator credit list all stay as they are.
|
||||
- **The original `Language.*.resx` files** remain in the source tree in their last upstream-sync state. They are the
|
||||
work of the Chat 2 Crowdin community and the existing translations stay valuable. They will not receive automatic
|
||||
upstream updates anymore — see CONTRIBUTING.md for what that means for translators.
|
||||
- **EUPL-1.2 anchor lines in source files.** Files that originated from Chat 2 keep their licence
|
||||
headers and any "based on Infiziert90/ChatTwo" notice exactly as they are. The licence obligations
|
||||
under EUPL-1.2 do not lapse because cherry-picking stopped.
|
||||
- **NOTICE.md** stays canonical. Attribution to Infi and Anna for the message store, channel logic,
|
||||
hook system, ImGui chat window and the localisation infrastructure remains the foundation
|
||||
statement of this fork.
|
||||
- **README acknowledgements.** The Acknowledgements section in `README.md`, the maintainer thanks in
|
||||
the About tab, and the `Language.*.resx` Crowdin translator credit list all stay as they are.
|
||||
- **The original `Language.*.resx` files** remain in the source tree in their last upstream-sync
|
||||
state. They are the work of the Chat 2 Crowdin community and the existing translations stay
|
||||
valuable. They will not receive automatic upstream updates anymore — see CONTRIBUTING.md for what
|
||||
that means for translators.
|
||||
|
||||
## What Could Re-Open Later
|
||||
|
||||
If Chat 2's rework lands and stabilises, and there is a piece of upstream code that I genuinely want in HellionChat, the
|
||||
path forward is **study and re-implement**, not cherry-pick. That means:
|
||||
If Chat 2's rework lands and stabilises, and there is a piece of upstream code that I genuinely want
|
||||
in HellionChat, the path forward is **study and re-implement**, not cherry-pick. That means:
|
||||
|
||||
- Read the upstream change, understand the design, port the concept to HellionChat's actual code paths.
|
||||
- Credit the upstream author in the commit message and, if the ported code is non-trivial, in a source-file comment.
|
||||
- Read the upstream change, understand the design, port the concept to HellionChat's actual code
|
||||
paths.
|
||||
- Credit the upstream author in the commit message and, if the ported code is non-trivial, in a
|
||||
source-file comment.
|
||||
- Pre-clear with Infi if the port is large enough to warrant a conversation.
|
||||
|
||||
This is heavier than `git cherry-pick -x` and that is the point. Cherry-picking was light because both codebases shared
|
||||
structure; once they do not, the proper attribution costs a real conversation rather than a flag on a git command.
|
||||
This is heavier than `git cherry-pick -x` and that is the point. Cherry-picking was light because
|
||||
both codebases shared structure; once they do not, the proper attribution costs a real conversation
|
||||
rather than a flag on a git command.
|
||||
|
||||
## Contributing Back
|
||||
|
||||
HellionChat benefits from Chat 2's work, so I try to give something back where I can. If I fix a bug or improve
|
||||
something that would be useful to Chat 2 and is not HellionChat-specific, I submit a good-will PR to
|
||||
[Infiziert90/ChatTwo](https://github.com/Infiziert90/ChatTwo).
|
||||
HellionChat benefits from Chat 2's work, so I try to give something back where I can. If I fix a bug
|
||||
or improve something that would be useful to Chat 2 and is not HellionChat-specific, I submit a
|
||||
good-will PR to [Infiziert90/ChatTwo](https://github.com/Infiziert90/ChatTwo).
|
||||
|
||||
A few things to note about that process:
|
||||
|
||||
- Good-will PRs are validated in a separate fork first to make sure the fix stands on its own without HellionChat
|
||||
context.
|
||||
- They are written by hand. No AI-generated code goes to Infi's project. He did not ask for Pair-level AI involvement
|
||||
and I will not push that decision onto his codebase.
|
||||
- This is not guaranteed for every change, only where it makes sense and where I am confident the fix is clean and
|
||||
self-contained.
|
||||
- Good-will PRs are validated in a separate fork first to make sure the fix stands on its own
|
||||
without HellionChat context.
|
||||
- They are written by hand. No AI-generated code goes to Infi's project. He did not ask for
|
||||
Pair-level AI involvement and I will not push that decision onto his codebase.
|
||||
- This is not guaranteed for every change, only where it makes sense and where I am confident the
|
||||
fix is clean and self-contained.
|
||||
- Whether it gets accepted is Infi's call, and a "no" is fine.
|
||||
|
||||
## When Upstream Takes a Direction I Cannot Follow
|
||||
|
||||
If a future Chat 2 release breaks compatibility with the HellionChat privacy philosophy in a way that cannot be resolved
|
||||
(mandatory cloud sync, removal of the local message store, an incompatible licence change), HellionChat continues from
|
||||
where it is. The inherited history stays under EUPL-1.2 and stays attributed.
|
||||
If a future Chat 2 release breaks compatibility with the HellionChat privacy philosophy in a way
|
||||
that cannot be resolved (mandatory cloud sync, removal of the local message store, an incompatible
|
||||
licence change), HellionChat continues from where it is. The inherited history stays under EUPL-1.2
|
||||
and stays attributed.
|
||||
|
||||
Reference in New Issue
Block a user