From a857714064468bd4848fb887061bca7efe1a2157 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 02:49:45 +0200 Subject: [PATCH 01/10] Clarify the privacy filter only governs storage, not the live chat log Audit finding M-5. The master switch description told users what the filter does to the database, but nothing in the UI ruled out the common misreading "if I disable a channel, it will also disappear from the chat log". A help-text line under the toggle now states explicitly that the filter is storage-only and points at the in-game chat tab filters for hiding channels visually. EN and DE strings added together. --- ChatTwo/Resources/HellionStrings.Designer.cs | 1 + ChatTwo/Resources/HellionStrings.de.resx | 3 +++ ChatTwo/Resources/HellionStrings.resx | 3 +++ ChatTwo/Ui/SettingsTabs/Privacy.cs | 2 ++ 4 files changed, 9 insertions(+) diff --git a/ChatTwo/Resources/HellionStrings.Designer.cs b/ChatTwo/Resources/HellionStrings.Designer.cs index 93b6f39..548ce72 100644 --- a/ChatTwo/Resources/HellionStrings.Designer.cs +++ b/ChatTwo/Resources/HellionStrings.Designer.cs @@ -44,6 +44,7 @@ internal class HellionStrings internal static string Privacy_Tab_Title => Get(nameof(Privacy_Tab_Title)); internal static string Privacy_FilterEnabled_Name => Get(nameof(Privacy_FilterEnabled_Name)); internal static string Privacy_FilterEnabled_Description => Get(nameof(Privacy_FilterEnabled_Description)); + internal static string Privacy_FilterEnabled_StorageOnly_Help => Get(nameof(Privacy_FilterEnabled_StorageOnly_Help)); internal static string Privacy_Whitelist_Help => Get(nameof(Privacy_Whitelist_Help)); internal static string Privacy_Preset_PrivacyFirst => Get(nameof(Privacy_Preset_PrivacyFirst)); internal static string Privacy_Preset_ClearAll => Get(nameof(Privacy_Preset_ClearAll)); diff --git a/ChatTwo/Resources/HellionStrings.de.resx b/ChatTwo/Resources/HellionStrings.de.resx index 16f5215..f7253cc 100644 --- a/ChatTwo/Resources/HellionStrings.de.resx +++ b/ChatTwo/Resources/HellionStrings.de.resx @@ -21,6 +21,9 @@ Wenn aktiviert, werden nur Nachrichten aus den erlaubten Kanälen in die Datenbank gespeichert. Beim Deaktivieren gilt wieder das Standard-Verhalten von ChatTwo, also alles außer Battle-Logs wird gespeichert. + + Der Filter steuert nur, was in die lokale Datenbank geschrieben wird. Im Chat-Log siehst du weiterhin jede Nachricht live, ausgeschlossene Kanäle werden nur nicht mehr gespeichert. Wenn du Kanäle auch aus der sichtbaren Anzeige entfernen willst, nutze die normalen Chat-Tab-Filter im Spiel. + Wähle aus, welche Kanäle in die lokale Datenbank gespeichert werden. Standard nach Datensparsamkeit: nur deine eigenen Konversationen. Über die Buttons unten kannst du eine Voreinstellung anwenden. diff --git a/ChatTwo/Resources/HellionStrings.resx b/ChatTwo/Resources/HellionStrings.resx index a3184ea..46f7722 100644 --- a/ChatTwo/Resources/HellionStrings.resx +++ b/ChatTwo/Resources/HellionStrings.resx @@ -21,6 +21,9 @@ When enabled, only messages from whitelisted channels are persisted to the database. Disabling restores upstream ChatTwo behavior (everything except battle messages is stored). + + The filter only controls what is written to the local database. The chat log itself keeps showing every message live — disallowed channels just stop being saved. Use the channel hide options in your in-game chat tabs if you want to remove channels from the visible chat. + Pick which channels are stored in the local database. Privacy-First default: only your own conversations. Use the buttons below to apply a preset. diff --git a/ChatTwo/Ui/SettingsTabs/Privacy.cs b/ChatTwo/Ui/SettingsTabs/Privacy.cs index aa2e226..42523af 100644 --- a/ChatTwo/Ui/SettingsTabs/Privacy.cs +++ b/ChatTwo/Ui/SettingsTabs/Privacy.cs @@ -104,6 +104,8 @@ internal sealed class Privacy : ISettingsTab HellionStrings.Privacy_FilterEnabled_Name, HellionStrings.Privacy_FilterEnabled_Description); + ImGuiUtil.HelpText(HellionStrings.Privacy_FilterEnabled_StorageOnly_Help); + ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); From 2ce30383d9e3ada440ad33235fc118d63fcd3eb9 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 02:50:29 +0200 Subject: [PATCH 02/10] Refuse to write emote cache files outside the cache directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit finding H-1. Defense-in-depth fix for EmoteCache.LoadAsync, which interpolated the BetterTTV-supplied Id and ImageType straight into a Path.Join. HTTPS protects the wire today, but a compromised upstream that hands back Id values like "../foo" would land outside EmoteCacheV1, anywhere under pluginConfigs that the plugin can write. Resolve the candidate path with Path.GetFullPath, then assert it starts with the cache directory plus a directory separator (so "EmoteCacheV1Sibling" cannot match "EmoteCacheV1"). Throw InvalidOperationException on mismatch — the surrounding load already swallows exceptions and logs them, so a tampered entry becomes a visible error in the log instead of a silent miss. --- ChatTwo/EmoteCache.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ChatTwo/EmoteCache.cs b/ChatTwo/EmoteCache.cs index 634927d..52ee38f 100644 --- a/ChatTwo/EmoteCache.cs +++ b/ChatTwo/EmoteCache.cs @@ -168,10 +168,19 @@ public static class EmoteCache internal async Task LoadAsync(Emote emote) { - var dir = Path.Join(Plugin.Interface.ConfigDirectory.FullName, "EmoteCacheV1"); + // BetterTTV-supplied Id and ImageType are interpolated straight + // into the filename. HTTPS protects the wire, but a compromised + // upstream could still hand us "../foo" and write into the + // pluginConfigs root (or worse). Resolve the candidate path and + // refuse anything that escapes the cache directory. + var dir = Path.GetFullPath(Path.Join(Plugin.Interface.ConfigDirectory.FullName, "EmoteCacheV1")); Directory.CreateDirectory(dir); - var filePath = Path.Join(dir, $"{emote.Id}.{emote.ImageType}"); + var dirPrefix = dir.EndsWith(Path.DirectorySeparatorChar) ? dir : dir + Path.DirectorySeparatorChar; + var filePath = Path.GetFullPath(Path.Join(dir, $"{emote.Id}.{emote.ImageType}")); + if (!filePath.StartsWith(dirPrefix, StringComparison.Ordinal)) + throw new InvalidOperationException($"Emote path escapes cache directory: id={emote.Id}, type={emote.ImageType}"); + if (File.Exists(filePath)) { RawData = await File.ReadAllBytesAsync(filePath); From de0d2c80cd9f059fcd090cad59222a1b3026c294 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 02:52:34 +0200 Subject: [PATCH 03/10] Serialise retention sweeps so the auto and manual paths cannot overlap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit findings M-3 and M-4. The 24h auto-sweep launched from Plugin's constructor and the manual button in the Privacy tab were both starting a background thread that called DeleteByRetentionPolicy on the shared MessageStore connection without coordinating. With unfortunate timing — manual click moments after a fresh plugin load — two sweeps would race for the same connection and the second would just re-do work the first one already did, while still overwriting RetentionLastRunAt. Move the running flag and a lock object to Plugin so both paths see the same gate. Each entry point takes the lock long enough to check and set the flag, then runs the actual delete on its background thread without holding the lock (other DB operations already happen without locking; spreading the lock further would suggest a guarantee we do not actually provide). The Privacy tab keeps a read-only property that surfaces the shared flag for its UI disable state — ImGui is single-threaded and bool reads are atomic, so the lock-free read is fine. --- ChatTwo/Plugin.cs | 24 ++++++++++++++++++++++++ ChatTwo/Ui/SettingsTabs/Privacy.cs | 21 ++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index e3c90ea..3c9e441 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -64,6 +64,15 @@ public sealed class Plugin : IDalamudPlugin internal int DeferredSaveFrames = -1; + // Serialises retention sweeps. The 24h auto-sweep on plugin load and + // the manual button in the Privacy tab both run on background threads; + // without this gate, hitting the manual button moments after a fresh + // plugin start would launch two sweeps in parallel and the second one + // would just re-do work the first one already finished. The lock guards + // the flag — the flag check itself bails before we touch the database. + internal readonly object RetentionSweepLock = new(); + internal bool RetentionSweepRunning; + internal DateTime GameStarted { get; } // Tab management needs to happen outside the chatlog window class for access reasons @@ -405,6 +414,16 @@ public sealed class Plugin : IDalamudPlugin new Thread(() => { + // Bail out cheaply if a manual sweep is already in flight; the + // lock around the actual work would queue us up otherwise and + // we would just re-do whatever the manual run already did. + lock (RetentionSweepLock) + { + if (RetentionSweepRunning) + return; + RetentionSweepRunning = true; + } + try { var deleted = MessageManager.Store.DeleteByRetentionPolicy(policy, defaultDays); @@ -429,6 +448,11 @@ public sealed class Plugin : IDalamudPlugin { Log.Error(e, "Retention sweep failed"); } + finally + { + lock (RetentionSweepLock) + RetentionSweepRunning = false; + } }) { IsBackground = true }.Start(); } diff --git a/ChatTwo/Ui/SettingsTabs/Privacy.cs b/ChatTwo/Ui/SettingsTabs/Privacy.cs index 42523af..9db5cfe 100644 --- a/ChatTwo/Ui/SettingsTabs/Privacy.cs +++ b/ChatTwo/Ui/SettingsTabs/Privacy.cs @@ -55,7 +55,10 @@ internal sealed class Privacy : ISettingsTab private long CleanupDeleteCount; private bool CleanupRunning; - private bool RetentionRunning; + // The retention-running state lives on Plugin so the auto-sweep and + // this manual button see the same flag. UI reads stay lock-free + // because ImGui is single-threaded and bool reads are atomic in .NET. + private bool RetentionRunning => Plugin.RetentionSweepRunning; // Export form state private int ExportRangeDays = 30; @@ -410,10 +413,17 @@ internal sealed class Privacy : ISettingsTab private void StartRetentionRun() { - if (RetentionRunning) - return; + // Take the shared retention lock so we cannot fight the auto-sweep + // for the database connection. If the auto-sweep is already in + // flight we just bail — the user can press the button again once + // it finishes. + lock (Plugin.RetentionSweepLock) + { + if (Plugin.RetentionSweepRunning) + return; + Plugin.RetentionSweepRunning = true; + } - RetentionRunning = true; var policy = Plugin.Config.RetentionPerChannelDays.ToDictionary(p => (int)(ushort)p.Key, p => p.Value); var defaultDays = Plugin.Config.RetentionDefaultDays; @@ -445,7 +455,8 @@ internal sealed class Privacy : ISettingsTab } finally { - RetentionRunning = false; + lock (Plugin.RetentionSweepLock) + Plugin.RetentionSweepRunning = false; } }) { IsBackground = true }.Start(); } From 4f25c2756bc004d9ca1fadf67125c11d5034f22f Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 02:56:40 +0200 Subject: [PATCH 04/10] =?UTF-8?q?Tighten=20DbViewer=20paging=20=E2=80=94?= =?UTF-8?q?=20int=20constant=20and=20matching=20SQL=20parameter=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit findings M-1 and M-2. Two small consistency issues in the upstream DbViewer paging path that we now own as a fork: - RowPerPage is a row count and should be an int. The upstream declaration was 1000.0f, which forced an implicit float divide in Math.Ceiling and an implicit float-to-integer conversion when SQLite bound the LIMIT parameter. Switching the constant to int and casting Count to double right at the division keeps the ceiling math intact while making the type story honest. - GetPagedDateRange's SQL uses the placeholder $OffsetCount, but the matching AddWithValue call passed the unprefixed name "OffsetCount". Microsoft.Data.Sqlite tolerates this today, so paging still worked; another provider or a stricter future version would not. Re-aligned the parameter name with the SQL. No behavioural change for users — paging continues to return 1000 rows per page. The fixes are kept on the fork rather than offered upstream because the project's recent triage history makes a non-trivial PR turnaround unlikely. --- ChatTwo/MessageStore.cs | 2 +- ChatTwo/Ui/DbViewer.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChatTwo/MessageStore.cs b/ChatTwo/MessageStore.cs index cd05ad2..3dee96c 100644 --- a/ChatTwo/MessageStore.cs +++ b/ChatTwo/MessageStore.cs @@ -724,7 +724,7 @@ internal class MessageStore : IDisposable cmd.Parameters.AddWithValue("$After", ((DateTimeOffset) after).ToUnixTimeMilliseconds()); cmd.Parameters.AddWithValue("$Before", ((DateTimeOffset) before).ToUnixTimeMilliseconds()); cmd.Parameters.AddWithValue("$Offset", DbViewer.RowPerPage * page); - cmd.Parameters.AddWithValue("OffsetCount", DbViewer.RowPerPage); + cmd.Parameters.AddWithValue("$OffsetCount", DbViewer.RowPerPage); return new MessageEnumerator(cmd.ExecuteReader()); } diff --git a/ChatTwo/Ui/DbViewer.cs b/ChatTwo/Ui/DbViewer.cs index e04992e..2304a66 100644 --- a/ChatTwo/Ui/DbViewer.cs +++ b/ChatTwo/Ui/DbViewer.cs @@ -22,7 +22,7 @@ namespace ChatTwo.Ui; public class DbViewer : Window { - public const float RowPerPage = 1000.0f; + public const int RowPerPage = 1000; private readonly Plugin Plugin; @@ -88,7 +88,7 @@ public class DbViewer : Window public override void Draw() { - var totalPages = (int)Math.Ceiling(Count / RowPerPage); + var totalPages = (int)Math.Ceiling((double)Count / RowPerPage); if (totalPages < 1) totalPages = 1; From 1f2cb000a277e983e5177524f1d8200fa4e71c6c Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 03:08:25 +0200 Subject: [PATCH 05/10] Rename slash commands from /chat2 to /hellion family Hellion Chat is an independent fork with its own assembly name and plugin slot, but it kept registering the upstream /chat2* slash commands. That mixed naming caused two friction points: users could not tell from the in-game help which plugin owned the command, and running both Hellion Chat and the upstream Chat 2 side by side would collide on the registration. Five commands change: /chat2 -> /hellion (settings + chat toggle) /chat2Viewer -> /hellionView (database viewer) /chat2Debugger -> /hellionDebugger (internal, not shown in help) /chat2SeString -> /hellionSeString (internal, debug-only) /clearlog2 -> /clearhellion (clear chat log) Help strings ("Perform various actions with Chat 2.", "Clear the Chat 2 chat log") are reworded to match. ImGui internal window IDs (###chat2-settings, ###chat2-dbviewer) are left untouched on purpose so existing user layouts for those windows do not snap back to default. Resource files do not reference any of these command names, so no localisation work needed. --- ChatTwo/Ui/ChatLogWindow.cs | 8 ++++---- ChatTwo/Ui/DbViewer.cs | 4 ++-- ChatTwo/Ui/Debugger.cs | 4 ++-- ChatTwo/Ui/SeStringDebugger.cs | 4 ++-- ChatTwo/Ui/Settings.cs | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 71e1037..018085c 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -99,8 +99,8 @@ public sealed class ChatLogWindow : Window SetUpTextCommandChannels(); SetUpAllCommands(); - Plugin.Commands.Register("/clearlog2", "Clear the Chat 2 chat log").Execute += ClearLog; - Plugin.Commands.Register("/chat2").Execute += ToggleChat; + Plugin.Commands.Register("/clearhellion", "Clear the Hellion Chat log").Execute += ClearLog; + Plugin.Commands.Register("/hellion").Execute += ToggleChat; Plugin.ClientState.Login += Login; Plugin.ClientState.Logout += Logout; @@ -115,8 +115,8 @@ public sealed class ChatLogWindow : Window Plugin.AddonLifecycle.UnregisterListener(AddonEvent.PostUpdate, "ActionDetail", PayloadHandler.MoveTooltip); Plugin.ClientState.Logout -= Logout; Plugin.ClientState.Login -= Login; - Plugin.Commands.Register("/chat2").Execute -= ToggleChat; - Plugin.Commands.Register("/clearlog2").Execute -= ClearLog; + Plugin.Commands.Register("/hellion").Execute -= ToggleChat; + Plugin.Commands.Register("/clearhellion").Execute -= ClearLog; } private void Logout(int _, int __) diff --git a/ChatTwo/Ui/DbViewer.cs b/ChatTwo/Ui/DbViewer.cs index 2304a66..4a8e512 100644 --- a/ChatTwo/Ui/DbViewer.cs +++ b/ChatTwo/Ui/DbViewer.cs @@ -76,12 +76,12 @@ public class DbViewer : Window RespectCloseHotkey = false; DisableWindowSounds = true; - Plugin.Commands.Register("/chat2Viewer", "Get access to your message history, with simple filter options.", true).Execute += Toggle; + Plugin.Commands.Register("/hellionView", "Get access to your message history, with simple filter options.", true).Execute += Toggle; } public void Dispose() { - Plugin.Commands.Register("/chat2Viewer", "Get access to your message history, with simple filter options.", true).Execute -= Toggle; + Plugin.Commands.Register("/hellionView", "Get access to your message history, with simple filter options.", true).Execute -= Toggle; } private void Toggle(string _, string __) => Toggle(); diff --git a/ChatTwo/Ui/Debugger.cs b/ChatTwo/Ui/Debugger.cs index d3e93f2..fdbb68e 100644 --- a/ChatTwo/Ui/Debugger.cs +++ b/ChatTwo/Ui/Debugger.cs @@ -28,12 +28,12 @@ public class DebuggerWindow : Window RespectCloseHotkey = false; DisableWindowSounds = true; - Plugin.Commands.Register("/chat2Debugger", showInHelp: false).Execute += Toggle; + Plugin.Commands.Register("/hellionDebugger", showInHelp: false).Execute += Toggle; } public void Dispose() { - Plugin.Commands.Register("/chat2Debugger", showInHelp: false).Execute -= Toggle; + Plugin.Commands.Register("/hellionDebugger", showInHelp: false).Execute -= Toggle; } private void Toggle(string _, string __) => Toggle(); diff --git a/ChatTwo/Ui/SeStringDebugger.cs b/ChatTwo/Ui/SeStringDebugger.cs index 167b3b9..a4d7465 100644 --- a/ChatTwo/Ui/SeStringDebugger.cs +++ b/ChatTwo/Ui/SeStringDebugger.cs @@ -30,14 +30,14 @@ public class SeStringDebugger : Window DisableWindowSounds = true; #if DEBUG - Plugin.Commands.Register("/chat2SeString", showInHelp: false).Execute += Toggle; + Plugin.Commands.Register("/hellionSeString", showInHelp: false).Execute += Toggle; #endif } public void Dispose() { #if DEBUG - Plugin.Commands.Register("/chat2SeString", showInHelp: false).Execute -= Toggle; + Plugin.Commands.Register("/hellionSeString", showInHelp: false).Execute -= Toggle; #endif } diff --git a/ChatTwo/Ui/Settings.cs b/ChatTwo/Ui/Settings.cs index c4fa8bc..b35e545 100755 --- a/ChatTwo/Ui/Settings.cs +++ b/ChatTwo/Ui/Settings.cs @@ -52,14 +52,14 @@ public sealed class SettingsWindow : Window Initialise(); - Plugin.Commands.Register("/chat2", "Perform various actions with Chat 2.").Execute += Command; + Plugin.Commands.Register("/hellion", "Perform various actions with Hellion Chat.").Execute += Command; Plugin.Interface.UiBuilder.OpenConfigUi += Toggle; } public void Dispose() { Plugin.Interface.UiBuilder.OpenConfigUi -= Toggle; - Plugin.Commands.Register("/chat2").Execute -= Command; + Plugin.Commands.Register("/hellion").Execute -= Command; } private void Command(string command, string args) From 8e964ca498bb6ae0ee633acc384f62d444bce980 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 03:25:29 +0200 Subject: [PATCH 06/10] Align HellionStyle with the Hellion Online Media brand palette MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plugin theme drifted from the website palette over time: cyan sat at #00B8D4 instead of the brand #00BED2, the warm highlights were industrial amber rather than Ember Orange, and active tabs and title bars were rendered in slate violet — a shade not part of the brand at all. This commit moves every HellionStyle slot onto the Arctic Cyan + Ember Glow tokens documented in the website's BRANDING.md: - Primary cyan slots (Button, CheckMark, Slider, Separator) now use brand-color / brand-color-light / brand-color-dark - Window title bars and the active tab use brand-color-dark as Identity teal — slightly varied so it reads as identity rather than as another action surface - Unfocused-active tabs drop to a deeper teal so the unfocused window's tab is still visible without pulling focus - Resize grips and scrollbar grabs lift into Ember Orange on hover and active states, replacing industrial amber - Window, child, popup, frame and header surfaces follow the brand background ladder (#070B12, #0C1220, #141E30, #1A2538, #22303F) - Borders use the brand cyan at 40% alpha (matches --border-brand on the website) instead of neutral steel grey The slate violet tertiary palette is gone. Brand tokens are declared once and the slot constants alias them, so a future brand shift only needs to touch the Identity / Accent / Primary stages. --- ChatTwo/Ui/HellionStyle.cs | 110 +++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 47 deletions(-) diff --git a/ChatTwo/Ui/HellionStyle.cs b/ChatTwo/Ui/HellionStyle.cs index 28c9a36..d5b8281 100644 --- a/ChatTwo/Ui/HellionStyle.cs +++ b/ChatTwo/Ui/HellionStyle.cs @@ -21,63 +21,79 @@ internal static class HellionStyle { // Encoded as 0xRRGGBBAA, matching ChatTwo convention (see Settings.cs // Ko-fi buttons). RgbaToAbgr handles the byte swap to the format ImGui - // expects. + // expects. Hex values are sourced from the Hellion Online Media brand + // guide ("Arctic Cyan + Ember Glow", BRANDING.md in the website repo). - // Primary — cyan-teal for actionable controls (buttons, checks, sliders). - private const uint PrimaryRgba = 0x00B8D4FF; - private const uint PrimaryHoverRgba = 0x26C6DAFF; - private const uint PrimaryActiveRgba = 0x00838FFF; + // Primary — Arctic Cyan, used for every interactive control (buttons, + // checks, sliders, separators when hovered). Three brand stages plus a + // hover that lifts to brand-color-light and a press that drops to + // brand-color-dark. + private const uint PrimaryRgba = 0x00BED2FF; // brand-color + private const uint PrimaryHoverRgba = 0x4DD9E8FF; // brand-color-light + private const uint PrimaryActiveRgba = 0x0097A7FF; // brand-color-dark - // Secondary — industrial amber, used as a warm highlight for active - // states (tab borders, resize grips, scrollbar grabs). - private const uint SecondaryRgba = 0xFFB300FF; - private const uint SecondaryHoverRgba = 0xFFC940FF; - private const uint SecondaryActiveRgba = 0xC68400FF; + // Identity — brand-color-dark teal for window title bars and the + // active tab. Sits visibly below the primary cyan on buttons so the + // user sees "where am I" (deep teal) versus "what can I click" + // (brand cyan) without leaving the cyan family. + private const uint IdentityRgba = 0x0097A7FF; // brand-color-dark + private const uint IdentityHoverRgba = 0x4DD9E8FF; // brand-color-light + private const uint IdentityDeepRgba = 0x005670FF; // dimmer teal for unfocused-active tab - // Tertiary — slate violet, reserved for title bars and the active tab - // background so identity beats out the cyan accent without competing - // with it on action controls. - private const uint TertiaryRgba = 0x7B61FFFF; - private const uint TertiaryHoverRgba = 0x9580FFFF; - private const uint TertiaryActiveRgba = 0x5E45D9FF; + // Accent — Ember Orange for warm highlights on grips and scrollbar + // pulls. Replaces the previous industrial amber so the plugin matches + // the website's CTA palette. AccentActive is reserved for any future + // pressed-state on accent surfaces; the current slots only need + // AccentRgba and AccentHoverRgba. + private const uint AccentRgba = 0xF97316FF; // accent-color + private const uint AccentHoverRgba = 0xFB923CFF; // accent-color-light - // Surfaces — deep slate window/frame backgrounds, steel borders. - private const uint WindowBgRgba = 0x0E1A20FF; - private const uint ChildBgRgba = 0x102027FF; - private const uint PopupBgRgba = 0x102027FF; - private const uint FrameBgRgba = 0x162831FF; - private const uint FrameBgHoverRgba = 0x1F3540FF; - private const uint FrameBgActiveRgba = 0x274250FF; - private const uint BorderRgba = 0x37474FFF; + // Surfaces — Hellion brand background ladder. Window darkest, frame + // hover ladder climbs into surface tones. Matches the website's + // background / background-medium / background-light / surface vars. + private const uint WindowBgRgba = 0x070B12FF; // background + private const uint ChildBgRgba = 0x0C1220FF; // background-medium + private const uint PopupBgRgba = 0x0C1220FF; // background-medium + private const uint FrameBgRgba = 0x141E30FF; // background-light + private const uint FrameBgHoverRgba = 0x1A2538FF; // surface + private const uint FrameBgActiveRgba = 0x22303FFF; // surface-hover + // Cyan-tinted border — matches website --border-brand (cyan @ 40% α). + private const uint BorderRgba = 0x00BED266; private const uint BorderShadowRgba = 0x00000000; - // Headers / collapsing-headers / tree nodes / selectables. - private const uint HeaderRgba = 0x1B2C36FF; - private const uint HeaderHoverRgba = 0x263A45FF; - private const uint HeaderActiveRgba = 0x324A57FF; + // Headers / collapsing-headers / tree nodes / selectables — same + // surface ladder as frames so panels feel consistent. + private const uint HeaderRgba = 0x141E30FF; + private const uint HeaderHoverRgba = 0x1A2538FF; + private const uint HeaderActiveRgba = 0x22303FFF; - // Title bars — tertiary identity for the active state. - private const uint TitleBgRgba = 0x0E1A20FF; - private const uint TitleBgActiveRgba = 0x5E45D9FF; - private const uint TitleBgCollapsedRgba = 0x0A1318FF; + // Title bars — Identity teal on active so the focused window reads + // as "yours" without using accent or primary slots. + private const uint TitleBgRgba = 0x070B12FF; + private const uint TitleBgActiveRgba = IdentityRgba; + private const uint TitleBgCollapsedRgba = 0x05080EFF; - // Tabs — tertiary tint, secondary highlight while hovered/unfocused. - private const uint TabRgba = 0x162831FF; - private const uint TabHoveredRgba = 0x9580FFFF; - private const uint TabActiveRgba = 0x7B61FFFF; - private const uint TabUnfocusedRgba = 0x12222AFF; - private const uint TabUnfocusedActiveRgba = 0x5E45D9FF; + // Tabs — neutral inactive, Identity-light on hover, Identity teal on + // active. Unfocused-active uses the deeper Identity stage so an + // unfocused window's active tab still reads but does not pull focus. + private const uint TabRgba = 0x141E30FF; + private const uint TabHoveredRgba = IdentityHoverRgba; + private const uint TabActiveRgba = IdentityRgba; + private const uint TabUnfocusedRgba = 0x0C1220FF; + private const uint TabUnfocusedActiveRgba = IdentityDeepRgba; - // Scrollbar — slate base, secondary amber on grab. - private const uint ScrollbarBgRgba = 0x0E1A20FF; - private const uint ScrollbarGrabRgba = 0x37474FFF; - private const uint ScrollbarGrabHoveredRgba = 0xFFC940FF; - private const uint ScrollbarGrabActiveRgba = 0xFFB300FF; + // Scrollbar — Ember on grab so the pull stands out without competing + // with the cyan action buttons. Idle grab is a subtle surface tone, + // hover/active climb into accent. + private const uint ScrollbarBgRgba = 0x070B12FF; + private const uint ScrollbarGrabRgba = 0x22303FFF; // surface-hover + private const uint ScrollbarGrabHoveredRgba = AccentHoverRgba; + private const uint ScrollbarGrabActiveRgba = AccentRgba; - // Resize grip — secondary amber for the active corner pull. - private const uint ResizeGripRgba = 0x37474FFF; - private const uint ResizeGripHoveredRgba = 0xFFC940FF; - private const uint ResizeGripActiveRgba = 0xFFB300FF; + // Resize grip — same Ember treatment as the scrollbar. + private const uint ResizeGripRgba = 0x141E30FF; + private const uint ResizeGripHoveredRgba = AccentHoverRgba; + private const uint ResizeGripActiveRgba = AccentRgba; // Separator and check mark / slider follow the primary cyan. From 462530dec5f653dd4b5fb5b9af254818dba441ae Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 03:40:57 +0200 Subject: [PATCH 07/10] Add a mission statement to the About tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The About tab already credits Chat 2 and the maintainers, but it never said why this fork exists in the first place. New users discovering Hellion Chat through the Dalamud plugin list could reasonably read it as a replacement attempt rather than what it is: a niche alternative for users who care about chat persistence and data minimisation. The new "Why this fork exists" block sits between the Maintainer and the Built-on-Chat-2 sections so the reading order goes from "who" to "why" to "from what". It states three things plainly: - Hellion Chat is not trying to replace Chat 2 - The trigger was the maintainer's own database (two years, two million messages, mostly public-chat from strangers) - Source is open under the same EUPL-1.2 licence; the upstream authors are welcome to look, take ideas, ask, or ignore The tone matches the rest of the About tab — direct, no marketing voice — and stays in English with the other legal-ish copy so a single source covers every locale. --- ChatTwo/Ui/SettingsTabs/About.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChatTwo/Ui/SettingsTabs/About.cs b/ChatTwo/Ui/SettingsTabs/About.cs index 8d78f0a..0b44421 100755 --- a/ChatTwo/Ui/SettingsTabs/About.cs +++ b/ChatTwo/Ui/SettingsTabs/About.cs @@ -74,6 +74,13 @@ internal sealed class About : ISettingsTab ImGuiHelpers.ScaledDummy(10.0f); + ImGui.TextColored(ImGuiColors.ParsedGold, "Why this fork exists"); + ImGui.TextUnformatted("Hellion Chat is not meant to replace Chat 2 — it is a niche alternative for users who want a privacy-by-default chat persistence layer aligned with EU, US and JP data protection rules."); + ImGui.TextUnformatted("The trigger was a personal one: a two-year Chat 2 database with over two million logged messages, the bulk of them /say, /shout and /yell from strangers in cities. Most plugin users do not mind that data sitting on their disk. I do, so this fork exists."); + ImGui.TextUnformatted("No big audience expected, no competition with the upstream plugin intended. The source is open under the same EUPL-1.2 licence as Chat 2 — Infi, Anna and anyone else are welcome to look at how this fork solves things, take ideas, ask questions or ignore it entirely. All three responses are fine."); + + ImGuiHelpers.ScaledDummy(10.0f); + ImGui.TextColored(ImGuiColors.ParsedGold, "Built on Chat 2"); ImGui.TextUnformatted("Hellion Chat is a fork of Chat 2 by Infi and Anna (ascclemens)."); ImGui.TextUnformatted("Every chat replacement feature, the IPC integration, the rendering engine and the storage core come from upstream Chat 2."); From 59332ce9ea725fcdc2f6f6a7e63b7d323e708f26 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 03:50:08 +0200 Subject: [PATCH 08/10] Move About-tab copy to HellionStrings and rephrase neutrally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The About tab copy was hand-written in English directly in About.cs, which left the German users on the upstream Chat 2 wording for the Hellion-specific blocks. The copy itself also leaned on em-dashes mid-sentence and on a tone that could read as accusing Chat 2 of GDPR violations, which was never the intent. This commit moves the six About-tab sections (Maintainer, Why this fork exists, Built on Chat 2, License, FFXIV disclaimer, Localization) into HellionStrings and tightens the wording in both languages. Tone change is the substantive part. Chat 2's full-history default is now described as "the right one for most users" rather than a problem the fork is fixing, and the webinterface removal is framed as a focus mismatch — Chat 2's webinterface targets remote chat access from a second device, this fork targets a smaller default footprint, neither approach is wrong. The personal trigger for the fork (two million logged messages over two years, mostly /say and /yell from strangers) stays as it is honest context rather than criticism. The same neutralised wording is mirrored in three more places that described the webinterface removal: the README "Was gegenüber Chat 2 fehlt" block, the HellionChat.yaml description and changelog, and the matching repo.json fields. The 0.2.0 changelog no longer recites the upstream auth-flow internals; "different use case, substantial rebuild, removed" is enough for users. Em-dashes were also removed from two body strings that previously used them as comma replacements (Privacy filter storage-only help and the retention default description). Heading-level dashes ("Hellion Chat — Welcome", "Export (GDPR Art. 15 — right of access)") stay because dashes are appropriate as separators in titles. The Theme description was already inaccurate — it still talked about slate-violet tabs and amber highlights even though the brand sweep moved everything onto Arctic Cyan plus Ember Orange. Updated to describe the current palette honestly. --- ChatTwo/HellionChat.yaml | 27 +++---- ChatTwo/Resources/HellionStrings.Designer.cs | 23 ++++++ ChatTwo/Resources/HellionStrings.de.resx | 74 ++++++++++++++++++- ChatTwo/Resources/HellionStrings.resx | 78 +++++++++++++++++++- ChatTwo/Ui/SettingsTabs/About.cs | 55 +++++++------- README.md | 2 +- repo.json | 4 +- 7 files changed, 212 insertions(+), 51 deletions(-) diff --git a/ChatTwo/HellionChat.yaml b/ChatTwo/HellionChat.yaml index b6efb1e..40e6d4f 100755 --- a/ChatTwo/HellionChat.yaml +++ b/ChatTwo/HellionChat.yaml @@ -3,12 +3,12 @@ author: JonKazama-Hellion punchline: Chat 2 with privacy controls aligned to EU, US and JP rules description: |- Hellion Chat is built on top of Chat 2 with one removal and a stack - of privacy controls on top. The /chat2 command, tabs, channel - filters, RGB colours, emotes, screenshot mode, IPC integration and - the chat replacement window itself work the same. The optional - webinterface that Chat 2 ships is intentionally not part of this - fork because it could not be hardened to the privacy guarantees - Hellion Chat makes by default. + of privacy controls on top. Tabs, channel filters, RGB colours, + emotes, screenshot mode, IPC integration and the chat replacement + window itself work the same. The optional webinterface that Chat 2 + ships is intentionally not part of this fork because it serves a + different use case from the smaller default footprint Hellion Chat + is built around. On top of that, Hellion Chat adds privacy and data-handling controls designed to align with the modern data protection rules that apply @@ -42,15 +42,12 @@ tags: changelog: |- **Hellion Chat 0.2.0 — Webinterface removed** - Following an internal security and consistency audit the upstream - webinterface has been removed in its entirety. Hardening it to the - privacy guarantees Hellion Chat makes by default would have meant - rewriting the auth flow (the upstream code uses a five-digit - numeric code from System.Random), changing the default bind address - (currently every interface), reworking cookie handling and adding - the privacy filter to the live message stream that the webinterface - was broadcasting around it. The cumulative cost did not match the - niche use case for a fork that wants less network surface, not more. + The upstream webinterface has been removed in its entirety. It + serves a different use case from the smaller default footprint + this fork is built around, namely remote access to chat from a + second device. Aligning it with the data minimisation defaults + Hellion Chat ships with would have meant a substantial rebuild. + Removing it was the cleaner path for this particular fork. What changed in this release: diff --git a/ChatTwo/Resources/HellionStrings.Designer.cs b/ChatTwo/Resources/HellionStrings.Designer.cs index 548ce72..02e9289 100644 --- a/ChatTwo/Resources/HellionStrings.Designer.cs +++ b/ChatTwo/Resources/HellionStrings.Designer.cs @@ -141,4 +141,27 @@ internal class HellionStrings internal static string Theme_WindowOpacity_Help => Get(nameof(Theme_WindowOpacity_Help)); internal static string Theme_UseHellionFont_Name => Get(nameof(Theme_UseHellionFont_Name)); internal static string Theme_UseHellionFont_Description => Get(nameof(Theme_UseHellionFont_Description)); + + internal static string About_Maintainer_Heading => Get(nameof(About_Maintainer_Heading)); + internal static string About_Maintainer_Body => Get(nameof(About_Maintainer_Body)); + internal static string About_Maintainer_Website_Label => Get(nameof(About_Maintainer_Website_Label)); + internal static string About_Mission_Heading => Get(nameof(About_Mission_Heading)); + internal static string About_Mission_P1 => Get(nameof(About_Mission_P1)); + internal static string About_Mission_P2 => Get(nameof(About_Mission_P2)); + internal static string About_Mission_P3 => Get(nameof(About_Mission_P3)); + internal static string About_BuiltOn_Heading => Get(nameof(About_BuiltOn_Heading)); + internal static string About_BuiltOn_P1 => Get(nameof(About_BuiltOn_P1)); + internal static string About_BuiltOn_P2 => Get(nameof(About_BuiltOn_P2)); + internal static string About_BuiltOn_Upstream_Label => Get(nameof(About_BuiltOn_Upstream_Label)); + internal static string About_License_Heading => Get(nameof(About_License_Heading)); + internal static string About_License_P1 => Get(nameof(About_License_P1)); + internal static string About_License_P2 => Get(nameof(About_License_P2)); + internal static string About_License_P3 => Get(nameof(About_License_P3)); + internal static string About_SE_Heading => Get(nameof(About_SE_Heading)); + internal static string About_SE_P1 => Get(nameof(About_SE_P1)); + internal static string About_SE_P2 => Get(nameof(About_SE_P2)); + internal static string About_Localization_Heading => Get(nameof(About_Localization_Heading)); + internal static string About_Localization_P1 => Get(nameof(About_Localization_P1)); + internal static string About_Localization_P2 => Get(nameof(About_Localization_P2)); + internal static string About_Translators_TreeNode => Get(nameof(About_Translators_TreeNode)); } diff --git a/ChatTwo/Resources/HellionStrings.de.resx b/ChatTwo/Resources/HellionStrings.de.resx index f7253cc..d13339b 100644 --- a/ChatTwo/Resources/HellionStrings.de.resx +++ b/ChatTwo/Resources/HellionStrings.de.resx @@ -280,7 +280,7 @@ Hellion-Theme für alle Plugin-Fenster verwenden - Industrielle HUD-Palette mit cyan-blauen Aktionsfarben, schiefer-violetten Tabs und Bernstein-Akzenten für aktive Zustände, global angewendet auf Chat-Fenster, Einstellungen, Viewer und Wizard. Deaktivieren, um das Standard-Dalamud-Erscheinungsbild zu nutzen. + Hellion-Online-Media-Palette aus Arctic Cyan und Ember Orange, angewendet auf Chat-Fenster, Einstellungen, Viewer und Wizard. Deaktivieren, um das Standard-Dalamud-Erscheinungsbild zu nutzen. Fenster-Deckkraft @@ -294,4 +294,76 @@ Rendert Chat und UI in Exo 2 (SIL Open Font License 1.1), die mit dem Plugin ausgeliefert wird. Deaktivieren, um auf die unter Einstellungen → Schrift gewählte Schriftart zurückzufallen. + + + Maintainer + + + Ich pflege Hellion Chat über Hellion Online Media. Auf der Website findest du die Kontaktdaten für lizenzrechtliche, rechtliche oder geschäftliche Fragen. + + + Website: + + + + Warum es diesen Fork gibt + + + Hellion Chat soll Chat 2 nicht ersetzen. Chat 2 liefert ein vollständiges Chat-Erlebnis mit kompletter Historie, die für Filter, Suche und Replay zur Verfügung steht. Dieser Default ist für die meisten Nutzer der richtige. Dieser Fork wählt einen anderen Ansatz: einen kleineren Default-Footprint, mit zusätzlichen Stellschrauben für Nutzer, die weniger fremden Chat auf der Festplatte behalten möchten. + + + Der Wunsch nach diesem engeren Default war persönlich. Nach zwei Jahren mit Chat 2 lag meine Datenbank bei über zwei Millionen Nachrichten, der Großteil davon /say, /shout und /yell von Fremden in Limsa. Genau diese Daten machen Chat 2's Voll-Historie nützlich, und die meisten Nutzer behalten sie gerne. Mein eigener Geschmack wollte einen kleineren Default. Also habe ich diesen Fork gebaut. + + + Ich strebe keine große Zielgruppe an, und der Fork steht nicht in Konkurrenz zu Chat 2. Der Code liegt offen unter derselben EUPL-1.2-Lizenz wie das Original. Infi, Anna oder sonst jemand dürfen reinschauen, Ideen mitnehmen, Fragen stellen oder das Projekt einfach ignorieren. Alles drei ist für mich in Ordnung. + + + + Aufbauend auf Chat 2 + + + Hellion Chat ist ein Fork von Chat 2 von Infi und Anna (ascclemens). Das Chat-Replacement-Fenster, die IPC-Integration, die Render-Engine und der komplette Storage-Kern stammen aus dem Original. + + + Das Webinterface ist das einzige größere Teil, das ich entfernt habe. Es ist für den Remote-Zugriff auf den Chat von einem zweiten Gerät gebaut, also für einen anderen Fokus als der kleinere Default-Footprint, den dieser Fork verfolgt. Es an diese Defaults anzupassen hätte einen erheblichen Umbau bedeutet, also war die Entfernung der saubere Weg für genau diesen Fork. + + + Upstream-Repository: + + + + Lizenz + + + Hellion Chat und Chat 2 stehen beide unter der European Union Public Licence v1.2 (EUPL-1.2). + + + © 2023 bis 2026, die Chat-2-Autoren (Infi, Anna und die Upstream-Mitwirkenden). + + + © 2026 Hellion Online Media für die Erweiterungen in diesem Fork. + + + + FINAL FANTASY XIV-Hinweis + + + FINAL FANTASY XIV © SQUARE ENIX CO., LTD. Alle Rechte vorbehalten. + + + Hellion Chat ist ein inoffizielles Fan-Plugin. Es steht in keiner Verbindung zu Square Enix und wird von ihnen weder unterstützt, gesponsert noch genehmigt. + + + + Lokalisierung + + + Die deutschen Übersetzungen der Hellion-spezifischen Strings stammen von mir. Weitere Sprachen sind aktuell nicht verfügbar. + + + Die Übersetzerliste weiter unten gehört zu den Chat-2-Strings auf Crowdin. Diese Freiwilligen haben Chat 2 übersetzt, nicht die Hellion-Erweiterungen. + + + Chat-2-Community-Übersetzer (Upstream) + diff --git a/ChatTwo/Resources/HellionStrings.resx b/ChatTwo/Resources/HellionStrings.resx index 46f7722..52c1d43 100644 --- a/ChatTwo/Resources/HellionStrings.resx +++ b/ChatTwo/Resources/HellionStrings.resx @@ -22,7 +22,7 @@ When enabled, only messages from whitelisted channels are persisted to the database. Disabling restores upstream ChatTwo behavior (everything except battle messages is stored). - The filter only controls what is written to the local database. The chat log itself keeps showing every message live — disallowed channels just stop being saved. Use the channel hide options in your in-game chat tabs if you want to remove channels from the visible chat. + The filter only controls what is written to the local database. The chat log itself keeps showing every message live, disallowed channels just stop being saved. Use the channel hide options in your in-game chat tabs if you want to remove channels from the visible chat. Pick which channels are stored in the local database. Privacy-First default: only your own conversations. Use the buttons below to apply a preset. @@ -124,7 +124,7 @@ Auto-delete messages after a per-channel retention window - When enabled, messages older than the configured window are deleted on every plugin start (at most once per 24 hours). Off by default — the plugin never deletes history without your explicit consent. + When enabled, messages older than the configured window are deleted on every plugin start (at most once per 24 hours). Off by default. The plugin never deletes history without your explicit consent. Default retention (days, 0 = never) @@ -280,7 +280,7 @@ Use the Hellion theme across all plugin windows - Industrial HUD palette with cyan-teal action accents, slate-violet tabs and amber active highlights, applied globally to chat log, settings, viewers and the wizard. Disable to fall back to the default Dalamud look. + Hellion Online Media palette of Arctic Cyan plus Ember Orange, applied across the chat log, settings, viewers and the wizard. Disable to fall back to the default Dalamud look. Window opacity @@ -294,4 +294,76 @@ Renders chat and UI in Exo 2 (SIL Open Font License 1.1) which ships with the plugin. Disable to fall back to whatever font you picked under Settings → Fonts. + + + Maintainer + + + I maintain Hellion Chat through Hellion Online Media. The website has the contact details for licensing, legal or business questions. + + + Website: + + + + Why this fork exists + + + Hellion Chat is not trying to replace Chat 2. Chat 2 ships a complete chat experience with full history available for filtering, search and replay. That default is the right one for most users. This fork takes a different stance: a smaller default footprint, with extra knobs for users who want to keep less third-party chat on disk. + + + The reason I wanted that narrower default was personal. After two years on Chat 2 my database had grown past two million messages, most of them /say, /shout and /yell from strangers in Limsa. That data is exactly what makes Chat 2's full-history view powerful and most users are happy to keep it. For my own taste I wanted a smaller default. So I built this fork. + + + I am not chasing a big audience and the fork is not in competition with Chat 2. The code is open under the same EUPL-1.2 licence as the upstream plugin. Infi, Anna or anyone else are welcome to read it, borrow ideas, ask questions, or ignore the project. All three are fine by me. + + + + Built on Chat 2 + + + Hellion Chat is a fork of Chat 2 by Infi and Anna (ascclemens). The chat replacement window, the IPC integration, the rendering engine and the entire storage core come from upstream Chat 2. + + + The webinterface is the only major piece I removed. It is built for remote access to chat from a second device, which is a different focus than the smaller default footprint this fork is built around. Aligning it with these defaults would have meant a substantial rebuild, so removing it was the cleaner path for this particular fork. + + + Upstream repository: + + + + License + + + Hellion Chat and Chat 2 both ship under the European Union Public Licence v1.2 (EUPL-1.2). + + + © 2023 to 2026, the Chat 2 authors (Infi, Anna and the upstream contributors). + + + © 2026 Hellion Online Media for the additions made in this fork. + + + + FINAL FANTASY XIV disclaimer + + + FINAL FANTASY XIV © SQUARE ENIX CO., LTD. All rights reserved. + + + Hellion Chat is an unofficial, fan-made plugin. It has no affiliation with Square Enix and is not endorsed, sponsored or approved by them. + + + + Localization + + + The German translations of the Hellion-specific strings come from me. Other languages are not provided yet. + + + The translator list below covers the upstream Chat 2 strings on Crowdin. Those volunteers translated Chat 2, not the Hellion additions. + + + Chat 2 community translators (upstream) + diff --git a/ChatTwo/Ui/SettingsTabs/About.cs b/ChatTwo/Ui/SettingsTabs/About.cs index 0b44421..43043db 100755 --- a/ChatTwo/Ui/SettingsTabs/About.cs +++ b/ChatTwo/Ui/SettingsTabs/About.cs @@ -60,55 +60,52 @@ internal sealed class About : ISettingsTab ImGuiHelpers.ScaledDummy(10.0f); - // Hellion-specific maintainer / attribution / license / SE- - // disclaimer block. Hand-rolled in English here rather than via - // HellionStrings — the legal-ish copy stays close to the EUPL-1.2 - // wording and the SE disclaimer is the same in every locale. - ImGui.TextColored(ImGuiColors.ParsedGold, "Maintainer"); - ImGui.TextUnformatted("Hellion Chat is maintained by Hellion Online Media (Florian Wathling)."); - ImGui.TextUnformatted("Website:"); + ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_Maintainer_Heading); + ImGui.TextUnformatted(HellionStrings.About_Maintainer_Body); + ImGui.TextUnformatted(HellionStrings.About_Maintainer_Website_Label); ImGui.SameLine(); if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "hellionMedia")) Dalamud.Utility.Util.OpenLink("https://hellion-media.de"); - ImGui.TextUnformatted("For licensing, legal or contact inquiries please reach out via the website above."); ImGuiHelpers.ScaledDummy(10.0f); - ImGui.TextColored(ImGuiColors.ParsedGold, "Why this fork exists"); - ImGui.TextUnformatted("Hellion Chat is not meant to replace Chat 2 — it is a niche alternative for users who want a privacy-by-default chat persistence layer aligned with EU, US and JP data protection rules."); - ImGui.TextUnformatted("The trigger was a personal one: a two-year Chat 2 database with over two million logged messages, the bulk of them /say, /shout and /yell from strangers in cities. Most plugin users do not mind that data sitting on their disk. I do, so this fork exists."); - ImGui.TextUnformatted("No big audience expected, no competition with the upstream plugin intended. The source is open under the same EUPL-1.2 licence as Chat 2 — Infi, Anna and anyone else are welcome to look at how this fork solves things, take ideas, ask questions or ignore it entirely. All three responses are fine."); + ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_Mission_Heading); + ImGui.TextUnformatted(HellionStrings.About_Mission_P1); + ImGui.Spacing(); + ImGui.TextUnformatted(HellionStrings.About_Mission_P2); + ImGui.Spacing(); + ImGui.TextUnformatted(HellionStrings.About_Mission_P3); ImGuiHelpers.ScaledDummy(10.0f); - ImGui.TextColored(ImGuiColors.ParsedGold, "Built on Chat 2"); - ImGui.TextUnformatted("Hellion Chat is a fork of Chat 2 by Infi and Anna (ascclemens)."); - ImGui.TextUnformatted("Every chat replacement feature, the IPC integration, the rendering engine and the storage core come from upstream Chat 2."); - ImGui.TextUnformatted("The upstream webinterface is intentionally not part of Hellion Chat — it could not be hardened to the privacy guarantees this fork makes by default."); - ImGui.TextUnformatted("Upstream repository:"); + ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_BuiltOn_Heading); + ImGui.TextUnformatted(HellionStrings.About_BuiltOn_P1); + ImGui.Spacing(); + ImGui.TextUnformatted(HellionStrings.About_BuiltOn_P2); + ImGui.Spacing(); + ImGui.TextUnformatted(HellionStrings.About_BuiltOn_Upstream_Label); ImGui.SameLine(); if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "chatTwoUpstream")) Dalamud.Utility.Util.OpenLink("https://github.com/Infiziert90/ChatTwo"); ImGuiHelpers.ScaledDummy(10.0f); - ImGui.TextColored(ImGuiColors.ParsedGold, "License"); - ImGui.TextUnformatted("Hellion Chat and Chat 2 are licensed under the European Union Public License v1.2 (EUPL-1.2)."); - ImGui.TextUnformatted("© 2023–2026 the Chat 2 authors (Infi, Anna and the upstream contributors)."); - ImGui.TextUnformatted("© 2026 Hellion Online Media — for the Hellion Chat additions."); + ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_License_Heading); + ImGui.TextUnformatted(HellionStrings.About_License_P1); + ImGui.TextUnformatted(HellionStrings.About_License_P2); + ImGui.TextUnformatted(HellionStrings.About_License_P3); ImGuiHelpers.ScaledDummy(10.0f); - ImGui.TextColored(ImGuiColors.DalamudOrange, "FINAL FANTASY XIV disclaimer"); - ImGui.TextUnformatted("FINAL FANTASY XIV © SQUARE ENIX CO., LTD. All rights reserved."); - ImGui.TextUnformatted("Hellion Chat is an unofficial, fan-made plugin and is not affiliated with, endorsed, sponsored or approved by Square Enix."); + ImGui.TextColored(ImGuiColors.DalamudOrange, HellionStrings.About_SE_Heading); + ImGui.TextUnformatted(HellionStrings.About_SE_P1); + ImGui.TextUnformatted(HellionStrings.About_SE_P2); ImGui.Spacing(); - ImGui.TextColored(ImGuiColors.ParsedGold, "Localization"); - ImGui.TextUnformatted("German translations of Hellion-specific UI strings (HellionStrings.de.resx) are written by the Hellion Online Media maintainer."); - ImGui.TextUnformatted("All other locales for Hellion-specific strings are not currently provided."); - ImGui.TextUnformatted("The translator list below covers the upstream Chat 2 community translators on Crowdin — their work covers the inherited Chat 2 strings, not the Hellion additions."); + ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_Localization_Heading); + ImGui.TextUnformatted(HellionStrings.About_Localization_P1); + ImGui.TextUnformatted(HellionStrings.About_Localization_P2); ImGui.Spacing(); @@ -117,7 +114,7 @@ internal sealed class About : ISettingsTab { if (aboutChild) { - using var treeNode = ImRaii.TreeNode("Chat 2 community translators (upstream)"); + using var treeNode = ImRaii.TreeNode(HellionStrings.About_Translators_TreeNode); if (treeNode) { using var translatorChild = ImRaii.Child("translators"); diff --git a/README.md b/README.md index 57fd286..1da86c4 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Privates Repository, EUPL-1.2-lizenziert. Distribution über Custom-Repo währen ### Was gegenüber Chat 2 fehlt -- **Webinterface** wurde in Hellion Chat 0.2.0 entfernt. Der eingebaute HTTP-Server hat unter dem Privacy-Versprechen nicht abgesichert werden können (5-stelliger numerischer Auth-Code aus `System.Random`, Bind auf alle Interfaces per Default, Cookies ohne Security-Flags und ein Server-Sent-Events-Stream der den Privacy-Filter umgangen hat). Wer den Funktionsumfang von Chat 2 vollständig braucht, sollte beim Upstream-Plugin bleiben; Hellion Chat fokussiert auf DSGVO-konforme Persistenz und verzichtet bewusst auf Remote-Zugriffs-Features. +- **Webinterface** wurde in Hellion Chat 0.2.0 entfernt. Es bedient einen anderen Anwendungsfall als der Fokus dieses Forks, nämlich Remote-Zugriff auf den Chat von einem zweiten Gerät. An die kleineren Defaults dieses Forks anzupassen hätte einen erheblichen Umbau bedeutet, also ist es ersatzlos entfernt worden. Wer den vollen Funktionsumfang von Chat 2 möchte, ist mit dem Upstream-Plugin besser bedient. Hellion Chat fokussiert sich auf einen schmaleren Datenbestand und verzichtet bewusst auf Remote-Zugriffs-Features. --- diff --git a/repo.json b/repo.json index d0a53af..8147520 100644 --- a/repo.json +++ b/repo.json @@ -4,7 +4,7 @@ "Name": "Hellion Chat", "InternalName": "HellionChat", "AssemblyVersion": "0.2.0.0", - "Description": "Hellion Chat is built on top of Chat 2 with one removal and a stack\nof privacy controls on top. The /chat2 command, tabs, channel\nfilters, RGB colours, emotes, screenshot mode, IPC integration and\nthe chat replacement window itself work the same. The optional\nwebinterface that Chat 2 ships is intentionally not part of this\nfork because it could not be hardened to the privacy guarantees\nHellion Chat makes by default.\n\nOn top of that, Hellion Chat adds privacy and data-handling controls\ndesigned to align with the modern data protection rules that apply\nacross the EU, the United States and Japan. By default only your own\nconversations are stored; messages from strangers, NPCs and system\nspam stay out of the database. Retention windows are configurable per\nchannel, history can be wiped retroactively, and stored data can be\nexported on demand.\n\nKey additions on top of Chat 2:\n\n- Channel whitelist with a Privacy-First default\n- Per-channel retention with a daily background sweep\n- Retroactive cleanup with a Ctrl+Shift confirm\n- Export to Markdown, JSON or CSV\n- First-run wizard with three preset profiles (Privacy-First, Casual,\n Full History)\n- Bilingual UI (English and German) with live language switching\n- Independent plugin state — own config file and database directory,\n so Hellion Chat does not share state with the upstream plugin\n\nBased on Chat 2 by Infi and Anna, licensed under EUPL-1.2.", + "Description": "Hellion Chat is built on top of Chat 2 with one removal and a stack\nof privacy controls on top. Tabs, channel filters, RGB colours,\nemotes, screenshot mode, IPC integration and the chat replacement\nwindow itself work the same. The optional webinterface that Chat 2\nships is intentionally not part of this fork because it serves a\ndifferent use case from the smaller default footprint Hellion Chat\nis built around.\n\nOn top of that, Hellion Chat adds privacy and data-handling controls\ndesigned to align with the modern data protection rules that apply\nacross the EU, the United States and Japan. By default only your own\nconversations are stored; messages from strangers, NPCs and system\nspam stay out of the database. Retention windows are configurable per\nchannel, history can be wiped retroactively, and stored data can be\nexported on demand.\n\nKey additions on top of Chat 2:\n\n- Channel whitelist with a Privacy-First default\n- Per-channel retention with a daily background sweep\n- Retroactive cleanup with a Ctrl+Shift confirm\n- Export to Markdown, JSON or CSV\n- First-run wizard with three preset profiles (Privacy-First, Casual,\n Full History)\n- Bilingual UI (English and German) with live language switching\n- Independent plugin state — own config file and database directory,\n so Hellion Chat does not share state with the upstream plugin\n\nBased on Chat 2 by Infi and Anna, licensed under EUPL-1.2.", "ApplicableVersion": "any", "RepoUrl": "https://github.com/JonKazama-Hellion/HellionChat", "Tags": [ @@ -20,7 +20,7 @@ "CanUnloadAsync": false, "LoadPriority": 0, "Punchline": "Chat 2 with privacy controls aligned to EU, US and JP rules", - "Changelog": "**Hellion Chat 0.2.0 — Webinterface removed**\n\nFollowing an internal security and consistency audit the upstream\nwebinterface has been removed in its entirety. Hardening it to the\nprivacy guarantees Hellion Chat makes by default would have meant\nrewriting the auth flow (the upstream code uses a five-digit\nnumeric code from System.Random), changing the default bind address\n(currently every interface), reworking cookie handling and adding\nthe privacy filter to the live message stream that the webinterface\nwas broadcasting around it. The cumulative cost did not match the\nniche use case for a fork that wants less network surface, not more.\n\nWhat changed in this release:\n\n- Settings tab \"Webinterface\" is gone, the corresponding\n Configuration fields (WebinterfaceEnabled / AutoStart / Password /\n Port / AuthStore / MaxLinesToSend) are dropped and stale entries\n fall out of the JSON on the next save automatically\n- The whole ChatTwo/Http tree, the bundled Svelte frontend in\n websiteBuild.zip and the WebinterfaceUtil helper are deleted\n- Watson.Lite (the HTTP server) and Newtonsoft.Json (only used by\n the webinterface JSON wire format) are removed from the\n package references\n- DbViewer's \"Chat2 JSON Export\" button is dropped because it\n serialised the database into the webinterface message protocol;\n the Privacy tab's MessageExporter (Markdown, JSON, CSV with\n channel and date filters) covers the same ground without the\n proprietary shape\n- About tab notes the absence so users coming from Chat 2 do not\n look for it\n- Configuration version bumps from 7 to 8 with a one-shot\n notification (EN + DE)\n\nNo changes to the privacy filter, retention sweep, first-run wizard\nor export pipeline. Existing chat history is preserved.\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n**Hellion Chat 0.1.2 — About tab rebrand, DBViewer polish**\n\n- About tab now shows Hellion-specific maintainer, license, EU/US/JP\n disclaimer and SQUARE ENIX disclaimer instead of the inherited\n Chat 2 contact info; original ChatTwo translator credits stay\n visible under a clearly labelled upstream tree node\n- Localization clarified: Hellion-specific German strings are\n maintained by the fork maintainer, the Crowdin contributor list\n only covers the inherited upstream strings\n- Cherry-picked DBViewer UI improvements from upstream Chat 2\n (auto-scroll-reset on page change, tooltips on date reset,\n folder export, page arrows, localized export-running messages)\n- README rewritten in the Hellion project style with a tech-stack\n table, architecture tree, database column list, install guide,\n upstream-sync workflow notes and project-status checklist\n\n**Hellion Chat 0.1.1 — Packaging and migration fixes**\n\n- Plugin icon now ships inside the bundle, so the Hellion logo\n renders locally in the Dalamud plugin list once installed (the\n previous release relied only on the remote IconUrl)\n- Plugin icon downsampled from 1024×1024 to 256×256 to match the\n rendered size; loads faster and caches better\n- Migration from upstream Chat 2 is more robust: each file move is\n wrapped individually, a locked SQLite database no longer aborts\n the rest of the migration, and a warning notification fires when\n any file is held open (with a hint to disable Chat 2 and restart\n the game)\n- README ships a step-by-step migration guide (fresh install versus\n coming from Chat 2) and a troubleshooting section with manual\n recovery commands for Linux and Windows\n\n**Hellion Chat 0.1.0 — Initial fork release**\n\nPrivacy\n- Channel whitelist filter in MessageStore.UpsertMessage with a\n Privacy-First default (own conversations only)\n- Per-channel retention with a 24-hour idempotent background sweep\n- Retroactive cleanup with a Ctrl+Shift confirm and VACUUM\n- Export to Markdown / JSON / CSV via Dalamud's file dialog\n\nOnboarding\n- First-run wizard with three profiles: Privacy-First / Casual /\n Full History\n- Configuration migration that seeds defaults on update\n- One-shot migration from upstream Chat 2's pluginConfigs layout\n- Migrate3 idempotency recovery for half-migrated databases\n\nLook & feel\n- Localized UI (English and German) with live language switching\n- Industrial HUD theme with cyan-teal action accents, slate-violet\n tabs, amber active highlights and a window-opacity slider\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).", + "Changelog": "**Hellion Chat 0.2.0 — Webinterface removed**\n\nThe upstream webinterface has been removed in its entirety. It\nserves a different use case from the smaller default footprint\nthis fork is built around, namely remote access to chat from a\nsecond device. Aligning it with the data minimisation defaults\nHellion Chat ships with would have meant a substantial rebuild.\nRemoving it was the cleaner path for this particular fork.\n\nWhat changed in this release:\n\n- Settings tab \"Webinterface\" is gone, the corresponding\n Configuration fields (WebinterfaceEnabled / AutoStart / Password /\n Port / AuthStore / MaxLinesToSend) are dropped and stale entries\n fall out of the JSON on the next save automatically\n- The whole ChatTwo/Http tree, the bundled Svelte frontend in\n websiteBuild.zip and the WebinterfaceUtil helper are deleted\n- Watson.Lite (the HTTP server) and Newtonsoft.Json (only used by\n the webinterface JSON wire format) are removed from the\n package references\n- DbViewer's \"Chat2 JSON Export\" button is dropped because it\n serialised the database into the webinterface message protocol;\n the Privacy tab's MessageExporter (Markdown, JSON, CSV with\n channel and date filters) covers the same ground without the\n proprietary shape\n- About tab notes the absence so users coming from Chat 2 do not\n look for it\n- Configuration version bumps from 7 to 8 with a one-shot\n notification (EN + DE)\n\nNo changes to the privacy filter, retention sweep, first-run wizard\nor export pipeline. Existing chat history is preserved.\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n**Hellion Chat 0.1.2 — About tab rebrand, DBViewer polish**\n\n- About tab now shows Hellion-specific maintainer, license, EU/US/JP\n disclaimer and SQUARE ENIX disclaimer instead of the inherited\n Chat 2 contact info; original ChatTwo translator credits stay\n visible under a clearly labelled upstream tree node\n- Localization clarified: Hellion-specific German strings are\n maintained by the fork maintainer, the Crowdin contributor list\n only covers the inherited upstream strings\n- Cherry-picked DBViewer UI improvements from upstream Chat 2\n (auto-scroll-reset on page change, tooltips on date reset,\n folder export, page arrows, localized export-running messages)\n- README rewritten in the Hellion project style with a tech-stack\n table, architecture tree, database column list, install guide,\n upstream-sync workflow notes and project-status checklist\n\n**Hellion Chat 0.1.1 — Packaging and migration fixes**\n\n- Plugin icon now ships inside the bundle, so the Hellion logo\n renders locally in the Dalamud plugin list once installed (the\n previous release relied only on the remote IconUrl)\n- Plugin icon downsampled from 1024×1024 to 256×256 to match the\n rendered size; loads faster and caches better\n- Migration from upstream Chat 2 is more robust: each file move is\n wrapped individually, a locked SQLite database no longer aborts\n the rest of the migration, and a warning notification fires when\n any file is held open (with a hint to disable Chat 2 and restart\n the game)\n- README ships a step-by-step migration guide (fresh install versus\n coming from Chat 2) and a troubleshooting section with manual\n recovery commands for Linux and Windows\n\n**Hellion Chat 0.1.0 — Initial fork release**\n\nPrivacy\n- Channel whitelist filter in MessageStore.UpsertMessage with a\n Privacy-First default (own conversations only)\n- Per-channel retention with a 24-hour idempotent background sweep\n- Retroactive cleanup with a Ctrl+Shift confirm and VACUUM\n- Export to Markdown / JSON / CSV via Dalamud's file dialog\n\nOnboarding\n- First-run wizard with three profiles: Privacy-First / Casual /\n Full History\n- Configuration migration that seeds defaults on update\n- One-shot migration from upstream Chat 2's pluginConfigs layout\n- Migrate3 idempotency recovery for half-migrated databases\n\nLook & feel\n- Localized UI (English and German) with live language switching\n- Industrial HUD theme with cyan-teal action accents, slate-violet\n tabs, amber active highlights and a window-opacity slider\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).", "AcceptsFeedback": true, "DownloadLinkInstall": "https://github.com/JonKazama-Hellion/HellionChat/releases/download/v0.2.0/latest.zip", "DownloadLinkUpdate": "https://github.com/JonKazama-Hellion/HellionChat/releases/download/v0.2.0/latest.zip", From 6b44310e04c86129e9a7fdcda61098c15d19cf74 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 03:54:34 +0200 Subject: [PATCH 09/10] Fix the About tab so the translator list is reachable The translator section sat inside a child window whose height was computed as "whatever is left in the content region minus a line". Once the About copy grew with the new mission and rewritten built-on sections, that remaining space dropped close to zero on smaller settings windows, so the tree node was rendered but its content was either invisible or unscrollable from the parent. Drop the fixed-height child entirely. The settings window already provides a scroll container around each tab, so rendering the tree node and the translator list directly into it lets the parent handle the scroll. The translators get a manual indent push to keep the visual nesting that the child-frame used to suggest. --- ChatTwo/Ui/SettingsTabs/About.cs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/ChatTwo/Ui/SettingsTabs/About.cs b/ChatTwo/Ui/SettingsTabs/About.cs index 43043db..fde0a6d 100755 --- a/ChatTwo/Ui/SettingsTabs/About.cs +++ b/ChatTwo/Ui/SettingsTabs/About.cs @@ -109,21 +109,18 @@ internal sealed class About : ISettingsTab ImGui.Spacing(); - var height = ImGui.GetContentRegionAvail().Y - ImGui.CalcTextSize("A").Y - ImGui.GetStyle().ItemSpacing.Y * 2; - using (var aboutChild = ImRaii.Child("about", new Vector2(-1, height))) + // The translator list lives at the bottom of the About tab. Render + // it directly inside the parent scroll container instead of a + // fixed-height child — the previous "remaining space" calculation + // shrank to zero (or below) once the About copy grew, which made + // the section unreachable on smaller settings windows. + using (var treeNode = ImRaii.TreeNode(HellionStrings.About_Translators_TreeNode)) { - if (aboutChild) + if (treeNode) { - using var treeNode = ImRaii.TreeNode(HellionStrings.About_Translators_TreeNode); - if (treeNode) - { - using var translatorChild = ImRaii.Child("translators"); - if (translatorChild) - { - foreach (var translator in Translators) - ImGui.TextUnformatted(translator); - } - } + using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false); + foreach (var translator in Translators) + ImGui.TextUnformatted(translator); } } ImGui.Spacing(); From 9c86619c9f32c27b75dd5578ccbb72b2f85fb3f2 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Sat, 2 May 2026 03:59:25 +0200 Subject: [PATCH 10/10] Bump to 0.3.0 with the audit, brand and command-rename changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2 of the audit follow-ups, the Hellion Online Media brand sweep and the rename of the slash commands all land in one release. The slash command rename is breaking for users who had macros bound to /chat2, /chat2Viewer or /clearlog2, which is the main reason this is a 0.3.0 rather than a 0.2.1. csproj, plugin manifest yaml, custom-repo repo.json (assembly version, testing assembly version and the three download links) and the README version banner are all moved over together so the Dalamud plugin list, the manifest and the install instructions agree. The README project status checklist is updated to reflect that Phase 2 is closed; Phase 3 holds the remaining backlog (MySQL backend, encryption, libnotify, etc). The yaml and repo.json changelogs gain a 0.3.0 block that walks through the four substantial groups of changes (slash command rename, audit hardening, brand sweep, About tab) in plain prose. The 0.2.0 block stays underneath in chronological order. Build (Release) verified — ChatTwo/bin/Release/HellionChat/latest.zip (~17.5 MB) and HellionChat.json regenerate cleanly with no warnings. The tag itself is created by hand alongside the GitHub release. --- ChatTwo/ChatTwo.csproj | 2 +- ChatTwo/HellionChat.yaml | 61 ++++++++++++++++++++++++++++++++++++++++ README.md | 12 ++++---- repo.json | 12 ++++---- 4 files changed, 75 insertions(+), 12 deletions(-) diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj index 5df366a..11ac682 100755 --- a/ChatTwo/ChatTwo.csproj +++ b/ChatTwo/ChatTwo.csproj @@ -4,7 +4,7 @@ 0.1.0 is our bootstrap release; the underlying Chat 2 base is called out in the yaml changelog so users can see what it derives from. --> - 0.2.0 + 0.3.0 enable