Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1d6ca3bbd | ||
|
|
4ab9202533 | ||
|
|
5e15d34eeb | ||
|
|
b9feb8650f | ||
|
|
0f9858a3d3 | ||
|
|
a3379818eb | ||
|
|
d5c7db9f43 | ||
|
|
6e24885241 |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
subtitle: "Kontextmenü in Pop-outs, Screenshot-Modus"
|
||||
versionsnatur: "Fehlerbehebung"
|
||||
---
|
||||
- **Das Kontextmenü ging in Pop-out-Fenstern nicht auf.** Ein Rechtsklick auf einen Namen oder ein Item hat dort schlicht nichts gemacht. Alle Chat-Flächen teilen sich einen Popup-Zustand, und das zuerst gezeichnete Fenster hat das Menü verworfen, bevor das Pop-out überhaupt an der Reihe war. Das Menü bleibt jetzt bei dem Fenster, aus dem der Klick kam.
|
||||
- **Der Screenshot-Modus ließ sich nicht ausschalten.** Er wird gespeichert, aber keiner der beiden Schalter hat die Änderung in die Konfiguration geschrieben. Einmal gespeichert kam er bei jedem Laden des Plugins wieder. Beide Schalter speichern jetzt. Wer ihn gerade festhängen hat, klickt ihn einmal aus.
|
||||
- **Vom Spiel eingefärbter Text konnte unsichtbar werden.** Manche dieser Farben kommen ohne Alpha-Byte an und wurden beim Umsortieren vollständig transparent.
|
||||
@@ -61,7 +61,7 @@ public sealed class FontManager : IDisposable
|
||||
// widget gallery exposes it.
|
||||
internal static float SenderWeight = 1.3f;
|
||||
|
||||
// Wired post-build (B4b-3); a Func keeps FontManager off the theme layer.
|
||||
// Wired post-build; a Func keeps FontManager off the theme layer.
|
||||
private Func<ThemeTypography?>? _typographySource;
|
||||
|
||||
// Lets RebuildDelegateFontsIfChanged skip rebuilds when the size is unchanged.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Dalamud.NET.Sdk/15.0.0">
|
||||
<PropertyGroup>
|
||||
<!-- Independent versioning; see yaml changelog for upstream Chat 2 base -->
|
||||
<Version>2.0.2</Version>
|
||||
<Version>2.0.3</Version>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- Use lock file to pin exact versions -->
|
||||
|
||||
@@ -35,6 +35,14 @@ tags:
|
||||
- Replacement
|
||||
- Privacy
|
||||
changelog: |-
|
||||
**v2.0.3 — Pop-out context menu, screenshot mode (2026-08-20)**
|
||||
|
||||
- **The context menu never opened in a pop-out window.** Right-clicking a name or an item there did nothing at all. Every chat surface shares one popup state, and whichever window drew first cleared the menu it had not opened before the pop-out got its turn. The menu now stays with the window the click came from.
|
||||
- **Screenshot mode could not be turned off.** It is a saved setting, but neither of its two toggles wrote the change to the config file, so switching it off never survived a reload and the mode came back on at every plugin load. Both toggles save now. If it is stuck on for you, one click off is enough.
|
||||
- **Text the game colours itself could render invisible.** Some of those colours arrive without an alpha byte, and the byte order swap then left them fully transparent.
|
||||
|
||||
---
|
||||
|
||||
**v2.0.2 — Emotes out, placeholders fixed (2026-08-19)**
|
||||
|
||||
- **BetterTTV emote support is gone.** Its shared-emote endpoint went behind authentication, and that was where nearly all of them came from — what remained was a 65-entry global set, eleven of which are on the known-broken list, so 54 mostly static images from Twitch's early days. Exactly one was animated, and that one wanted 492 frames and 37 MB of video memory. The plugin now makes no outbound network calls at all. Messages stored with emotes still read fine; they show the code that was typed.
|
||||
@@ -83,18 +91,4 @@ changelog: |-
|
||||
|
||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2). The two codebases have diverged far enough that they no longer line up.
|
||||
|
||||
---
|
||||
|
||||
**v1.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).
|
||||
|
||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
Earlier history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases
|
||||
|
||||
@@ -29,7 +29,7 @@ internal sealed class ThemeRegistryInitHostedService(
|
||||
foreach (var _ in registry.AllCustom()) { }
|
||||
registry.SwitchSilent(Plugin.Config.Theme);
|
||||
|
||||
// B4b-3: point font sizes at the active theme's typography, wire future
|
||||
// Point font sizes at the active theme's typography, wire future
|
||||
// theme switches to the atlas rebuild, and apply the boot theme's override.
|
||||
fontManager.SetTypographySource(() => registry.Active.Typography);
|
||||
registry.SetActiveChangedCallback(() => fontManager.RebuildDelegateFontsIfChanged());
|
||||
|
||||
@@ -49,6 +49,7 @@ internal sealed class PayloadHandler
|
||||
public uint LastHoverCounter;
|
||||
|
||||
private (Chunk, Payload?)? _popup;
|
||||
private object? _popupOwner;
|
||||
|
||||
public PayloadHandler(
|
||||
ThemeRegistry themes,
|
||||
@@ -69,6 +70,10 @@ internal sealed class PayloadHandler
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// Every chat surface shares this handler, so each one claims it before it
|
||||
// renders. Without that the popup cannot tell whose click it belongs to.
|
||||
internal object? ActiveSurface { get; set; }
|
||||
|
||||
internal void Draw()
|
||||
{
|
||||
DrawPopups();
|
||||
@@ -84,7 +89,9 @@ internal sealed class PayloadHandler
|
||||
|
||||
private void DrawPopups()
|
||||
{
|
||||
if (_popup == null)
|
||||
// A foreign surface finds no open popup here and would otherwise read
|
||||
// that as "closed" and drop the state before the owner gets to draw.
|
||||
if (_popup == null || !PopupOwnership.Owns(_popupOwner, ActiveSurface))
|
||||
return;
|
||||
|
||||
var (chunk, payload) = _popup.Value;
|
||||
@@ -93,6 +100,7 @@ internal sealed class PayloadHandler
|
||||
if (!popup.Success)
|
||||
{
|
||||
_popup = null;
|
||||
_popupOwner = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,7 +194,10 @@ internal sealed class PayloadHandler
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.Checkbox(Language.Context_ScreenshotMode, ref Plugin.Config.ScreenshotMode);
|
||||
// The flag is persisted, so every toggle has to write it. Without the
|
||||
// write it only sticks when some unrelated save happens to run after.
|
||||
if (ImGui.Checkbox(Language.Context_ScreenshotMode, ref Plugin.Config.ScreenshotMode))
|
||||
Plugin.Instance.SaveConfig();
|
||||
|
||||
if (ImGui.Selectable(Language.Context_HideChat))
|
||||
Plugin.Config.HideChat = true;
|
||||
@@ -934,6 +945,7 @@ internal sealed class PayloadHandler
|
||||
private void RightClickPayload(Chunk chunk, Payload? payload)
|
||||
{
|
||||
_popup = (chunk, payload);
|
||||
_popupOwner = ActiveSurface;
|
||||
ImGui.OpenPopup(PopupId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using Dalamud.Plugin.SelfTest;
|
||||
namespace HellionChat.SelfTests;
|
||||
|
||||
// Optional metric capture. Accumulates 1000 steady-state frames of ImGui IO
|
||||
// counters plus the plugin's full-Draw wall-time (Plugin.LastDrawMs, B5-1),
|
||||
// counters plus the plugin's full-Draw wall-time (Plugin.LastDrawMs),
|
||||
// then writes a single perf-baseline.json into the plugin ConfigDirectory so
|
||||
// the cycle-notes author can copy the baseline figures without a separate
|
||||
// profiling harness. The step only records — it never fails on a threshold
|
||||
|
||||
@@ -13,7 +13,7 @@ internal static class ThemeJsonLoader
|
||||
// policy from the v2.x style refactor: v1 user themes are not migrated,
|
||||
// they're silently ignored so the loader stays free of legacy mapping
|
||||
// code. Any other malformed input still throws FormatException.
|
||||
// B4b-2: callers must pass the logger or the default-fill warnings go silent.
|
||||
// Callers must pass the logger or the default-fill warnings go silent.
|
||||
public static Theme? LoadFromString(string json, ILogger? logger = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
|
||||
@@ -901,7 +901,10 @@ internal sealed class InputBar
|
||||
)
|
||||
)
|
||||
{
|
||||
// Persisted flag -- write it here too, or turning it back off
|
||||
// never reaches the config file.
|
||||
Plugin.Config.ScreenshotMode = !Plugin.Config.ScreenshotMode;
|
||||
Plugin.Instance.SaveConfig();
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
@@ -158,6 +158,11 @@ internal sealed class MessageList
|
||||
return;
|
||||
}
|
||||
|
||||
// Claim the shared handler, so a popup opened here stays with this
|
||||
// window instead of being cleared by whichever surface draws first.
|
||||
if (_handler is not null)
|
||||
_handler.ActiveSurface = this;
|
||||
|
||||
// No own ImRaii.Child here — MainWindow already wraps the message
|
||||
// area in one. Nesting would give the window two stacked scrolls
|
||||
// and a runaway content-height computation.
|
||||
|
||||
@@ -52,7 +52,7 @@ internal sealed class TopTabBar
|
||||
for (var i = 0; i < tabs.Count; i++)
|
||||
{
|
||||
var tab = tabs[i];
|
||||
// POP-1b: a popped-out tab is owned by its pop-out window, not the main
|
||||
// A popped-out tab is owned by its pop-out window, not the main
|
||||
// strip (1.5.6 exclusivity). Gate on the pool, not Tab.PopOut (stale flag).
|
||||
if (_pool.IsOpen(tab.Identifier))
|
||||
continue;
|
||||
|
||||
@@ -175,6 +175,7 @@ internal sealed class InputPreview : Window
|
||||
_handlerLender.ResetCounter();
|
||||
|
||||
var handler = _handlerLender.Borrow();
|
||||
handler.ActiveSurface = this;
|
||||
_chunkRenderer.DrawChunks(
|
||||
_previewMessage!.Content,
|
||||
wrap: true,
|
||||
|
||||
@@ -155,7 +155,7 @@ internal sealed class ChannelPopoutWindow : Window, IFocusableChatWindow
|
||||
if (Bound is null)
|
||||
return;
|
||||
|
||||
// POP-1f: the bound tab is live-visible in this pop-out, so it carries no
|
||||
// The bound tab is live-visible in this pop-out, so it carries no
|
||||
// unread badge — mirror MainWindow's per-frame zero for the active tab.
|
||||
// View-state reset only (tab.Messages store is untouched).
|
||||
Bound.Unread = 0;
|
||||
|
||||
@@ -310,9 +310,9 @@ internal sealed class MainWindow : Window, IFocusableChatWindow
|
||||
TabLifecycleHelpers.OnTabActivated(reseed, active);
|
||||
}
|
||||
|
||||
// POP-1c: a popped-out tab must not stay the main window's active surface
|
||||
// A popped-out tab must not stay the main window's active surface
|
||||
// (1.5.6 exclusivity). Re-anchor to the first non-popped tab the moment the
|
||||
// active one is popped; null when every tab is popped (POP-1d guards Draw).
|
||||
// active one is popped; null when every tab is popped, which Draw guards for.
|
||||
// Runs post-seed, before the sidebar/top-tab draw, so the popped tab never
|
||||
// renders. Idempotent: PickMainActiveTab returns the same reference once
|
||||
// settled, so OnTabActivated fires only on the pop frame.
|
||||
|
||||
@@ -66,8 +66,23 @@ internal static class ColourUtil
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Cherry-picked from ChatTwo upstream a51789b + 5b58513 (Infiziert90,
|
||||
// 2026-06-12). Both guards belong together -- a51789b alone turns a zero
|
||||
// colour into opaque black, which then slips past the invisible-text
|
||||
// guard in ChunkUtil and paints over the inherited colour.
|
||||
// TEST-MIRROR: ../../../Hellion Build test/Util/ColourUtilTests.cs
|
||||
// ---------------------------------------------------------------
|
||||
public static unsafe uint ArgbToRgba(uint x)
|
||||
{
|
||||
if (x == 0)
|
||||
return 0;
|
||||
|
||||
// The game omits the alpha byte on some GlobalValue colours, and the
|
||||
// swap below would leave alpha at 0 -- invisible text.
|
||||
if (x <= 0x00FFFFFFu)
|
||||
x |= 0xFF000000u;
|
||||
|
||||
var buf = (byte*)&x;
|
||||
(buf[1], buf[2], buf[3], buf[0]) = (buf[0], buf[1], buf[2], buf[3]);
|
||||
return x;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace HellionChat.Util;
|
||||
|
||||
// Chat surfaces share one payload handler and all render it in the same frame.
|
||||
// A popup belongs to the surface that opened it; the others must leave its
|
||||
// state alone instead of reading "no popup here" as "popup was closed".
|
||||
internal static class PopupOwnership
|
||||
{
|
||||
// Reference identity on purpose -- two surfaces can compare equal without
|
||||
// being the same window.
|
||||
internal static bool Owns(object? owner, object? surface) =>
|
||||
owner is not null && ReferenceEquals(owner, surface);
|
||||
}
|
||||
@@ -165,7 +165,7 @@ internal static class TabLifecycleHelpers
|
||||
|
||||
// Sectioned sidebar render order (1.5.6 parity): persistent → pinned TempTabs →
|
||||
// unpinned TempTabs. Returns indices into the live tab list so the list order is
|
||||
// never mutated and DrawRow keeps each tab's ORIGINAL index for PushID. POP-1a:
|
||||
// never mutated and DrawRow keeps each tab's ORIGINAL index for PushID.
|
||||
// isPoppedOut excludes tabs bound to a pop-out window — an excluded tab draws no
|
||||
// row and its pool's section header gates on the first tab actually reached. Pure
|
||||
// + Dalamud-free so the Build-Suite can pin it.
|
||||
@@ -215,7 +215,7 @@ internal static class TabLifecycleHelpers
|
||||
return persistent;
|
||||
}
|
||||
|
||||
// POP-1c: returns the tab the main window should display — the current tab if it
|
||||
// Returns the tab the main window should display — the current tab if it
|
||||
// is not popped out, else the FIRST non-popped tab in list order, else null when
|
||||
// every tab is popped. NOTE: deliberately NOT ResetActiveTabIfRemoved's
|
||||
// unconditional Tabs[0] — Tabs[0] may itself be popped and would re-trigger the
|
||||
@@ -243,7 +243,7 @@ internal static class TabLifecycleHelpers
|
||||
Popout,
|
||||
}
|
||||
|
||||
// POP-1f: the alreadyPopped case is the whole reason this is a function.
|
||||
// The alreadyPopped case is the whole reason this is a function.
|
||||
//
|
||||
// Revealing a popped-out tab in the main window looks like it does nothing,
|
||||
// and then does something worse: PickMainActiveTab re-anchors on the next
|
||||
@@ -268,12 +268,12 @@ internal static class TabLifecycleHelpers
|
||||
_ => switchAlways && !alreadyPopped ? TellReveal.MainWindow : TellReveal.None,
|
||||
};
|
||||
|
||||
// POP-1e: popout-aware sibling of WrapTabIndex for the ChatTabForward/Backward
|
||||
// Popout-aware sibling of WrapTabIndex for the ChatTabForward/Backward
|
||||
// keybind. Steps from current by delta's sign (±1), wrapping, and returns the
|
||||
// first index whose tab is NOT popped out within tabs.Count steps; returns current
|
||||
// when every other tab is popped (no-op), the list is empty, or a single tab.
|
||||
// Skipping popped tabs here is what stops the POP-1c re-anchor from making the
|
||||
// cycle stick (CYCLE-1). Pure + Dalamud-free.
|
||||
// Skipping popped tabs here is what stops the PickMainActiveTab re-anchor
|
||||
// from making the cycle stick. Pure + Dalamud-free.
|
||||
// TEST-MIRROR: ../../../Hellion Build test/_Helpers/NextMainTabIndexTests.cs
|
||||
internal static int NextMainTabIndex(
|
||||
int current,
|
||||
|
||||
+25
-38
@@ -20,8 +20,8 @@ Last reviewed: 2026-08-18 (HellionChat v1.12.0).
|
||||
`pluginConfigs/HellionChat/` directory.
|
||||
- The plugin does not phone home. No telemetry, no analytics, no crash reporter, no usage counter,
|
||||
no remote update check beyond what Dalamud itself does.
|
||||
- One outbound network call exists by design: the BetterTTV emote service (for chat emotes). It is
|
||||
documented in detail below and can be reasoned about per request.
|
||||
- As of v2.0.2 the plugin makes **no outbound network requests at all**. The one that existed, the
|
||||
BetterTTV emote service, was removed along with the feature it served.
|
||||
- You can export every message the plugin has stored, in Markdown, JSON or CSV. You can delete it by
|
||||
channel, by age, or all of it at once.
|
||||
|
||||
@@ -42,8 +42,9 @@ HellionChat keeps three kinds of state on your machine, all under
|
||||
ExtraChat. Public chat, NPC dialogue, system messages and battle logs are dropped on the storage
|
||||
layer and never written to disk.
|
||||
|
||||
3. **Cached emote images** (`EmoteCacheV1/` directory). Image files downloaded from BetterTTV when
|
||||
an emote appears in a message you receive. See "Outbound network calls" below.
|
||||
Earlier versions kept a third item here, an `EmoteCacheV1/` directory of images downloaded from
|
||||
BetterTTV. Nothing writes to it as of v2.0.2. If you used a version before that, the directory is
|
||||
still on disk and can be deleted by hand; the plugin no longer reads or creates it.
|
||||
|
||||
There is no shared state with the upstream Chat 2 plugin. `pluginConfigs/HellionChat/` is
|
||||
independent from `pluginConfigs/ChatTwo/`.
|
||||
@@ -73,35 +74,23 @@ turn the retention sweep on in the settings. Until then, stored messages stay un
|
||||
|
||||
## Outbound network calls
|
||||
|
||||
HellionChat makes one kind of automatic outbound network request, inherited from upstream Chat 2 and
|
||||
documented here because "GDPR-by-design" means you should know what your client does on your behalf.
|
||||
The second one this section used to list, the Lodestone font download, was removed in v1.0.4 and the
|
||||
font is bundled instead.
|
||||
**None.** As of v2.0.2 the plugin makes no automatic outbound network requests of any kind. Both
|
||||
calls this section used to describe are gone, and the section is kept so the claim can be checked
|
||||
against what it replaced rather than simply asserted.
|
||||
|
||||
### 1. BetterTTV emote service (`api.betterttv.net`, `cdn.betterttv.net`)
|
||||
### 1. BetterTTV emote service (removed in v2.0.2)
|
||||
|
||||
- **What it does:** When a chat message arrives that references a BetterTTV emote, the plugin asks
|
||||
the BetterTTV API for the emote metadata and downloads the image from the BetterTTV CDN to display
|
||||
it inline.
|
||||
- **What is sent:** A standard HTTPS GET request. Your IP address reaches BetterTTV (unavoidable for
|
||||
any HTTPS request); the request itself contains no identifying user data, no character name, no
|
||||
message text. Only the emote ID being looked up is in the URL path.
|
||||
- **When it triggers:**
|
||||
- The emote _list_ (global emotes plus the top-1500 community emotes over fifteen API pages) is
|
||||
fetched from `api.betterttv.net` once per session at plugin startup, provided the **Show
|
||||
emotes** option is on. This first list-fetch happens before any chat message has arrived.
|
||||
BetterTTV's edge therefore sees your IP as soon as the plugin loads, not only after an emote is
|
||||
mentioned.
|
||||
- The individual emote _images_ on `cdn.betterttv.net` are fetched on demand, only when an
|
||||
incoming chat message contains a token matching one of the cached IDs. These are cached locally
|
||||
(`EmoteCacheV1/`) and reused across sessions.
|
||||
- **Cached:** Yes, in `EmoteCacheV1/`. A given emote is downloaded once per machine and reused.
|
||||
- **How to opt out:** Turn off the **Show emotes** option in Settings → Chat → Display modes. With
|
||||
it disabled, the emote cache does not load and no requests to BetterTTV are made for the rest of
|
||||
the session.
|
||||
- **BetterTTV's privacy policy:** <https://betterttv.com/privacy>
|
||||
Until v2.0.2 the plugin fetched an emote list from `api.betterttv.net` once per session at startup,
|
||||
and individual images from `cdn.betterttv.net` on demand, caching them in `EmoteCacheV1/`. The
|
||||
startup fetch meant BetterTTV's edge saw your IP as soon as the plugin loaded, whether or not an
|
||||
emote ever appeared in your chat. A setting could switch it off, but it was on by default.
|
||||
|
||||
Source: `HellionChat/EmoteCache.cs`.
|
||||
The feature was removed rather than defaulted off: BetterTTV moved its shared-emote endpoint behind
|
||||
authentication, which left 54 mostly static images from a set of 65, and that is not worth a
|
||||
connection nobody asked for. The download path, the cache directory and the renderer are gone from
|
||||
the source.
|
||||
|
||||
Messages already stored with emotes in them remain readable and show the code that was typed.
|
||||
|
||||
### 2. Square Enix Lodestone font (removed in v1.0.4)
|
||||
|
||||
@@ -204,13 +193,12 @@ messages. Existing data needs the retroactive cleanup to apply retroactively, by
|
||||
|
||||
| Party | Why they appear | What reaches them | Their privacy policy |
|
||||
| ---------------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------ | -------------------------------------- |
|
||||
| BetterTTV (NightDev LLC) | Optional emote rendering | HTTPS request for an emote ID; your IP | <https://betterttv.com/privacy> |
|
||||
| Hellion Forge (Gitea, self-hosted by Hellion Online Media) | Plugin distribution via custom repo, issue tracker | Whatever the Gitea instance sees from any HTTPS request to a public repo | <https://hellion-media.de/datenschutz> |
|
||||
| Dalamud / XIVLauncher (goatcorp) | Plugin loader, font subsystem, repo polling | Whatever Dalamud reports for itself; out of HellionChat's scope | <https://github.com/goatcorp/Dalamud> |
|
||||
|
||||
The Hellion Forge Gitea instance and the Dalamud/XIVLauncher loader are unavoidable for anyone using
|
||||
HellionChat through Dalamud at all. BetterTTV is the only third party HellionChat introduces on top
|
||||
of that baseline, and it is opt-out via settings.
|
||||
Both are unavoidable for anyone using HellionChat through Dalamud at all. Since v2.0.2 the plugin
|
||||
introduces no third party on top of that baseline: BetterTTV was the only one, and it left with the
|
||||
emote feature.
|
||||
|
||||
---
|
||||
|
||||
@@ -223,11 +211,10 @@ plugin pulls in:
|
||||
- `Microsoft.Data.Sqlite`: local SQLite access, no network.
|
||||
- `morelinq`: LINQ helpers, no network.
|
||||
- `Pidgin`: parser combinators, no network.
|
||||
- `SixLabors.ImageSharp`: image decoding (used for the BetterTTV emote pipeline), no network on its
|
||||
own.
|
||||
- `SixLabors.ImageSharp`: image decoding (icon handling), no network on its own.
|
||||
|
||||
The single network call listed under "Outbound network calls" is written directly in HellionChat's
|
||||
own source, not delegated to a dependency.
|
||||
No dependency opens a connection on the plugin's behalf, and since v2.0.2 the plugin opens none
|
||||
itself either.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/actions/workflows/build.yml)
|
||||
[](LICENSE)
|
||||
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/latest)
|
||||
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/latest)
|
||||
[](https://github.com/goatcorp/Dalamud)
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](https://www.finalfantasyxiv.com/)
|
||||
@@ -11,7 +11,7 @@
|
||||
<img src="docs/images/hellion-forge.png" alt="Hellion Forge" width="180" />
|
||||
</p>
|
||||
|
||||
**Version 2.0.2** — Privacy-first chat plugin for FINAL FANTASY XIV / Dalamud, originally
|
||||
**Version 2.0.3** — Privacy-first chat plugin for FINAL FANTASY XIV / Dalamud, originally
|
||||
forked from [Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2).
|
||||
|
||||
Hellion Chat is a privacy-first chat plugin that began as a fork of Chat 2. The core it grew from
|
||||
@@ -84,9 +84,9 @@ Hellion Chat is developed under **Hellion Forge**, the specialized modding and p
|
||||
- **Export** to Markdown, JSON, or CSV via the Dalamud file dialog (GDPR Art. 15 right of access).
|
||||
Narrow it by channel group, by age in days, or by a substring of the sender's name.
|
||||
- **Full privacy overview** in [`PRIVACY.md`](PRIVACY.md) and third-party components in
|
||||
[`docs/THIRD_PARTY_NOTICES.md`](docs/THIRD_PARTY_NOTICES.md): what is stored, the single outbound
|
||||
call that exists and how to switch it off (BetterTTV), an explicit no-telemetry statement,
|
||||
and the mapping of GDPR rights (Art. 15/17/18/20/21) to concrete plugin functions.
|
||||
[`docs/THIRD_PARTY_NOTICES.md`](docs/THIRD_PARTY_NOTICES.md): what is stored, an explicit
|
||||
no-telemetry statement, and the mapping of GDPR rights (Art. 15/17/18/20/21) to concrete plugin
|
||||
functions. As of v2.0.2 the plugin makes no outbound network requests at all.
|
||||
|
||||
### Onboarding
|
||||
|
||||
@@ -143,7 +143,6 @@ Deuteranopia/Protanopia-safe (red-green color blindness) based on the Wong/Okabe
|
||||
|
||||
### Stability
|
||||
|
||||
- BetterTTV cache crash fix (null key handling).
|
||||
- Font atlas build fallback for missing system fonts.
|
||||
- Defensive wrapping of all migration operations.
|
||||
|
||||
@@ -272,9 +271,6 @@ Linux / XIVLauncher Core:
|
||||
```bash
|
||||
mv ~/.xlcore/pluginConfigs/ChatTwo/chat-sqlite.db \
|
||||
~/.xlcore/pluginConfigs/HellionChat/chat-sqlite.db
|
||||
[ -d ~/.xlcore/pluginConfigs/ChatTwo/EmoteCacheV1 ] && \
|
||||
mv ~/.xlcore/pluginConfigs/ChatTwo/EmoteCacheV1 \
|
||||
~/.xlcore/pluginConfigs/HellionChat/
|
||||
```
|
||||
|
||||
Windows / XIVLauncher:
|
||||
|
||||
@@ -24,7 +24,6 @@ I respond on weekdays during European business hours.
|
||||
influence)
|
||||
- The privacy filter in `MessageStore.cs` and the export pipeline
|
||||
- The configuration migration logic
|
||||
- The `EmoteCache` HTTP client and path handling
|
||||
- The Auto-Tell-Tabs spawn logic and history preload
|
||||
|
||||
### Out of scope
|
||||
|
||||
@@ -11,6 +11,26 @@ releases as an overview and links to the release pages for details.
|
||||
|
||||
---
|
||||
|
||||
## [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
|
||||
|
||||
@@ -12,6 +12,18 @@ be a poor fit for the plugin's privacy-first scope during brainstorming.
|
||||
|
||||
## Released
|
||||
|
||||
**v2.0.2 — Emotes out, placeholders fixed (2026-08-19)** removed BetterTTV emote
|
||||
support entirely. Its shared-emote endpoint went behind authentication and that was
|
||||
where nearly all of them came from; what remained was 54 mostly static images, one
|
||||
of them animated at 492 frames. With it went the plugin's only outbound network
|
||||
call. Also fixed five settings descriptions that printed `{0}` instead of the
|
||||
plugin name, and replaced the preview images, which were older than every cycle in
|
||||
2.0.0.
|
||||
|
||||
**v2.0.1 — Hotfix (2026-08-19)** lifted MessagePack to 3.1.7 and repaired the
|
||||
release workflow, which built the 2.0.0 archive successfully and then failed to
|
||||
attach it.
|
||||
|
||||
**v2.0.0 — Rebuilt, Repaired, Reset (2026-08-19)** ships everything that was
|
||||
developed as v1.6.0 through v1.15.0. Those versions were never published on their
|
||||
own: the whole window layer was being rewritten from ImGui defaults to custom
|
||||
@@ -66,6 +78,25 @@ follows.
|
||||
Native-speaker review of the AI-assisted v1.5.3 translations (13 legacy Crowdin locales) runs in
|
||||
parallel as a continuous correction pass, gathered via the Hellion Forge Discord.
|
||||
|
||||
### Carried over from the 2.0.x cycle
|
||||
|
||||
Small items that surfaced during the 2.0.0 release and were deliberately left for
|
||||
a later pass rather than rushed into a patch:
|
||||
|
||||
- **Comment length.** 47 comment blocks run to 12 lines or more, the longest at 41.
|
||||
The task codes and personal names came out in 2.0.0; trimming the long ones needs
|
||||
judgement rather than a pattern, since several carry reasoning that is now the
|
||||
basis of the style reference.
|
||||
- **Channel names are only translated in 10 of 25 languages.** Spanish had two of
|
||||
three translated and one left in English, which was corrected. The other 15 sit
|
||||
fully in English, which is correct for anyone on an English client — FFXIV ships
|
||||
four client languages — but inconsistent where a language does have its own.
|
||||
- **An orphaned `EmoteCacheV1/` directory** stays on disk for anyone who ran a
|
||||
version before 2.0.2. Nothing reads or writes it. A one-time cleanup on load
|
||||
would be tidier than asking people to delete it by hand.
|
||||
- **`Metrics.Scale` uses the deprecated `ImGuiHelpers.GlobalScaleSafe`**, which is
|
||||
the only compiler warning left in the build.
|
||||
|
||||
---
|
||||
|
||||
## v1.5.6 — Settings Overhaul + Filter & Notification Polish (released 2026-05-23)
|
||||
|
||||
@@ -55,13 +55,12 @@ 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":
|
||||
their own, and as of v2.0.2 neither does HellionChat itself. Both calls this list used to name are
|
||||
gone:
|
||||
|
||||
- `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.
|
||||
- `HellionChat/EmoteCache.cs` → BetterTTV API + CDN, removed in v2.0.2 along with the emote feature.
|
||||
- The Square Enix Lodestone font download (`FontManager.cs`), removed in v1.0.4 — a leftover from
|
||||
upstream's removed webinterface feature that was no longer consumed.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"Author": "Jon Kazama (Hellion Forge)",
|
||||
"Name": "Hellion Chat",
|
||||
"InternalName": "HellionChat",
|
||||
"AssemblyVersion": "2.0.2.0",
|
||||
"AssemblyVersion": "2.0.3.0",
|
||||
"Description": "A Hellion Forge plugin — privacy-focused chat replacement for FINAL FANTASY XIV, built for EU, US and JP data rules.\n\nBy default only your own conversations are stored. Public chat, NPC dialogue, system messages and battle logs are discarded at the storage layer unless you opt in. Retention windows are configurable per channel, history can be wiped retroactively, and everything can be exported on demand.\n\nFeatures:\n- Channel whitelist with a Privacy-First default\n- Per-channel retention with a daily background sweep\n- Retroactive cleanup with preview and Ctrl+Shift confirm\n- Export to Markdown, JSON or CSV\n- First-run wizard with four profiles: Privacy-First, Casual, Roleplay, Full History\n- Multi-language UI (24 locales) with live language switching\n- Own config and database — no shared state with other plugins\n\nBased on Chat 2 by Infi and Anna (EUPL-1.2).\nSupport: https://discord.gg/X9V7Kcv5gR",
|
||||
"ApplicableVersion": "any",
|
||||
"RepoUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat",
|
||||
@@ -20,12 +20,12 @@
|
||||
"CanUnloadAsync": false,
|
||||
"LoadPriority": 0,
|
||||
"Punchline": "A Hellion Forge plugin. Privacy-first chat for FFXIV, built to stay out of your way.",
|
||||
"Changelog": "**v2.0.2 — Emotes out, placeholders fixed (2026-08-19)**\n\n- **BetterTTV emote support is gone.** Its shared-emote endpoint went behind authentication, and that was where nearly all of them came from — what remained was a 65-entry global set, eleven of which are on the known-broken list, so 54 mostly static images from Twitch's early days. Exactly one was animated, and that one wanted 492 frames and 37 MB of video memory. The plugin now makes no outbound network calls at all. Messages stored with emotes still read fine; they show the code that was typed.\n- **Five settings descriptions printed `{0}` instead of the plugin name.** All 25 languages were affected, English included — it just hid better there, since \"Hide {0} during cutscenes\" still scans as a sentence while German puts the placeholder first. A test now walks every resource string with a placeholder and fails if one reaches a widget unformatted.\n- **New preview images.** The ones in the plugin installer were from 8 May and showed the interface as it looked before any of the window rebuilds.",
|
||||
"Changelog": "**v2.0.3 \u2014 Pop-out context menu, screenshot mode (2026-08-20)**\n\n- **The context menu never opened in a pop-out window.** Right-clicking a name or an item there did nothing at all. Every chat surface shares one popup state, and whichever window drew first cleared the menu it had not opened before the pop-out got its turn. The menu now stays with the window the click came from.\n- **Screenshot mode could not be turned off.** It is a saved setting, but neither of its two toggles wrote the change to the config file, so switching it off never survived a reload and the mode came back on at every plugin load. Both toggles save now. If it is stuck on for you, one click off is enough.\n- **Text the game colours itself could render invisible.** Some of those colours arrive without an alpha byte, and the byte order swap then left them fully transparent.\n\n---\n\n**v2.0.2 — Emotes out, placeholders fixed (2026-08-19)**\n\n- **BetterTTV emote support is gone.** Its shared-emote endpoint went behind authentication, and that was where nearly all of them came from — what remained was a 65-entry global set, eleven of which are on the known-broken list, so 54 mostly static images from Twitch's early days. Exactly one was animated, and that one wanted 492 frames and 37 MB of video memory. The plugin now makes no outbound network calls at all. Messages stored with emotes still read fine; they show the code that was typed.\n- **Five settings descriptions printed `{0}` instead of the plugin name.** All 25 languages were affected, English included — it just hid better there, since \"Hide {0} during cutscenes\" still scans as a sentence while German puts the placeholder first. A test now walks every resource string with a placeholder and fails if one reaches a widget unformatted.\n- **New preview images.** The ones in the plugin installer were from 8 May and showed the interface as it looked before any of the window rebuilds.",
|
||||
"AcceptsFeedback": true,
|
||||
"DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.2/latest.zip",
|
||||
"DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.2/latest.zip",
|
||||
"DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.2/latest.zip",
|
||||
"TestingAssemblyVersion": "2.0.2.0",
|
||||
"DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.3/latest.zip",
|
||||
"DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.3/latest.zip",
|
||||
"DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.3/latest.zip",
|
||||
"TestingAssemblyVersion": "2.0.3.0",
|
||||
"IconUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/icon.png",
|
||||
"ImageUrls": [
|
||||
"https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/chatWindow.png",
|
||||
|
||||
Reference in New Issue
Block a user