Files
HellionChat/docs/CHANGELOG.md
T
JonKazama-Hellion 522545c7a2
Security Scan (reusable) / Security Scan (push) Successful in 20s
Security / scan (push) Successful in 20s
Forge Announce / Post changelog to Hellion Forge (push) Successful in 12s
Build / Build (Release) (push) Successful in 29s
Release / Build and attach release ZIP (push) Successful in 29s
chore(release): 2.0.4 -- shorter description, webinterface strings out
Housekeeping only, no behaviour change.

The v2.0.0 changelog block moves out of the plugin manifest to stay
inside the four-subblock limit; it remains in docs/CHANGELOG.md.
2026-08-20 18:21:59 +02:00

81 KiB
Raw Permalink Blame History

Changelog — Hellion Chat

All user-facing changes to Hellion Chat. Format follows Keep a Changelog, version numbers follow Semantic Versioning.

Detailed release notes per version are available directly on the Gitea Release page 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.


[2.0.4] — 2026-08-20

Housekeeping. Nothing changes in how the plugin behaves.

Changed

  • The installer description is shorter and leads with what the plugin does rather than what category it belongs to: 367 characters instead of 908. Two things in it were also wrong rather than merely long — it claimed 24 interface languages when there are 25, and repo.json and the plugin yaml carried different one-line summaries, so the store listing and the plugin details showed different sentences.

Removed

  • 30 translation keys belonging to the webinterface, across all 25 languages, plus the generated property for each. The feature itself, its HTTP routes and its frontend went out in May; only the strings were left behind. Every key was matched on word boundaries against the whole repository before removal, and the build proves the rest: a key still in use would have failed to compile.

[2.0.3] — 2026-08-20

Fixed

  • The context menu never opened in a pop-out window. Right-clicking a name or an item there did nothing at all. One payload handler is shared by the main window, every pop-out and the input preview, and it carries a single popup state. The main window draws first, found no open popup in its own scope, read that as closed and cleared the state before the pop-out had drawn. A popup now belongs to the surface that opened it and the others leave it alone.
  • Screenshot mode could not be turned off. The setting is persisted, but neither of its two toggles saved the config, so turning it on only stuck when an unrelated save happened to follow — and once stored, turning it off never reached the file, so it returned at every plugin load. Both toggles save now; an install currently stuck on it needs one click.
  • Text the game colours itself could render invisible. Some of those colours arrive without an alpha byte, and the byte order swap left them fully transparent. A colour that carries no value at all still resolves to the colour already in effect rather than to opaque black.

[2.0.2] — 2026-08-19

Removed

  • BetterTTV emote support, in full. The shared-emote endpoint went behind authentication and it supplied nearly all of them; what was left is a 65-entry global set, eleven of those on the known-broken list, so 54 largely static images. One was animated — 492 frames at 140x140, which is 37 MB of texture memory for a single emote. Gone with it: the download path, the on-disk cache, the GIF renderer, the settings section, the block list, and 13 translation keys across 50 resource files. The plugin makes no outbound network calls at all now.
  • EmotePayload and its MessagePack type byte deliberately stay. Messages stored before this release carry them, and removing the type would make those rows fail to deserialise. Nothing writes one any more; a stored emote renders as the code that was typed. 0x53 and PayloadMessagePackType.Emote must never be reassigned.

Fixed

  • Five descriptions in the Window settings tab printed {0} where the plugin name belonged. They were handed to the widget directly instead of through string.Format, which the neighbouring rows do correctly. Every language was affected including English, where it read less obviously — German puts the placeholder at the start of the sentence, which is why it surfaced there.

Added

  • A test that walks every resource string containing a placeholder, finds each use of it in the sources, and fails if one reaches a widget unformatted. It was verified by falsification: reintroducing the defect turns it red.
  • New preview images for the plugin installer. The previous set was from 2026-05-08 and showed the interface as it looked before the window rebuilds. Taken with screenshot mode on, so no character names are in them.

[2.0.1] — 2026-08-19

A same-day hotfix on 2.0.0 with nothing user-facing in it.

Changed

  • MessagePack from 3.1.4 to 3.1.7. The range in the csproj already allowed it; NuGet resolves the lower bound of a range and so does trivy, so the floor is the version that actually ships. The advisories cover recursion depth in MessagePackReader.Skip and a fault in LZ4 decompression — both need crafted input, and this plugin serialises its own payloads into a local database and reads its own bytes back. Practical exposure requires write access to that file. Lifted because it is free and a scan left red for a known-harmless reason is how a real finding gets missed later.

Fixed

  • The release workflow attaches its archive again. gitea.com/actions/release-action declares using: go and has to be compiled by the runner, which act cannot do here — the 2.0.0 build went green and then died on the publish step with exit 127, leaving an announced release with no download. Publishing runs through the Gitea API with curl now, and is idempotent so a recovery run replaces the asset instead of tripping over it.

[2.0.0] — 2026-08-19

Rebuilt, repaired, reset. Everything developed as v1.6.0 through v1.15.0 ships here: those versions were never published on their own, because the whole window layer was being rewritten from ImGui defaults to custom drawing and shipping it half-done would have been worse than shipping nothing.

This release resets your configuration. Nine cycles of window rebuilds left saved values pointing at surfaces that no longer exist, and starting over is the only way to be sure every install is on the same defaults. The message database is untouched — it lives in its own file. The previous config is kept alongside it as HellionChat.json.pre-2.0.0.bak.

Added

  • An Emote tab in the default layout, requested by a tester: custom emotes, standard emotes and echo, which is where emote-adjacent output lands.
  • /hellion wizard reopens the first-run wizard. It had no way back into it at all — the reopen button lost its call site in the four-step rewrite and nothing replaced it, so the only route was closing the game and editing the config.
  • The plugin notice on the welcome page: plugins are a grey area in this game and do not belong in public channels.

Changed

  • The first-run wizard is drawn like the rest of the plugin. Profile cards are tiles in the row language, sized to their own text so a privacy description is never cut off mid-sentence, and their emoji are icon-font glyphs.
  • The welcome page is opaque, unlike every other window here. It is read once and carries a privacy decision; the game showing through it was noise.
  • Config version 27 replaces the config instead of migrating it.

Fixed

  • The GDPR notice for the full-history profile is shown again. It had been translated into 25 languages and drawn nowhere since 2026-05-18.
  • Two profile cards claimed to be recommended at once: the badge sat on one while the other still said "(recommended)" in its heading, in every language.
  • Reopening the wizard no longer carries staged choices from a previous run.

[1.14.0] — unreleased (local only)

The input row. It was the one surface a user actually works in and it spoke three shape languages at once — a rounded pill, a square field, five filled default buttons — sitting directly above a status bar that is rounded throughout. This release makes the row one thing, and it teaches the popups the window's own shapes while it is in there. Not published — the public release stays at v1.5.6.

Added

  • A style lab under /hellion lab: variants of UI elements side by side, drawn in real ImGui against the live theme. Permanent, as a dev playground — its radio buttons default to whatever shipped, so the window doubles as a record of which variant won. Every decision in this release was made by looking at it in-game rather than at a drawing.
  • PopupRow, the row a popup is made of: soft hover fill, a two-pixel accent bar on the active entry — the sidebar row's shape, which is also exactly what Character Select+ draws for its own popup entries. The channel picker and the new input-row menu are built from it.
  • A colour-channel guard in preflight (Block G). EnsureContrast works in ABGR, theme colours are RGBA, both are uint, and the mistake of feeding one into the other happened six times in a single day — each found only by a human staring at unreadable text. The guard flags any raw theme member reaching the helper; on its first real run it caught three cases a hand-written search had missed minutes earlier.

Changed

  • The five filled buttons in the input row are two ghost buttons and a menu. Symbols stay outside because they are used mid-sentence; theme, settings, screenshot mode and hide moved in. The ghosts are flat at rest and glow on hover — the first caller DrawGlowBorder ever had.
  • The screenshot toggle was allowed into the menu only because its state moved to the status bar first, as an accent camera pill that shows while the mode is on. Knowing what the screen shows before the screenshot key is pressed is the entire point of the mode; a state behind a closed menu answers nothing.
  • The input field paints its own rounded surface and hands ImGui a transparent frame, so the widget draws only text and caret. Focus is a two-pixel accent rail on the left edge — the same mark rows and popups use. The field is about a hundred pixels wider, reclaimed from the button reserve.
  • The channel pill is chamfered, the corner language of the segmented control, with a faint white depth gradient.
  • The channel header traded its surface plate for a tenth-opacity accent wash. The plate was the exact shape the settings window abandoned in v1.11.0 — it reads fine on blue themes and vanishes on violet ones.
  • The sidebar softened, on tester feedback that the transition to the chat field was too hard: the full-width border line under every row is a fading rule now, the gap between list and messages carries a surface wash instead of two hard edges, and the selected tab bridges across it so it touches the conversation it selects.
  • Hover fades run at a quarter of their old speed with a smoothstep curve — soft start, soft landing. 14/s in was judged light speed; it is 3/s now, and a test guards against drifting back. Every hover in the plugin inherits both changes through the one shared code path.

Fixed

  • Popups size themselves from their widest visible entry. Both new popups carried a guessed minimum width, and German outran it within a day — Schnellauswahl and Chat ausblenden were clipped mid-word.
  • Popup entry text was unreadable at rest on several themes and only became legible on hover: the RGBA-into-ABGR mistake again, in PopupRow and three spots in the lab. Fixed, and the new preflight guard exists so the seventh occurrence dies in CI rather than in a screenshot.
  • The pill label was raw theme text on an accent fill with no contrast check.

Known issues

  • The channel header still recomputes its widths every frame rather than on the status bar's one-second tick.
  • Metrics.Scale still calls a Dalamud property that is marked obsolete.
  • The first-run wizard is now the last surface drawn in ImGui defaults. It is the next cycle.

[1.13.0] — unreleased (local only)

Typography. The message list was the last surface still drawn in ImGui defaults, and it is the only one a reader looks at line by line rather than in glances. It now has named type roles instead of a single size, a header above the conversation, and rows that read as rows. Not published — the public release stays at v1.5.6.

Added

  • A header above the conversation: the channel name in tracked small caps with the tab's own icon, and the home world on the right. It stays out of the way where the name is already on screen — the top-tab strip carries it, and so does a pop-out's title bar — and disappears entirely when the window is too short to leave room for readable chat.
  • Timestamps sit in a column of their own, so sender names line up underneath each other instead of starting wherever the previous stamp happened to end.
  • Local and server time in the status bar, in the game's own LT/ST notation. Server time comes from the game rather than from a local UTC conversion, so it follows whatever Square Enix does with it.
  • Rows have a surface: a wash from the left and a two-pixel accent bar, fading in and out rather than snapping.
  • The sender is set in a heavier cut of the same face. There is no bold in the plugin, so the weight comes from a denser rasterisation — which means it has no effect when both font settings are off and the game's own face draws.
  • System messages are italic. Nobody said them; it is the game talking.
  • Card density finally differs from compact: the sender on its own line, the body indented onto the text column, and air between messages.
  • A camera button in the input row for screenshot mode, lit while active. The setting had only ever existed in the right-click menu on a player name, which for a privacy feature is close to not existing at all.
  • Hide timestamps when redundant is reachable. It has shipped with translations since 1.5.6 and never had a control.

Fixed

  • Screenshot mode reached one surface out of four. An auto-tell tab is named Player@World, and the sidebar, the tab strip and a pop-out's window title all drew that untouched — so a screenshot of the default view named the conversation partner while every message below it was anonymised. All four surfaces now share one rule, and the placeholder is re-salted on every plugin load so two screenshots taken weeks apart cannot be tied together by it.
  • Show timestamps in the tab editor does something again. It had two readers in 1.5.6 and lost both when the old chat window was retired in May.
  • Four settings could always leave the row-height cache stale and never did: the italic size, the italic toggle, and the two font toggles. The last two were the quiet ones — both size fields default to the same value, so nothing in the cache key moved while the glyph widths underneath it did. The clock format and the per-tab timestamp switch join them.
  • The word-wrap calculation was handed the UI scale where ImGui wanted the ratio between rendered and baked size. The two are the same number until a second size enters a line, which is exactly what this release introduces.
  • Emoji and other characters outside the basic plane no longer break a tab name drawn in tracked caps.

Changed

  • Config version 26. The migration marks existing tell tabs as partner-named, which is what screenshot mode reads. Tabs promoted to permanent before this version cannot be recovered — promotion clears every marker and keeps the name — but renaming one has the same effect.
  • The header replaced the pop-out's plain title row. With the title bar on it shows only the world, because the title already carries the name.
  • The channel header gave up its clock when both times moved to the status bar. Three copies of the same number on one screen is two too many.
  • All three custom font handles now rasterise the full glyph range. The smaller one started with an ASCII-sized range on the assumption it would only draw clocks and world names; a single umlaut in a tab name would have broken it.

Known issues

  • The channel header recomputes its widths every frame rather than on the status bar's one-second tick. Measurable, not measured.
  • Metrics.Scale still calls a Dalamud property that is marked obsolete.
  • The database viewer and the emoji picker are still English on every client.
  • 245 orphaned resource keys remain as the inventory of what the v1.6.0 rebuild lost.

[1.12.0] — unreleased (local only)

The reconnection cycle. A commit during the v1.6.0 window layer rebuild removed the old tab system, and the rebuild wired back roughly two thirds of it — 433 of 824 translation keys reached no line of code, and four whole features had a saved setting but no way in. This release puts them back and translates the settings window. Not published — the public release stays at v1.5.6.

Added

  • Message export is reachable again. Markdown, JSON and CSV, per channel or the whole database, written atomically so a failed write cannot leave half a file behind.
  • Retroactive cleanup is back, with a preview that counts what a run would delete before anything is removed, and the Ctrl+Shift confirm in front of the actual delete.
  • Database maintenance — integrity check, VACUUM, index rebuild — and the manual retention run both have a control again.
  • The tab editor returns: create, rename, reorder and delete tabs, with the channel selector and the activity filter attached to each one.
  • Pinning has a way in and a way back out. A pinned tab survives a restart and reopens with its history.
  • Eleven settings that steered real behaviour and had no control at all are reachable again.
  • The settings window is translated into all 25 languages: 435 keys, held against the glossary the plugin already used, with the game client's own word for a tell in each locale rather than a new invention.
  • Map flags and item links can be inserted into the input again, and the right-click menu is back on the input field.

Fixed

  • The channel grid now decides what is stored. Until this release the unknown-channel failsafe was applied to known channels as well, so a channel the user had unticked was still written when that failsafe was on. See the note under Changed — this is a behaviour change, not only a fix.
  • The retroactive cleanup could never be applied. The preview takes the database lock itself, and its release was counted as a mutation, so every preview was stale the instant it finished and the apply button never appeared.
  • VACUUM ran against an open reader on the primary connection and failed, after which the plugin reported that nothing had been deleted — while everything had. The reader sits on its own connection now.
  • Deleting messages left the full-text index behind, so search kept returning rows that no longer existed.
  • A tell arriving from a popped-out partner hijacked the main window's active tab.
  • The arrow keys walk the sent-message history again.
  • The export wrote a UTF-8 BOM into JSON, which strict parsers reject; CSV keeps it, because without it Excel guesses the code page. Enum values interpolated into JSON wrote the member name and produced invalid output.
  • Five strings in the settings and privacy tabs were still drawn as English literals.

Changed

  • Behaviour change in storage. Anyone who had unticked channels and left the unknown-channel failsafe on was storing more than the channel grid said. From v1.12.0 the grid is authoritative, so those installations store less. Nothing already in the database is touched.
  • Every long-running database operation now goes through one gate instead of seven near-identical background workers, each of which had to remember to take the lock. One of them did not.
  • The export reads message text from the chunk lists rather than the flattened field, so links and formatted segments survive the round trip.
  • Config version 25. There is no migration step behind it: the v24 migration switched the privacy filter off where it was on with nothing selected, and 25 only records that the storage rule changed shape.

Known issues

  • The database viewer (/hellionView) and the emoji picker are still English on every client, and one export dialog still carries the old Chat 2 branding in its title.
  • 145 Language.* and 100 HellionStrings keys still have no caller. That is an inventory of features lost in the v1.6.0 rebuild, not a delete list — timestamp layouts, duplicate-message collapsing, the About texts, the novice network button and the honorific glow are all in there.
  • AboutTab states GPL-3.0-or-later while the translated resources say EUPL-1.2.
  • The full-text index is emptied on every deletion and only rebuilt at the next plugin start; until then the database viewer searches more slowly.
  • The first-run wizard, the input bar buttons and the message list itself are still stock ImGui.

[1.11.0] — unreleased (local only)

The settings window, rebuilt on the widgets v1.10.0 introduced, plus the defects the rebuild kept turning up. Not published — the public release stays at v1.5.6.

Added

  • Every settings tab now draws the same way: section headings as tracked small caps with a rule that fades out, rows with the label left and the control right-aligned, sliding switches instead of checkboxes, and a segmented control where two radio buttons used to pretend to be two settings.
  • Explanations moved out of the help markers and onto the rows. Many were translated into 25 languages and had never appeared on screen.
  • Surfaces have depth: a gradient, an accent wash from the top edge, and slow drifting motes. The chat log and the pop-outs stand on the same floor, at a fraction of the intensity — a settings page is read in glances, a chat log line by line. Reduce motion turns the motes off entirely.
  • Foreground colours are chosen by measured WCAG contrast against the surface they land on rather than taken from the theme unchanged, so a pale accent cannot swallow white iconography.
  • The settings window is opaque, unlike the chat window. Contrast cannot be computed against a background that is the game world.
  • Three settings that steered real behaviour and had no control at all are reachable again: interface language, the global sound toggle, and the keybind mode.
  • Whether the log shows history from before the current session now has a control. It was written only by the first-run wizard, and only if the user reached step 3.
  • Per-channel retention is visible in Data & Privacy. Three of the four wizard profiles write one and switch the sweep on, and the window only ever showed the global default.

Fixed

  • Pinned tell tabs came up empty for the whole session. The history query runs at plugin start, where no character is logged in yet, so it looked up tells for character zero and never tried again. It defers to the login now.
  • Every enum dropdown listed eight rows regardless of how many values it had, several of them blank. The fourth argument of the binding is the popup height, not the item count.
  • Six settings had a control, a saved value and no reader anywhere in the plugin. Three sat together under timestamps, where only the 24-hour clock actually works. Removed rather than wired up: their own strings disagree about what they were meant to do.
  • The same privacy grid was drawn in two tabs, all 89 entries of it.
  • Descriptions were sized as one line while being drawn with a wrap width — clipped in a setting row, overflowing in a section heading.
  • Row separators used an unscaled offset with a scaled stroke, so at UI scale 2 half the line landed in the next row.
  • A pop-out with its title bar on could not be closed at all. Pop-in now sits in the input row.
  • BetterTTV's shared-emote endpoint went behind authentication; the 403 response was handed to a list deserializer and took the 65 working global emotes down with it on every start.

Changed

  • Thirteen labels that had translated resources but were drawn as English literals now use them, including all seven tab names in the sidebar.
  • Twelve duplicated per-tab helpers merged into one place. The enum combos were calling Enum.GetValues inside their draw call, allocating five arrays per frame while the window was open.

Known issues

  • Around 270 visible strings across the plugin are still English on non-English clients, most of them in the settings window: section headings, keybind labels, and the descriptions written during this cycle. They have no resource key yet. Translation is planned as its own pass before the tester beta; new strings ship translated from here on.
  • The first-run wizard, the input bar buttons and the message list itself are still stock ImGui.

[1.10.0] — unreleased (local only)

The style foundation the v2.x plan has been carrying since May. Custom-drawn chrome instead of ImGui defaults, and a layout layer that survives display scaling. Not published — the public release stays at v1.5.6.

Added

  • Sidebar rows now have surfaces: the active tab gets a raised fill and a 2px accent bar, hover fades in and back out, and rows sit flush with separators between them.
  • Top tabs are drawn rather than borrowed from ImGui selectables: their own height, an accent underline on the active one, and three text states.
  • Unread markers are count badges in the accent colour rather than a red dot. Red reads as an error; an unread message is not one.
  • Status bar slots are pills — channel with its status dot, privacy with its lock, counts, tells, and the version right-aligned. Each slot drops out on its own when the window gets too narrow.
  • Widget gallery under /hellion widgets (debug builds only) showing every drawn widget in its states.

Fixed

  • Compact message rows assumed a constant height and were clipped with a fixed 18px. They wrap, so they are not constant, and the real single line is 17px at the default font — the list drifted against the scrollbar. Both densities now plan from measured heights.
  • The message height cache ignored display scaling, which feeds word wrapping. Changing the scale left every cached row stale.
  • Layout constants across the sidebar, input bar, honorific header and message list did not scale. At 150% the text grew and the boxes did not; the quick buttons stopped fitting their column.
  • Vertical offsets were frozen values that only centred correctly at one font size. They are measured now.
  • The hover sheen kept its own timestamp map and only cleared it on un-hover, so a row that disappeared while hovered leaked its entry until the plugin reloaded.
  • Quick-button tooltips rendered inside the icon font, which has no ASCII glyphs, and came out as empty boxes. They are also localised now.
  • The theme preview in settings had been showing surfaces, an accent bar and an unread marker that the real sidebar never drew. Both sides match now, and the preview reads the same colour tokens.

Changed

  • Hover is a held value that rises at 14/s and falls at 8/s, rather than a one-shot sweep that stopped after 0.65s while the pointer was still there.
  • The active surface token went from 10% to 25% toward the primary colour. At 10% it was indistinguishable from an idle row.
  • Row fills follow the window's own opacity, so the sidebar no longer sits as a solid block inside a translucent window.

Internal

  • New style layer: Metrics (scale-aware layout values), HoverState (held hover per element), and five drawn widgets — Row, Badge, IconButton, LineDivider, Pill.
  • Pure halves split out for the build suite: MetricsMath, HoverMath, WidgetGeometry, LayoutFingerprint.
  • Test suite 829 → 892, and StatusBarCacheTests is back in the build after sitting in the exclusion block.

[1.9.0] — unreleased (local only)

Polish, tester-beta preparation and a concurrency hardening pass. Not published — the public release stays at v1.5.6, so the download links in repo.json deliberately still point there while the assembly version reads 1.9.0.

Fixed

  • Incoming messages could be dropped without a trace. The worker walked the tab list while another thread rewrote it, the resulting exception was swallowed by the surrounding error handler, and the message ended up nowhere: no tab entry, no notification sound, no tell routing.
  • Clicking a tab could open a different one, or crash the render loop, when tabs appeared or were evicted in the same frame. The window now renders from one consistent snapshot per frame.
  • An open tab context menu could silently re-bind to a different tab. Widget identity is derived from the tab itself now, not from its position in the list.
  • Tab.Clone() dropped the tab icon and legacy channel data.

Changed

  • Settings sliders write the configuration once, on release, instead of on every frame while dragging. Renaming a tab writes once when you finish, including when the context menu is dismissed by clicking elsewhere.
  • Tell history loads through a dedicated (Receiver, Date) index (schema 5). Opening a tell tab no longer sorts the entire conversation history first.
  • Pinning, unpinning and promoting tabs no longer write to disk while holding the tab lock.

Internal

  • New ConfigMapsLock guards the config maps the settings UI edits while a background save may be serialising them.
  • Removed DeferredSaveFrames: the debounce was fully wired but never armed.
  • Test suite 812 → 829, including migration and query-plan coverage.

Hellion Chat 1.5.6 — Settings Overhaul + Filter & Notification Polish (2026-05-23)

  • Settings window reorganised: ten tabs down to seven (General, Appearance, Chat, Window, Channels, Data & Privacy, About). Each tab now uses collapsible sections grouped by control type. Sections start collapsed every time you open a tab — less noise, easier to find what you need.
  • New sender-name display options under Chat → Messages: separate world-suffix and name-format modes (Full name / First name only / Initials × Never / Other worlds only / Always).
  • Plugin-only symbols now show a pre-send warning so other players do not get empty boxes (Chat → Messages → "Warn before sending plugin-only symbols").
  • Separate window opacity for focused vs. inactive chat window (Appearance → Window style → "Inactive window opacity"). The slider above sets the focused value.
  • Custom notification sound volume slider (General → Sound, and mirrored in Channels → per-tab → Notification). Affects only the three bundled custom sounds; the 16 game sounds are unaffected.
  • The per-tab regex filter that briefly shipped earlier in this cycle has been removed — FFXIV's built-in blackword filter covers the same need.
  • All 24 locale files updated for the new section labels and the v1.5.6 control labels (machine translation; native review continues via the Hellion Forge Discord).

Hellion Chat 1.5.5 — Upstream-Sync Tab-Features (2026-05-21)

A backlog-sync cycle. Inherited tab items: a failed-tell warning toast, per-tab notification sounds (16 game sounds or three bundled Hellion sounds with a preview button), an auto-focusing 512-character tab rename, a jump-to-latest button in the chat log header, and map-flag / item-link insertion from the chat input. Plus the Hellion Forge fox banner becomes a real image. Schema bumped to v18, additive fields only, no data migration.


Hellion Chat 1.5.4 — Polish and Motion (2026-05-20)

A polish cycle of three P3 items. Theme switches now crossfade smoothly over ~300 ms across every Hellion-rendered surface; the window background snaps deliberately so the per-window opacity override from Dalamud's pinning menu stays intact. A new header quick-picker — a palette button left of the cog — opens a compact popup for switching themes and tabs without opening Settings. Sidebar icons and card-mode message borders gain framerate-independent hover animations. A new "Reduce motion" toggle in Theme & Layout disables the crossfade, hover animations and unread-tab pulse for accessibility. No schema bump, migration v17 stays.

Hellion Chat 1.5.3 — Localisation Wave + Bundled-Font Overhaul (2026-05-19)

Multi-language pass plus a long-standing first-frame HITCH lands as a side effect of a font-stack rewrite. The bundled UI font swaps from Exo 2 to Inter Light. HellionChat now ships strings and renderable glyph coverage for 24 languages.

User-visible

  • Twenty-four selectable UI languages: Catalan, Czech, Danish, Dutch, English, Finnish, French, German, Greek, Hungarian, Italian, Japanese, Korean, Norsk bokmål, Polish, Portuguese (Brazil), Portuguese (Portugal), Romanian, Russian, Spanish, Swedish, Turkish, Ukrainian, Simplified Chinese, Traditional Chinese. The dropdown sorts alphabetically by endonym, "None" pinned first. Non-native translations are AI-assisted and flagged for community native-speaker review via the Hellion Forge Discord.
  • Bundled Inter Light replaces Exo 2 as the in-plugin font. Wider European coverage (Latin Extended-A/B, Greek polytonic, Cyrillic Supplement) so Czech, Polish, Romanian, Turkish, Hungarian, Greek and Ukrainian render without manual font configuration. SIL OFL 1.1, 343 KB.
  • NotoSansCjkRegular fallback layer added as a merge-on-top so Hangul, Simplified-Chinese characters specific to the post-1956 reform, and other CJK glyphs the FFXIV Japanese game font does not ship now render correctly inside the HellionChat UI.
  • First-frame HITCH dropped from ~74 ms (the v1.5.2 baseline that has held since v1.4.x) to a median of ~20 ms (5-reload sample: 23.6 / 20.4 / 17.9 / 20.1 / 19.2 ms, Linux/Wine; Windows baseline pending Jin's verification per the cross-platform-pflicht). The bundled-font path silently fell back to the FFXIV Axis game font for the entire v1.5.x series because of an early-return in the draw loop. The fix that routes RegularFont through draw also lands the defer-pattern win the v1.5.1 cycle was reaching for.
  • ExtraGlyphRanges activates automatically when the user picks a language that needs a non-Latin script. Selecting Korean enables the Korean glyph range and rebuilds the atlas without a manual toggle in Fonts & Colours.
  • New WarningText under the language dropdown notes that FFXIV's own chat input only fully supports EN, DE, FR and JA character sets. Other languages render inside HellionChat but may garble when typed into in-game chat or sent as messages.

Under the hood

  • Three-layer font stack in FontManager.BuildRegularFontHandle and BuildItalicFontHandle: Inter Light (or the user-selected global font) as primary, FFXIV JapaneseFont as merge 1 for native FFXIV kana/kanji style, NotoSansCjkRegular as merge 2 for everything else CJK.
  • Two new ExtraGlyphRanges flags: LatinExtended (U+0100-U+024F) and Greek (U+0370-U+03FF + U+1F00-U+1FFF). Implemented as builder.AddChar pair lists in SetUpRanges (no managed-pointer pinning needed).
  • LanguageOverride enum gains ten locales (Catalan, Czech, Danish, Finnish, Hungarian, Norwegian, Polish, Portuguese (Portugal), Turkish, Ukrainian) plus three previously commented-out entries (Italian, Korean, Norwegian re-enabled with code nb instead of no). New values are appended to the enum to keep existing user-config integer serialisation stable.
  • Crowdin gap closed: four ChatTwo keys added after the last community sync (Options_ColorSelectedInputChannelButton_Name / _Description, Options_HideInNewGamePlusMenu_Name / _Description) are now backfilled into the thirteen legacy Crowdin locales with per-key AI-translated markers.
  • Plugin init runs a one-shot migration that ORs in the matching ExtraGlyphRanges flag based on the user's current LanguageOverride. An update from v1.5.2 picks up the new coverage without the user having to toggle the language twice.
  • Plugin.cs:937 draw-path fixed: RegularFont is now pushed whenever either FontsEnabled or UseHellionFont is on. The previous Config.FontsEnabled-only check meant the bundled font path was silently dead whenever FontsAndColours.cs:50 force-set FontsEnabled = false on the UseHellionFont-toggle. Source of the HITCH win.
  • ExtraGlyphRanges settings panel is now reachable in all UseHellionFont / FontsEnabled combinations. The bundled-font branch used to short-circuit past it.
  • Resource bundle split: fork-added strings live in HellionStrings.resx (24 locales, 328 keys each) alongside the ChatTwo-Crowdin-heritage Language.resx (24 locales, 456 keys each). The Language siblings for the ten brand-new locales and Greek carry a Hellion Forge maintainer header that points reviewers at the Discord rather than the standalone-hosted Gitea.
  • Em-dash sweep across the EN source and 18 translations: in-prose em-dashes replaced with period or colon per the house style guide. Russian and Ukrainian keep their typographic norm where the em-dash is orthographically required (subject-predicate separator).
  • Bundled font asset rotation: HellionFont.ttf (Exo 2) plus its OFL notice removed from Resources/. Inter-Light.ttf plus Inter-OFL.txt take their place. FontManager references rename to BundledFontBytes / TryGetBundledFontBytes() for clarity (config field UseHellionFont keeps its name so existing user configs deserialize cleanly).

Migration

  • Migration v17 stays (no schema bump).
  • Existing UseHellionFont = true users transition transparently from Exo 2 to Inter Light on first reload.
  • Existing users with LanguageOverride != None get their matching ExtraGlyphRanges flag set on the first plugin init after the v1.5.3 update (Plugin.cs LoadAsync migration step).

Reserved for follow-up cycles

  • Native-speaker review pass for AI-assisted translations in the 13 legacy Crowdin locales (ca, es, fr, it, ja, ko, nl, pt-BR, ro, ru, sv, zh-Hans, zh-Hant) — corrections via the Hellion Forge Discord.

Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).


Hellion Chat 1.5.2 — First-Run Wizard Rework (2026-05-18)

UX patch. The single-page first-run wizard becomes a four-step staged-commit flow, the privacy profile catalogue gains a fourth entry "Roleplay", and a new power-settings stage surfaces six previously-hidden Configuration defaults. Existing v1.5.1 users see the new wizard once on first v1.5.2 boot via a new WizardLastShownVersion config marker.

User-visible:

  • Wizard layout: Welcome → Privacy profile → Power settings → Done. Forge-Bronze pagination dots, per-step Back / Decide later / Next footer. Decide-later and X-close both leave the existing config untouched; only the Finish ✓ click commits pending choices.
  • Fourth privacy profile "Roleplay": Privacy-First whitelist plus Say and both emote types, with a 30-day retention window for Say and 90 days for the two emote channels. Shout, Yell and NoviceNetwork stay out — public-distance noise from strangers is not story content.
  • Privacy picker becomes a 2×2 grid. Casual stays the recommended option with a ★ marker.
  • Power-settings stage surfaces six existing Configuration fields in one place: Load Previous Session, Filter Include Previous Sessions, Auto-Tell-Tabs History Preload, Compact Density, Prettier Timestamps, plus a built-in theme picker. No new settings are introduced — the stage just collects what was previously buried in Settings → Privacy / Chat / Data Management / Appearance.
  • Inline test hint on the done stage: type /tell <Player Name> into chat surfaces the auto-tell-tab spawn mechanism for new users.
  • Wizard window starts at 720×480 (was 900×560) and can shrink to 600×400. Step 1 wraps the fox banner in a collapsible TreeNode, folded by default — onboarding copy stays primary.
  • Existing v1.5.1 users get the new wizard surfaced once on first v1.5.2 boot. A new WizardLastShownVersion config field tracks the most recent version whose wizard was shown; Plugin.LoadAsync resets FirstRunCompleted once when the constant 1.5.2 doesn't match.

Under the hood:

  • WizardStateSmokeStep registered with /xlperf. Variant 1 walks the four steps with empty pending state to pin the no-op CommitPending path. Variant 2 picks Roleplay on Step 2, skips Step 3, commits, and asserts LoadPreviousSession / FilterIncludePreviousSessions stayed on their pre-test value — pinning the null-semantics contract. The step snapshots six privacy / retention fields before Variant 2 and CleanUp() restores them, so back-to-back runs don't drift the active profile.
  • Twelve pure-helper xUnit Facts in the Build Suite (Privacy/PrivacyDefaultsTests.cs) cover all four profile whitelists plus the new Roleplay retention overrides.
  • Configuration grows one optional string field WizardLastShownVersion (default empty). No schema bump — migration v17 still applies.

Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).

Full release notes on the Gitea release page.


Hellion Chat 1.5.1 — FontAtlas Refactor and Hellion Forge Signature (2026-05-17)

Hybrid FontManager refactor plus an embedded Hellion Forge provenance mark.

  • FontManager handle creation moves into the ctor inside a single atlas.SuppressAutoRebuild() block — the font atlas now builds once per plugin load instead of four to five times.
  • Hybrid property model: Axis, AxisItalic and FontAwesome become init-only handles; RegularFont and ItalicFont stay mutable because the eight font settings still need to replace them at runtime via the renamed RebuildDelegateFonts() path, without a plugin reload.
  • FontAwesome reuses Dalamud's UiBuilder.IconFontFixedWidthHandle instead of building its own atlas slot.
  • BuildFontsAsync and BuildFonts are removed; the live mutation path is RebuildDelegateFonts() now.
  • Two new self-test steps register with /xlperf: FontManager ctor smoke (every handle non-null, no atlas load-exception) and push smoke (Push() returns without throwing).
  • Hellion Forge signature embedded in the plugin DLL: a fox-head ASCII silhouette is prepended to the /xllog bootstrap banner on every plugin load, and a full fox banner with "Hellion Forge" set inside the body is available as a folded TreeNode in the First-Run Wizard and Settings → Information tab. Drawn by Julia Moon.
  • Honorific full-gradient port (originally scheduled for this cycle) was dropped: Honorific 3.2 exposes no IPC for the rendered gradient frame, and an in-plugin port of the colour palette was declined. The integration stays at the v1.4.7 glow-only shape.

The 10× HITCH cut targeted from the v1.5.0 cross-plugin baseline did not land — HITCH stays around 80 ms because the cost lives in the UiBuilder first-frame render path, not in the atlas build (which this cycle did reduce from 4-5 builds per load to 1). A first-frame render investigation is reserved for a later cycle. No settings changes, no migration. v17 stays.

Full release notes on the Gitea release page.


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.

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.

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.

Migration v17 stays (no schema bump).

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.

  • 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) modelled on Lightless.
  • Migration v17 stays (no schema bump).

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).

  • 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.
  • 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.

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.

  • 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).

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.

  • 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

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.

  • 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

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.

  • 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

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.

  • 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

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.

  • 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

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.

  • 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.

Modding & support: join Hellion Forge — https://discord.gg/X9V7Kcv5gR

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.

  • 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.

Modding & support: join Hellion Forge — https://discord.gg/X9V7Kcv5gR

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.

  • 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

Modding & support: join Hellion Forge — https://discord.gg/X9V7Kcv5gR

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.

  • 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
  • Maintainer attribution buttons for Honorific repo and Caraxi
  • New service-class pattern under HellionChat/Integrations/

Modding & support: join Hellion Forge — https://discord.gg/X9V7Kcv5gR

Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).


v1.2.3 — Theme Expansion (2026-05-06)

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
  • 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.

v1.2.1 — Settings Cleanup (2026-05-06)

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.
  • 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.
  • 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.
  • Unused ShowThemeQuickPicker schema field.

Migration

  • v15 → v16 with backup at pluginConfigs/HellionChat.json.pre-v16-backup.
  • All other settings preserved unchanged.
  • One-time toast on first start if Style override was previously active.

v1.2.0 — Layout Refresh (2026-05-05)

Added

  • 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
  • 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

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)

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
  • 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 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.

[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.

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.
  • 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.

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.

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.

Internal

  • 51 local unit tests (theme records, registry, JSON round-trip, sanity per built-in theme). Tests are gitignored.

[1.0.3] — 2026-05-04 — Polish Patch

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.

Release Notes 1.0.3


[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.

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


[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.

Release Notes 1.0.0


[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.

Release Notes 0.6.1


[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.

Release Notes 0.6.0


[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.

Release Notes 0.5.4


[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.

Release Notes 0.5.3


Earlier Versions

Releases before 0.5.3 (bootstrap phase 0.1.0 to 0.5.2) are available directly on the Gitea release stream:

All Releases


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.