diff --git a/AI_DISCLOSURE.md b/AI_DISCLOSURE.md
index 8aa662e..c37ba10 100644
--- a/AI_DISCLOSURE.md
+++ b/AI_DISCLOSURE.md
@@ -1,18 +1,49 @@
# AI assistance disclosure
-Per the [Dalamud Plugin AI Usage Policy](https://github.com/goatcorp/DalamudPluginsD17/),
-this fork uses AI assistance at the **Pair** level. Pair means the maintainer
-plans the architecture, decides what gets built, reviews each change and
-tests against the running game; Claude (Anthropic) helps explain Dalamud
-APIs, suggests patterns, drafts code on request, and reviews approaches.
-Neither side acts autonomously: nothing ships without the maintainer's
-review, and Claude can't run the game.
+This fork uses AI assistance per the [Dalamud Plugin AI Usage Policy](https://github.com/goatcorp/DalamudPluginsD17/)
+at the **Pair** level.
-The level varies by area and over time. Some commits are mostly hand-written
-with the AI used as a sounding board, others lean more on Claude for an API
-walkthrough or a code draft that the maintainer then reads, edits and
-integrates. The maintainer's commitment is to be able to explain why every
-piece of Hellion code is the way it is — not "I typed every character."
+A note up front: Hellion Chat is currently in a rebuild and adjustment
+phase, and there are no plans to submit it to the Dalamud team for review
+while it stays standalone. If the plugin stays out of the official repo I
+technically wouldn't need to disclose any of this, but I'd rather be
+upfront about how it's built.
+
+Hellion Chat is my entry point into game modding and plugin development. I
+have never written a plugin for a game before. I work alone, so I get help
+where I need it. That's not something I want to hide.
+
+## How I actually work
+
+I plan the architecture, decide what gets built, and own every design
+decision. For each change I:
+
+- Read the code Claude drafts before I integrate it
+- Test with my own tooling and in the running game
+- Read the Dalamud log output to verify behaviour
+- Run security and privacy audits on anything that touches user data
+
+One of the main reasons I use AI is consistency. I want the Hellion code to
+match the style of the upstream Chat 2 codebase and stay readable for
+anyone who opens the repo, not just for me. Claude helps me catch when I'm
+drifting from upstream conventions or writing something that only makes
+sense in my own head.
+
+The balance is shifting toward more hand-written work as I get more
+comfortable with Dalamud and plugin development in general.
+
+## What AI is used for
+
+- API explanations (Dalamud, ImGui, .NET specifics I haven't worked with before)
+- Code drafts that I read, edit, and integrate
+- Pattern suggestions and code review
+- Keeping the style aligned with the upstream Chat 2 codebase
+
+## What AI isn't used for
+
+- **Visual assets.** Logos, icons, banners, and screenshots are human-drawn
+ or taken from the running game.
+- **German translations.** Written by me as a native speaker.
## What's where
@@ -22,20 +53,24 @@ produced with AI assistance. Hellion-specific code lives in
`Ui/SettingsTabs/Privacy.cs`, `Ui/FirstRunWizard.cs`, `Ui/HellionStyle.cs`,
plus the Migrate3 recovery and plugin layout migration in `MessageStore.cs`
and `Plugin.cs`. These were developed with Pair-level assistance as
-described above; the share of human vs. AI authorship varies file by file
-and is expected to keep shifting toward more hand-written work as the
-maintainer's plugin-dev experience grows.
+described above.
-## What AI is not used for
+## If AI-assisted development is a dealbreaker for you
-- **Visual assets.** Logos, icons, banners, screenshots are human-drawn or
- taken from the running game.
-- **German translations.** Written by the maintainer (native speaker).
+Fair enough. There are solid alternatives that don't rely on AI in their
+development:
+
+- [Chat 2](https://github.com/Infiziert90/ChatTwo), the original upstream
+ this fork is based on
+- [XIV Instant Messenger](https://github.com/NightmareXIV/XIVInstantMessenger),
+ a different approach to chat in FFXIV
+
+Both are good projects. Use what fits you best.
## Tooling
-- Claude (Anthropic) via Claude Code CLI as the main pair partner.
-- Context7 / Microsoft Learn for current Dalamud and .NET documentation.
+- Claude (Anthropic) via Claude Code CLI
+- Context7 / Microsoft Learn for current Dalamud and .NET documentation
## Contact
diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj
index d1d9dce..4a6c618 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.4.0
+ 0.5.0
enable
-
- Auto-Tell-Tabs
-
-
- Auto-Tell-Tabs sind ab Version 0.4.0 standardmäßig aktiv. Du kannst sie im Chat-Tab deaktivieren oder anpassen.
-
Aktive Tells
@@ -438,4 +423,127 @@
Greift nur, wenn Auto-Tell-Tabs im Chat-Tab aktiviert sind.
+
+
+
+ Settings umstrukturiert
+
+
+ Hellion Chat 0.5.0 hat die Settings in thematische Tabs umstrukturiert. Deine Chat-Datenbank und dein Nachrichtenverlauf bleiben unverändert. Settings wurden auf Defaults zurückgesetzt. Falls du das Privacy-Profil neu wählen willst, findest du den Reopen-Button im Datenschutz-Tab. Ein Backup der vorherigen Config liegt unter HellionChat.json.pre-v10-backup neben der aktiven Config-Datei.
+
+
+
+
+ Allgemein
+
+
+ Aussehen
+
+
+ Fenster
+
+
+ Chat
+
+
+ Über
+
+
+
+
+ Eingabe
+
+
+ Audio & Benachrichtigungen
+
+
+ Performance
+
+
+ Sprache & Eingabe-Hilfen
+
+
+
+
+ Theme
+
+
+ Schriftarten
+
+
+ Chat-Farben
+
+
+ Zeitstempel
+
+
+
+
+ Verstecken
+
+
+ Inaktivitäts-Verstecken
+
+
+ Fenster-Rahmen
+
+
+ Tooltips
+
+
+
+
+ Auto-Tell-Tabs
+
+
+ Nachrichten-Verhalten
+
+
+ Vorschau
+
+
+ Emotes
+
+
+
+
+ Speicherung
+
+
+ Übersicht
+
+
+ Wartung
+
+
+
+
+ Versionsinfo
+
+
+ Über HellionChat
+
+
+ Changelog
+
+
+
+
+ System
+
+
+ Free Company
+
+
+ Gruppe
+
+
+ Neulinge
+
+
+ Linkshell
+
+
+ Wenn du mehrere Linkshells benutzt, empfiehlt der Maintainer einen Tab pro Shell für eine sauberere Übersicht. Tab duplizieren und je Kopie die Kanalauswahl einschränken.
+
diff --git a/ChatTwo/Resources/HellionStrings.resx b/ChatTwo/Resources/HellionStrings.resx
index 01cb688..519f010 100644
--- a/ChatTwo/Resources/HellionStrings.resx
+++ b/ChatTwo/Resources/HellionStrings.resx
@@ -24,6 +24,9 @@
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.
+
+ Privacy filter and whitelist
+
Pick which channels are stored in the local database. Privacy-First default: only your own conversations. Use the buttons below to apply a preset.
@@ -75,6 +78,9 @@
Cleanup uses your SAVED whitelist (Plugin.Config), not unsaved edits above. Click Save first if you want to apply your current edits.
+
+ The manual sweep uses your SAVED retention policy, not the slider values above. Click Save first if you want the run to apply your current edits.
+
Refresh preview
@@ -174,18 +180,6 @@
Retention sweep failed, see /xllog
-
- Hellion Chat
-
-
- Privacy filter activated by default. Settings → Privacy to adjust.
-
-
- Hellion Chat 0.2.0
-
-
- The webinterface has been removed in this version because it could not be hardened to the privacy guarantees Hellion Chat makes by default. If you used it, please consult the README for context.
-
Hellion Chat — Welcome
@@ -273,9 +267,6 @@
Export failed, see /xllog
-
- Appearance
-
Use the Hellion theme across all plugin windows
@@ -368,12 +359,6 @@
-
- Auto-Tell-Tabs
-
-
- Auto-Tell-Tabs are enabled by default starting with version 0.4.0. You can disable or fine-tune them in the Chat tab.
-
Active Tells
@@ -438,4 +423,127 @@
Only takes effect when auto tell tabs are enabled in the Chat tab.
+
+
+
+ Settings reorganised
+
+
+ Hellion Chat 0.5.0 reorganised the settings into themed tabs. Your chat database and your message history stay untouched. Settings have been reset to defaults; if you want to pick a privacy profile again, the reopen button is in the Privacy tab. A backup of your previous config is at HellionChat.json.pre-v10-backup next to the live config file.
+
+
+
+
+ General
+
+
+ Appearance
+
+
+ Window
+
+
+ Chat
+
+
+ Information
+
+
+
+
+ Input
+
+
+ Audio & Notifications
+
+
+ Performance
+
+
+ Language & Input Helpers
+
+
+
+
+ Theme
+
+
+ Fonts
+
+
+ Chat Colours
+
+
+ Timestamps
+
+
+
+
+ Hide
+
+
+ Inactivity Hide
+
+
+ Window Frame
+
+
+ Tooltips
+
+
+
+
+ Auto-Tell-Tabs
+
+
+ Message Behaviour
+
+
+ Preview
+
+
+ Emotes
+
+
+
+
+ Storage
+
+
+ Overview
+
+
+ Maintenance
+
+
+
+
+ Version Info
+
+
+ About HellionChat
+
+
+ Changelog
+
+
+
+
+ System
+
+
+ Free Company
+
+
+ Party
+
+
+ Beginner
+
+
+ Linkshell
+
+
+ If you use multiple linkshells, the maintainer recommends one tab per shell for cleaner readability. Duplicate this tab and narrow the channel selection per copy.
+
diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs
index 0ba15f6..1e60ba8 100644
--- a/ChatTwo/Ui/ChatLogWindow.cs
+++ b/ChatTwo/Ui/ChatLogWindow.cs
@@ -451,7 +451,9 @@ public sealed class ChatLogWindow : Window
Flags |= ImGuiWindowFlags.NoTitleBar;
if (LastViewport == ImGuiHelpers.MainViewport.Handle && !WasDocked)
- BgAlpha = Plugin.Config.WindowAlpha / 100f;
+ BgAlpha = Plugin.Config.HellionThemeEnabled
+ ? Plugin.Config.HellionThemeWindowOpacity
+ : Plugin.Config.WindowAlpha / 100f;
LastViewport = ImGui.GetWindowViewport().Handle;
WasDocked = ImGui.IsWindowDocked();
@@ -1188,7 +1190,13 @@ public sealed class ChatLogWindow : Window
if (tab.DisplayTimestamp)
{
var localTime = message.Date.ToLocalTime();
- var timestamp = localTime.ToString("t", !Plugin.Config.Use24HourClock ? null : CultureInfo.CreateSpecificCulture("de-DE"));
+ // Force the format explicitly per setting. Relying on the
+ // current culture meant a German system locale always
+ // produced 24h regardless of the toggle, so the checkbox
+ // looked dead.
+ var timestamp = Plugin.Config.Use24HourClock
+ ? localTime.ToString("HH:mm", CultureInfo.InvariantCulture)
+ : localTime.ToString("h:mm tt", CultureInfo.InvariantCulture);
if (isTable)
{
if (!Plugin.Config.HideSameTimestamps || timestamp != lastTimestamp)
diff --git a/ChatTwo/Ui/Popout.cs b/ChatTwo/Ui/Popout.cs
index 8871d54..c3f85ab 100644
--- a/ChatTwo/Ui/Popout.cs
+++ b/ChatTwo/Ui/Popout.cs
@@ -70,8 +70,16 @@ internal class Popout : Window
if (!ChatLogWindow.PopOutDocked[Idx])
{
- var alpha = Tab.IndependentOpacity ? Tab.Opacity : Plugin.Config.WindowAlpha;
- BgAlpha = alpha / 100f;
+ if (Tab.IndependentOpacity)
+ {
+ BgAlpha = Tab.Opacity / 100f;
+ }
+ else
+ {
+ BgAlpha = Plugin.Config.HellionThemeEnabled
+ ? Plugin.Config.HellionThemeWindowOpacity
+ : Plugin.Config.WindowAlpha / 100f;
+ }
}
}
diff --git a/ChatTwo/Ui/Settings.cs b/ChatTwo/Ui/Settings.cs
index b35e545..cd7ddc7 100755
--- a/ChatTwo/Ui/Settings.cs
+++ b/ChatTwo/Ui/Settings.cs
@@ -9,7 +9,7 @@ using Dalamud.Bindings.ImGui;
namespace ChatTwo.Ui;
-public sealed class SettingsWindow : Window
+public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
{
private readonly Plugin Plugin;
@@ -33,18 +33,14 @@ public sealed class SettingsWindow : Window
Tabs =
[
- new Display(Mutable),
- new ChatLog(Plugin, Mutable),
- new Emote(Plugin, Mutable),
- new Preview(Mutable),
- new Fonts(Mutable),
- new ChatColours(Plugin, Mutable),
- new Tabs(Plugin, Mutable),
+ new General(Plugin, Mutable),
+ new Appearance(Plugin, Mutable),
+ new SettingsTabs.Window(Plugin, Mutable),
+ new Chat(Plugin, Mutable),
+ new SettingsTabs.Tabs(Plugin, Mutable),
new SettingsTabs.Privacy(Plugin, Mutable),
new Database(Plugin, Mutable),
- new Miscellaneous(Mutable),
- new Changelog(Mutable),
- new About()
+ new Information(Mutable),
];
RespectCloseHotkey = false;
@@ -114,14 +110,16 @@ public sealed class SettingsWindow : Window
ImGui.SameLine();
- if (ImGui.Button(Language.Settings_SaveAndClose)) {
+ if (ImGui.Button(Language.Settings_SaveAndClose))
+ {
save = true;
IsOpen = false;
}
ImGui.SameLine();
- if (ImGui.Button(Language.Settings_Discard)) {
+ if (ImGui.Button(Language.Settings_Discard))
+ {
IsOpen = false;
}
@@ -135,7 +133,7 @@ public sealed class SettingsWindow : Window
{
var buttonWidth = ImGui.CalcTextSize(buttonLabel).X + ImGui.GetStyle().FramePadding.X * 2;
var buttonWidth2 = ImGui.CalcTextSize(buttonLabel2).X + ImGui.GetStyle().FramePadding.X * 2;
- ImGui.SameLine(ImGui.GetContentRegionAvail().X - buttonWidth - buttonWidth2);
+ ImGui.SameLine(ImGui.GetContentRegionAvail().X - buttonWidth - buttonWidth2 - ImGui.GetStyle().ItemSpacing.X);
if (ImGui.Button(buttonLabel2))
Dalamud.Utility.Util.OpenLink("https://ko-fi.com/infiii");
diff --git a/ChatTwo/Ui/SettingsTabs/About.cs b/ChatTwo/Ui/SettingsTabs/About.cs
deleted file mode 100755
index fde0a6d..0000000
--- a/ChatTwo/Ui/SettingsTabs/About.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-using System.Numerics;
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud.Interface;
-using Dalamud.Interface.Colors;
-using Dalamud.Interface.Utility;
-using Dalamud.Interface.Utility.Raii;
-using Dalamud.Bindings.ImGui;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-internal sealed class About : ISettingsTab
-{
- public string Name => string.Format(Language.Options_About_Tab, Plugin.PluginName) + "###tabs-about";
-
- private readonly List Translators =
- [
- "q673135110", "Akizem", "d0tiKs",
- "Moonlight_Everlit", "Dark32", "andreycout",
- "Button_", "Cali666", "cassandra308",
- "lokinmodar", "jtabox", "AkiraYorumoto",
- "MKhayle", "elena.space", "imlisa",
- "andrei5125", "ShivaMaheshvara", "aislinn87",
- "nishinatsu051", "lichuyuan", "Risu64",
- "yummypillow", "witchymary", "Yuzumi",
- "zomsakura", "Sirayuki"
- ];
-
- internal About()
- {
- Translators.Sort((a, b) => string.Compare(a.ToLowerInvariant(), b.ToLowerInvariant(), StringComparison.Ordinal));
- }
-
- public void Draw(bool changed)
- {
- using var wrap = ImRaii.TextWrapPos(0.0f);
-
- ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName));
-
- ImGuiHelpers.ScaledDummy(10.0f);
-
- ImGui.TextUnformatted(Language.Options_About_Authors);
- ImGui.SameLine();
- ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Interface.Manifest.Author);
-
- ImGui.TextUnformatted(Language.Options_About_Discord);
- ImGui.SameLine();
- ImGui.TextColored(ImGuiColors.ParsedGold, "@j.j_kazama");
-
- ImGui.TextUnformatted(Language.Options_About_Version);
- ImGui.SameLine();
- ImGui.TextColored(ImGuiColors.ParsedOrange, Plugin.Interface.Manifest.AssemblyVersion.ToString(3));
-
- ImGuiHelpers.ScaledDummy(10.0f);
-
- ImGui.TextUnformatted(Language.Options_About_Github_Issues);
- ImGui.SameLine();
- if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "githubIssues"))
- Dalamud.Utility.Util.OpenLink("https://github.com/JonKazama-Hellion/HellionChat/issues");
-
- ImGuiHelpers.ScaledDummy(10.0f);
-
- 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");
-
- ImGuiHelpers.ScaledDummy(10.0f);
-
- 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, 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, 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, HellionStrings.About_SE_Heading);
- ImGui.TextUnformatted(HellionStrings.About_SE_P1);
- ImGui.TextUnformatted(HellionStrings.About_SE_P2);
-
- ImGui.Spacing();
-
- ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_Localization_Heading);
- ImGui.TextUnformatted(HellionStrings.About_Localization_P1);
- ImGui.TextUnformatted(HellionStrings.About_Localization_P2);
-
- ImGui.Spacing();
-
- // 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 (treeNode)
- {
- using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
- foreach (var translator in Translators)
- ImGui.TextUnformatted(translator);
- }
- }
- ImGui.Spacing();
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/Appearance.cs b/ChatTwo/Ui/SettingsTabs/Appearance.cs
new file mode 100644
index 0000000..e15e10b
--- /dev/null
+++ b/ChatTwo/Ui/SettingsTabs/Appearance.cs
@@ -0,0 +1,286 @@
+using ChatTwo.Code;
+using ChatTwo.Resources;
+using ChatTwo.Util;
+using Dalamud;
+using Dalamud.Interface;
+using Dalamud.Interface.FontIdentifier;
+using Dalamud.Interface.Style;
+using Dalamud.Interface.Utility;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Bindings.ImGui;
+
+namespace ChatTwo.Ui.SettingsTabs;
+
+internal sealed class Appearance : ISettingsTab
+{
+ private Plugin Plugin { get; }
+ private Configuration Mutable { get; }
+
+ public string Name => HellionStrings.Settings_Tab_Appearance + "###tabs-appearance";
+
+ internal Appearance(Plugin plugin, Configuration mutable)
+ {
+ Plugin = plugin;
+ Mutable = mutable;
+ }
+
+ public void Draw(bool changed)
+ {
+ DrawThemeSection();
+ ImGui.Spacing();
+ DrawFontsSection();
+ ImGui.Spacing();
+ DrawColoursSection();
+ ImGui.Spacing();
+ DrawTimestampsSection();
+ }
+
+ private void DrawThemeSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Appearance_Theme_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(HellionStrings.Theme_Enabled_Name, ref Mutable.HellionThemeEnabled);
+ ImGuiUtil.HelpMarker(HellionStrings.Theme_Enabled_Description);
+
+ // Clamp 0.5–1.0 stays consistent with Privacy.cs which already
+ // shipped this slider; lower values would let chat windows
+ // disappear behind game UI.
+ using (ImRaii.Disabled(!Mutable.HellionThemeEnabled))
+ {
+ ImGui.SetNextItemWidth(200f * ImGuiHelpers.GlobalScale);
+ var opacity = Mutable.HellionThemeWindowOpacity;
+ if (ImGui.SliderFloat($"{HellionStrings.Theme_WindowOpacity_Label}##theme-opacity", ref opacity, 0.5f, 1.0f, "%.2f"))
+ {
+ Mutable.HellionThemeWindowOpacity = Math.Clamp(opacity, 0.5f, 1.0f);
+ }
+ ImGuiUtil.HelpMarker(HellionStrings.Theme_WindowOpacity_Help);
+ }
+
+ ImGui.Spacing();
+
+ ImGui.Checkbox(Language.Options_OverrideStyle_Name, ref Mutable.OverrideStyle);
+ ImGuiUtil.HelpMarker(Language.Options_OverrideStyle_Name_Desc);
+
+ if (Mutable.OverrideStyle)
+ {
+ DrawStyleCombo();
+ }
+
+ // The Bestand-Slider WindowAlpha targets the chat log window's
+ // background only. The Hellion theme opacity above already covers
+ // every plugin window globally, so the two sliders fight each
+ // other when the theme is active. Disable the legacy slider in
+ // that case to make Hellion theme the single source of truth.
+ using (ImRaii.Disabled(Mutable.HellionThemeEnabled))
+ {
+ ImGuiUtil.DragFloatVertical(Language.Options_WindowOpacity_Name, ref Mutable.WindowAlpha, .25f, 0f, 100f, $"{Mutable.WindowAlpha:N2}%%", ImGuiSliderFlags.AlwaysClamp);
+ }
+ }
+ }
+
+ private void DrawStyleCombo()
+ {
+ var styles = StyleModel.GetConfiguredStyles();
+ if (styles == null)
+ {
+ ImGui.TextUnformatted(Language.Options_OverrideStyle_NotAvailable);
+ return;
+ }
+
+ var currentStyle = Mutable.ChosenStyle ?? Language.Options_OverrideStyle_NotSelected;
+ using var combo = ImRaii.Combo(Language.Options_OverrideStyleDropdown_Name, currentStyle);
+ if (!combo)
+ {
+ return;
+ }
+
+ foreach (var style in styles)
+ {
+ if (ImGui.Selectable(style.Name, Mutable.ChosenStyle == style.Name))
+ {
+ Mutable.ChosenStyle = style.Name;
+ }
+ }
+ }
+
+ private void DrawFontsSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Appearance_Fonts_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ if (ImGui.Checkbox(HellionStrings.Theme_UseHellionFont_Name, ref Mutable.UseHellionFont))
+ {
+ // Mutex with the Bestand custom-font stack. Leaving FontsEnabled
+ // checked alongside UseHellionFont made both checkboxes look
+ // active even though the lower stack was greyed out, which
+ // confused the user during the v0.5.0 walkthrough.
+ if (Mutable.UseHellionFont)
+ Mutable.FontsEnabled = false;
+ }
+ ImGuiUtil.HelpMarker(HellionStrings.Theme_UseHellionFont_Description);
+ ImGui.Spacing();
+
+ using var fontDisabled = ImRaii.Disabled(Mutable.UseHellionFont);
+
+ ImGui.Checkbox(Language.Options_FontsEnabled, ref Mutable.FontsEnabled);
+ ImGui.Spacing();
+
+ var unused = false;
+ if (!Mutable.FontsEnabled)
+ {
+ ImGuiUtil.FontSizeCombo(Language.Options_FontSize_Name, ref Mutable.FontSizeV2);
+ }
+ else
+ {
+ var globalChooser = ImGuiUtil.FontChooser(Language.Options_Font_Name, Mutable.GlobalFontV2, false, ref unused);
+ globalChooser?.ResultTask.ContinueWith(r =>
+ {
+ if (r.IsCompletedSuccessfully)
+ Mutable.GlobalFontV2 = r.Result;
+ });
+ ImGui.SameLine();
+ if (ImGui.Button("Reset##global"))
+ {
+ Mutable.GlobalFontV2 = new SingleFontSpec { FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular), SizePt = 12.75f };
+ }
+
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_Font_Description, Plugin.PluginName));
+ ImGuiUtil.WarningText(Language.Options_Font_Warning);
+ ImGui.Spacing();
+
+ // LocaleNames being null means it is likely a game font which all support JP symbols.
+ var japaneseChooser = ImGuiUtil.FontChooser(Language.Options_JapaneseFont_Name, Mutable.JapaneseFontV2, false, ref unused, id => !id.LocaleNames?.ContainsKey("ja-jp") ?? false, "いろはにほへと ちりぬるを");
+ japaneseChooser?.ResultTask.ContinueWith(r =>
+ {
+ if (r.IsCompletedSuccessfully)
+ Mutable.JapaneseFontV2 = r.Result;
+ });
+ ImGui.SameLine();
+ if (ImGui.Button("Reset##japanese"))
+ {
+ Mutable.JapaneseFontV2 = new SingleFontSpec { FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkMedium), SizePt = 12.75f };
+ }
+
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_JapaneseFont_Description, Plugin.PluginName));
+ ImGui.Spacing();
+
+ var italicChooser = ImGuiUtil.FontChooser(Language.Options_ItalicFont_Name, Mutable.ItalicFontV2, true, ref Mutable.ItalicEnabled);
+ italicChooser?.ResultTask.ContinueWith(r =>
+ {
+ if (r.IsCompletedSuccessfully)
+ Mutable.ItalicFontV2 = r.Result;
+ });
+ ImGui.SameLine();
+ if (ImGui.Button("Reset##italic"))
+ {
+ Mutable.ItalicEnabled = false;
+ Mutable.ItalicFontV2 = new SingleFontSpec { FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular), SizePt = 12.75f };
+ }
+
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_Italic_Description, Plugin.PluginName));
+ ImGui.Spacing();
+
+ if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name))
+ {
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName));
+
+ var range = (int)Mutable.ExtraGlyphRanges;
+ foreach (var extra in Enum.GetValues())
+ {
+ ImGui.CheckboxFlags(extra.Name(), ref range, (int)extra);
+ }
+
+ Mutable.ExtraGlyphRanges = (ExtraGlyphRanges)range;
+ }
+
+ ImGui.Spacing();
+ }
+
+ ImGuiUtil.FontSizeCombo(Language.Options_SymbolsFontSize_Name, ref Mutable.SymbolsFontSizeV2);
+ ImGuiUtil.HelpMarker(Language.Options_SymbolsFontSize_Description);
+
+ ImGui.Spacing();
+ }
+ }
+
+ private void DrawColoursSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Appearance_Colours_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ foreach (var (_, types) in ChatTypeExt.SortOrder)
+ {
+ foreach (var type in types)
+ {
+ if (ImGuiUtil.IconButton(FontAwesomeIcon.UndoAlt, $"{type}", Language.Options_ChatColours_Reset))
+ {
+ Mutable.ChatColours.Remove(type);
+ }
+
+ ImGui.SameLine();
+
+ if (ImGuiUtil.IconButton(FontAwesomeIcon.LongArrowAltDown, $"{type}", Language.Options_ChatColours_Import))
+ {
+ var gameColour = Plugin.Functions.Chat.GetChannelColor(type);
+ Mutable.ChatColours[type] = gameColour ?? type.DefaultColor() ?? 0;
+ }
+
+ ImGui.SameLine();
+
+ var vec = Mutable.ChatColours.TryGetValue(type, out var colour)
+ ? ColourUtil.RgbaToVector3(colour)
+ : ColourUtil.RgbaToVector3(type.DefaultColor() ?? 0);
+ if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs))
+ {
+ Mutable.ChatColours[type] = ColourUtil.Vector3ToRgba(vec);
+ }
+ }
+ }
+
+ ImGui.Spacing();
+ }
+ }
+
+ private void DrawTimestampsSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Appearance_Timestamps_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_PrettierTimestamps_Name, ref Mutable.PrettierTimestamps);
+ ImGuiUtil.HelpMarker(Language.Options_PrettierTimestamps_Description);
+
+ if (Mutable.PrettierTimestamps)
+ {
+ ImGui.Checkbox(Language.Options_MoreCompactPretty_Name, ref Mutable.MoreCompactPretty);
+ ImGuiUtil.HelpMarker(Language.Options_MoreCompactPretty_Description);
+
+ ImGui.Checkbox(Language.Options_HideSameTimestamps_Name, ref Mutable.HideSameTimestamps);
+ ImGuiUtil.HelpMarker(Language.Options_HideSameTimestamps_Description);
+ }
+
+ ImGui.Checkbox(Language.Options_Use24HourClock_Name, ref Mutable.Use24HourClock);
+ ImGuiUtil.HelpMarker(Language.Options_Use24HourClock_Description);
+ }
+ }
+}
diff --git a/ChatTwo/Ui/SettingsTabs/Changelog.cs b/ChatTwo/Ui/SettingsTabs/Changelog.cs
deleted file mode 100644
index 8ee43a7..0000000
--- a/ChatTwo/Ui/SettingsTabs/Changelog.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud.Interface.Utility.Raii;
-using Dalamud.Bindings.ImGui;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-internal sealed class Changelog : ISettingsTab
-{
- private Configuration Mutable { get; }
-
- public string Name => Language.Options_Changelog_Tab + "###tabs-changelog";
-
- internal Changelog(Configuration mutable)
- {
- Mutable = mutable;
- }
-
- public void Draw(bool changed)
- {
- using var wrap = ImRaii.TextWrapPos(0.0f);
-
- ImGui.TextUnformatted(Language.Options_Warning_NotImplemented);
- ImGuiUtil.OptionCheckbox(ref Mutable.PrintChangelog, Language.Options_PrintChangelog_Name, Language.Options_PrintChangelog_Description);
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- var changelog = Plugin.Interface.Manifest.Changelog;
- if (changelog != null)
- {
- ImGui.TextUnformatted(Language.Options_Changelog_Header);
- ImGui.TextUnformatted($"Version {Plugin.Interface.Manifest.AssemblyVersion.ToString(3)}");
- ImGui.Spacing();
- foreach (var sentence in changelog.Split("\n"))
- {
- if (sentence == string.Empty)
- {
- ImGui.NewLine();
- continue;
- }
-
- var condition = sentence.StartsWith('-') || sentence.StartsWith(" -");
- using var indent = ImRaii.PushIndent(10.0f, true, condition);
- ImGui.TextUnformatted(sentence);
- }
- }
- ImGui.Spacing();
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/Chat.cs b/ChatTwo/Ui/SettingsTabs/Chat.cs
new file mode 100644
index 0000000..fffb17a
--- /dev/null
+++ b/ChatTwo/Ui/SettingsTabs/Chat.cs
@@ -0,0 +1,234 @@
+using System.Numerics;
+using ChatTwo.Resources;
+using ChatTwo.Util;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Utility;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Bindings.ImGui;
+
+namespace ChatTwo.Ui.SettingsTabs;
+
+// Chat-Tab — vier eigenständige Sektionen: Auto-Tell-Tabs, Behaviour,
+// Preview, Emotes. Der Emotes-Block ist 1:1 aus der Bestand-Datei
+// Emote.cs übernommen; die Datei wird in Plan-Task 11 (Settings UX
+// Polish v0.5.0) entfernt, sobald alle Tabs migriert sind.
+internal sealed class Chat : ISettingsTab
+{
+ private Plugin Plugin { get; }
+ private Configuration Mutable { get; }
+
+ public string Name => HellionStrings.Settings_Tab_Chat + "###tabs-chat";
+
+ private SearchSelector.SelectorPopupOptions WordPopupOptions;
+
+ internal Chat(Plugin plugin, Configuration mutable)
+ {
+ Plugin = plugin;
+ Mutable = mutable;
+
+ WordPopupOptions = RefillSheet();
+ }
+
+ private SearchSelector.SelectorPopupOptions RefillSheet()
+ {
+ return new SearchSelector.SelectorPopupOptions
+ {
+ FilteredSheet = EmoteCache.SortedCodeArray.Where(w => !Mutable.BlockedEmotes.Contains(w)).ToArray()
+ };
+ }
+
+ public void Draw(bool changed)
+ {
+ DrawAutoTellTabsSection();
+ ImGui.Spacing();
+ DrawBehaviourSection();
+ ImGui.Spacing();
+ DrawPreviewSection();
+ ImGui.Spacing();
+ DrawEmotesSection();
+ }
+
+ private void DrawAutoTellTabsSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Chat_AutoTellTabs_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(HellionStrings.ChatLog_AutoTellTabs_Enable_Name, ref Mutable.EnableAutoTellTabs);
+ ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Enable_Description);
+
+ ImGui.SetNextItemWidth(200f * ImGuiHelpers.GlobalScale);
+ var limit = Mutable.AutoTellTabsLimit;
+ if (ImGui.SliderInt(HellionStrings.ChatLog_AutoTellTabs_Limit_Name, ref limit, 1, 50))
+ {
+ Mutable.AutoTellTabsLimit = limit;
+ }
+ ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Limit_Description);
+
+ ImGui.Checkbox(HellionStrings.ChatLog_AutoTellTabs_Compact_Name, ref Mutable.AutoTellTabsCompactDisplay);
+ ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Compact_Description);
+
+ ImGui.Checkbox(HellionStrings.ChatLog_AutoTellTabs_GreetedToggle_Name, ref Mutable.AutoTellTabsShowGreetedToggle);
+ ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_GreetedToggle_Description);
+
+ ImGui.Spacing();
+ ImGuiUtil.HelpText(HellionStrings.ChatLog_AutoTellTabs_PreloadHint);
+
+ ImGui.Spacing();
+ ImGuiUtil.WarningText(HellionStrings.ChatLog_AutoTellTabs_ConflictHint);
+ }
+ }
+
+ private void DrawBehaviourSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Chat_Behaviour_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_CollapseDuplicateMessages_Name, ref Mutable.CollapseDuplicateMessages);
+ ImGuiUtil.HelpMarker(Language.Options_CollapseDuplicateMessages_Description);
+
+ if (Mutable.CollapseDuplicateMessages)
+ {
+ ImGui.Checkbox(Language.Options_CollapseDuplicateMsgUniqueLink_Name, ref Mutable.CollapseKeepUniqueLinks);
+ ImGuiUtil.HelpMarker(Language.Options_CollapseDuplicateMsgUniqueLink_Description);
+ }
+ }
+ }
+
+ private void DrawPreviewSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Chat_Preview_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Preview_Name, Mutable.PreviewPosition.Name()))
+ {
+ if (combo)
+ {
+ foreach (var position in Enum.GetValues())
+ {
+ if (ImGui.Selectable(position.Name(), Mutable.PreviewPosition == position))
+ {
+ Mutable.PreviewPosition = position;
+ }
+ }
+ }
+ }
+ ImGuiUtil.HelpMarker(Language.Options_Preview_Description);
+
+ if (ImGuiUtil.InputIntVertical(Language.Options_PreviewMinimum_Name, Language.Options_PreviewMinimum_Description, ref Mutable.PreviewMinimum))
+ {
+ Mutable.PreviewMinimum = Math.Clamp(Mutable.PreviewMinimum, 1, 250);
+ }
+
+ ImGui.Checkbox(Language.Options_PreviewOnlyIf_Name, ref Mutable.OnlyPreviewIf);
+ ImGuiUtil.HelpMarker(Language.Options_PreviewOnlyIf_Description);
+ }
+ }
+
+ private void DrawEmotesSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Chat_Emotes_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_ShowEmotes_Name, ref Mutable.ShowEmotes);
+ ImGuiUtil.HelpMarker(Language.Options_ShowEmotes_Desc);
+
+ ImGui.Spacing();
+ ImGui.TextUnformatted(Language.Options_Emote_BlockedEmotes);
+ ImGui.Spacing();
+
+ if (EmoteCache.State is EmoteCache.LoadingState.Done && WordPopupOptions.FilteredSheet.Length == 0)
+ {
+ WordPopupOptions = RefillSheet();
+ }
+
+ var buttonWidth = ImGui.GetContentRegionAvail().X / 3;
+ using (Plugin.FontManager.FontAwesome.Push())
+ ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(buttonWidth, 0));
+
+ if (SearchSelector.SelectorPopup("WordAddPopup", out var newWord, WordPopupOptions))
+ {
+ Mutable.BlockedEmotes.Add(newWord);
+ }
+
+ using (var table = ImRaii.Table("##BlockedWords", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInner))
+ {
+ if (table)
+ {
+ ImGui.TableSetupColumn(Language.Options_Emote_EmoteTable);
+ ImGui.TableSetupColumn("##Del", ImGuiTableColumnFlags.WidthStretch, 0.07f);
+
+ ImGui.TableHeadersRow();
+
+ var copiedList = Mutable.BlockedEmotes.ToArray();
+ foreach (var word in copiedList)
+ {
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(word);
+
+ ImGui.TableNextColumn();
+ if (ImGuiUtil.Button($"##{word}Del", FontAwesomeIcon.Trash, !ImGui.GetIO().KeyCtrl))
+ {
+ Mutable.BlockedEmotes.Remove(word);
+ }
+ }
+ }
+ }
+
+ ImGui.Spacing();
+ ImGui.Separator();
+ ImGui.Spacing();
+
+ ImGui.TextUnformatted(Language.Options_Emote_EmoteStats);
+ ImGui.Spacing();
+
+ if (EmoteCache.State is EmoteCache.LoadingState.Done)
+ {
+ ImGui.TextColored(ImGuiColors.HealerGreen, Language.Options_Emote_Ready);
+ }
+ else
+ {
+ ImGui.TextColored(ImGuiColors.DPSRed, Language.Options_Emote_NotReady);
+ }
+
+ ImGui.TextUnformatted($"{Language.Options_Emote_Loaded} {EmoteCache.SortedCodeArray.Length}");
+ using (var emoteTable = ImRaii.Table("##LoadedEmotes", 5, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInner))
+ {
+ if (emoteTable)
+ {
+ ImGui.TableSetupColumn("##word1");
+ ImGui.TableSetupColumn("##word2");
+ ImGui.TableSetupColumn("##word3");
+ ImGui.TableSetupColumn("##word4");
+ ImGui.TableSetupColumn("##word5");
+
+ foreach (var word in EmoteCache.SortedCodeArray)
+ {
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(word);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ChatTwo/Ui/SettingsTabs/ChatColours.cs b/ChatTwo/Ui/SettingsTabs/ChatColours.cs
deleted file mode 100755
index 18f784f..0000000
--- a/ChatTwo/Ui/SettingsTabs/ChatColours.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using ChatTwo.Code;
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud.Interface;
-using Dalamud.Bindings.ImGui;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-internal sealed class ChatColours : ISettingsTab
-{
- private Plugin Plugin { get; }
- private Configuration Mutable { get; }
-
- public string Name => Language.Options_ChatColours_Tab + "###tabs-chat-colours";
-
- internal ChatColours(Plugin plugin, Configuration mutable)
- {
- Plugin = plugin;
- Mutable = mutable;
-
- #if DEBUG
- // Users can set colours for ExtraChat linkshells in the ExtraChat plugin directly.
- var sortable = ChatTypeExt.SortOrder
- .SelectMany(entry => entry.Item2)
- .Where(type => !type.IsGm() && !type.IsExtraChatLinkshell())
- .ToHashSet();
- var total = Enum.GetValues()
- .Where(type => !type.IsGm() && !type.IsExtraChatLinkshell())
- .ToHashSet();
- if (sortable.Count != total.Count)
- {
- Plugin.Log.Warning($"There are {sortable.Count} sortable channels, but there are {total.Count} total channels.");
- total.ExceptWith(sortable);
- foreach (var missing in total)
- Plugin.Log.Information($"Missing {missing}");
- }
- #endif
- }
-
- public void Draw(bool changed)
- {
- foreach (var (_, types) in ChatTypeExt.SortOrder)
- {
- foreach (var type in types)
- {
- if (ImGuiUtil.IconButton(FontAwesomeIcon.UndoAlt, $"{type}", Language.Options_ChatColours_Reset))
- Mutable.ChatColours.Remove(type);
-
- ImGui.SameLine();
-
- if (ImGuiUtil.IconButton(FontAwesomeIcon.LongArrowAltDown, $"{type}", Language.Options_ChatColours_Import))
- {
- var gameColour = Plugin.Functions.Chat.GetChannelColor(type);
- Mutable.ChatColours[type] = gameColour ?? type.DefaultColor() ?? 0;
- }
-
- ImGui.SameLine();
-
- var vec = Mutable.ChatColours.TryGetValue(type, out var colour)
- ? ColourUtil.RgbaToVector3(colour)
- : ColourUtil.RgbaToVector3(type.DefaultColor() ?? 0);
- if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs))
- Mutable.ChatColours[type] = ColourUtil.Vector3ToRgba(vec);
- }
- }
-
- ImGui.Spacing();
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/ChatLog.cs b/ChatTwo/Ui/SettingsTabs/ChatLog.cs
deleted file mode 100644
index a88f5be..0000000
--- a/ChatTwo/Ui/SettingsTabs/ChatLog.cs
+++ /dev/null
@@ -1,159 +0,0 @@
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud.Interface.Style;
-using Dalamud.Interface.Utility;
-using Dalamud.Interface.Utility.Raii;
-using Dalamud.Bindings.ImGui;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-internal sealed class ChatLog : ISettingsTab
-{
- private readonly Plugin Plugin;
- private Configuration Mutable { get; }
-
- public string Name => Language.Options_ChatLog_Tab + "###tabs-chatlog";
-
- internal ChatLog(Plugin plugin, Configuration mutable)
- {
- Plugin = plugin;
- Mutable = mutable;
- }
-
- public void Draw(bool changed)
- {
- using (ImRaii.TextWrapPos(0.0f))
- {
- ImGuiUtil.OptionCheckbox(ref Mutable.KeepInputFocus, Language.Options_KeepInputFocus_Name, Language.Options_KeepInputFocus_Description);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.PlaySounds, Language.Options_PlaySounds_Name, Language.Options_PlaySounds_Description);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.SidebarTabView, Language.Options_SidebarTabView_Name, string.Format(Language.Options_SidebarTabView_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.ShowNoviceNetwork, Language.Options_ShowNoviceNetwork_Name, Language.Options_ShowNoviceNetwork_Description);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.ShowHideButton, Language.Options_ShowHideButton_Name, Language.Options_ShowHideButton_Description);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.NativeItemTooltips, Language.Options_NativeItemTooltips_Name, string.Format(Language.Options_NativeItemTooltips_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- if (Mutable.NativeItemTooltips)
- {
- ImGuiUtil.DragFloatVertical(Language.Options_TooltipOffset_Name, Language.Options_TooltipOffset_Desc, ref Mutable.TooltipOffset, 1, 0f, 400f, $"{Mutable.TooltipOffset:N0}px", ImGuiSliderFlags.AlwaysClamp);
- ImGui.Spacing();
- }
-
- ImGuiUtil.DragFloatVertical(Language.Options_WindowOpacity_Name, ref Mutable.WindowAlpha, .25f, 0f, 100f, $"{Mutable.WindowAlpha:N2}%%", ImGuiSliderFlags.AlwaysClamp);
- ImGui.Spacing();
-
- if (ImGuiUtil.InputIntVertical(Language.Options_MaxLinesToShow_Name, Language.Options_MaxLinesToShow_Description, ref Mutable.MaxLinesToRender))
- Mutable.MaxLinesToRender = Math.Clamp(Mutable.MaxLinesToRender, 1, 10_000);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.CanMove, Language.Options_CanMove_Name);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.CanResize, Language.Options_CanResize_Name);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.ShowTitleBar, Language.Options_ShowTitleBar_Name);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.ShowPopOutTitleBar, Language.Options_ShowPopOutTitleBar_Name);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.OverrideStyle, Language.Options_OverrideStyle_Name, Language.Options_OverrideStyle_Name_Desc);
- ImGui.Spacing();
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- ImGui.TextUnformatted(Language.Options_ChatTabForwardKeybind_Name);
- ImGui.SetNextItemWidth(-1);
- ImGuiUtil.KeybindInput("ChatTabForwardKeybind", ref Mutable.ChatTabForward);
-
- ImGui.TextUnformatted(Language.Options_ChatTabBackwardKeybind_Name);
- ImGui.SetNextItemWidth(-1);
- ImGuiUtil.KeybindInput("ChatTabBackwardKeybind", ref Mutable.ChatTabBackward);
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- ImGui.TextUnformatted(Language.Options_AdjustPosition_Name);
- ImGui.SetNextItemWidth(-1);
- var pos = Plugin.ChatLogWindow.LastWindowPos;
- if (ImGui.DragFloat2($"##{Language.Options_AdjustPosition_Name}", ref pos, 1, 0, float.MaxValue, "%.0fpx"))
- Plugin.ChatLogWindow.Position = pos;
- ImGuiUtil.WarningText(Language.Options_AdjustPosition_Warning);
- ImGui.Spacing();
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- DrawAutoTellTabsSection();
- }
-
- if (!Mutable.OverrideStyle)
- return;
-
- var styles = StyleModel.GetConfiguredStyles();
- if (styles == null)
- {
- ImGui.TextUnformatted(Language.Options_OverrideStyle_NotAvailable);
- ImGui.Spacing();
- return;
- }
-
- var currentStyle = Mutable.ChosenStyle ?? Language.Options_OverrideStyle_NotSelected;
- using var combo = ImRaii.Combo(Language.Options_OverrideStyleDropdown_Name, currentStyle);
- if (combo)
- {
- foreach (var style in styles)
- if (ImGui.Selectable(style.Name, Mutable.ChosenStyle == style.Name))
- Mutable.ChosenStyle = style.Name;
- }
-
- ImGui.Spacing();
- }
-
- private void DrawAutoTellTabsSection()
- {
- using var tree = ImRaii.TreeNode(HellionStrings.ChatLog_AutoTellTabs_Section_Title);
- if (!tree.Success)
- return;
-
- using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
- {
- ImGui.Checkbox(HellionStrings.ChatLog_AutoTellTabs_Enable_Name, ref Mutable.EnableAutoTellTabs);
- ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Enable_Description);
-
- ImGui.SetNextItemWidth(200f * ImGuiHelpers.GlobalScale);
- var limit = Mutable.AutoTellTabsLimit;
- if (ImGui.SliderInt(HellionStrings.ChatLog_AutoTellTabs_Limit_Name, ref limit, 1, 50))
- {
- Mutable.AutoTellTabsLimit = limit;
- }
- ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Limit_Description);
-
- ImGui.Checkbox(HellionStrings.ChatLog_AutoTellTabs_Compact_Name, ref Mutable.AutoTellTabsCompactDisplay);
- ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Compact_Description);
-
- ImGui.Checkbox(HellionStrings.ChatLog_AutoTellTabs_GreetedToggle_Name, ref Mutable.AutoTellTabsShowGreetedToggle);
- ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_GreetedToggle_Description);
-
- ImGui.Spacing();
- ImGuiUtil.HelpText(HellionStrings.ChatLog_AutoTellTabs_PreloadHint);
-
- ImGui.Spacing();
- ImGuiUtil.WarningText(HellionStrings.ChatLog_AutoTellTabs_ConflictHint);
- }
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/Database.cs b/ChatTwo/Ui/SettingsTabs/Database.cs
index e38445d..afa75e9 100755
--- a/ChatTwo/Ui/SettingsTabs/Database.cs
+++ b/ChatTwo/Ui/SettingsTabs/Database.cs
@@ -33,56 +33,76 @@ internal sealed class Database : ISettingsTab
public void Draw(bool changed)
{
+ // Shift-on-open keeps the Advanced tools available without a permanent
+ // toggle in the UI, mirroring upstream Chat 2 behaviour.
if (changed)
ShowAdvanced = ImGui.GetIO().KeyShift;
- ImGuiUtil.OptionCheckbox(ref Mutable.DatabaseBattleMessages, Language.Options_DatabaseBattleMessages_Name, Language.Options_DatabaseBattleMessages_Description);
+ DrawStorageSection();
ImGui.Spacing();
-
- if (ImGuiUtil.OptionCheckbox(ref Mutable.LoadPreviousSession, Language.Options_LoadPreviousSession_Name, Language.Options_LoadPreviousSession_Description))
- if (Mutable.LoadPreviousSession)
- Mutable.FilterIncludePreviousSessions = true;
-
+ DrawViewerSection();
ImGui.Spacing();
+ DrawStatsSection();
+ }
- if (ImGuiUtil.OptionCheckbox(ref Mutable.FilterIncludePreviousSessions, Language.Options_FilterIncludePreviousSessions_Name, Language.Options_FilterIncludePreviousSessions_Description))
- if (!Mutable.FilterIncludePreviousSessions)
- Mutable.LoadPreviousSession = false;
+ private void DrawStorageSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Database_Storage_Heading);
+ if (!tree.Success)
+ return;
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- var old = new FileInfo(Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat.db"));
- var migratedOld = new FileInfo(Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat-litedb.db"));
- if (old.Exists || migratedOld.Exists)
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
{
- ImGui.TextUnformatted(Language.Options_Database_Old_Heading);
- ImGui.Spacing();
+ ImGui.Checkbox(Language.Options_DatabaseBattleMessages_Name, ref Mutable.DatabaseBattleMessages);
+ ImGuiUtil.HelpMarker(Language.Options_DatabaseBattleMessages_Description);
- if (ImGuiUtil.CtrlShiftButton(Language.Options_Database_Old_Delete, Language.Options_Database_Old_Delete_Tooltip))
+ if (ImGui.Checkbox(Language.Options_LoadPreviousSession_Name, ref Mutable.LoadPreviousSession))
+ if (Mutable.LoadPreviousSession)
+ Mutable.FilterIncludePreviousSessions = true;
+ ImGuiUtil.HelpMarker(Language.Options_LoadPreviousSession_Description);
+
+ if (ImGui.Checkbox(Language.Options_FilterIncludePreviousSessions_Name, ref Mutable.FilterIncludePreviousSessions))
+ if (!Mutable.FilterIncludePreviousSessions)
+ Mutable.LoadPreviousSession = false;
+ ImGuiUtil.HelpMarker(Language.Options_FilterIncludePreviousSessions_Description);
+
+ var old = new FileInfo(Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat.db"));
+ var migratedOld = new FileInfo(Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat-litedb.db"));
+ if (old.Exists || migratedOld.Exists)
{
- try
+ ImGui.Spacing();
+ ImGui.Separator();
+ ImGui.Spacing();
+
+ ImGui.TextUnformatted(Language.Options_Database_Old_Heading);
+ ImGui.Spacing();
+
+ if (ImGuiUtil.CtrlShiftButton(Language.Options_Database_Old_Delete, Language.Options_Database_Old_Delete_Tooltip))
{
- if (old.Exists)
- old.Delete();
- else
- migratedOld.Delete();
- WrapperUtil.AddNotification(Language.Options_Database_Old_Delete_Success, NotificationType.Success);
- }
- catch (Exception e)
- {
- Plugin.Log.Error(e, "Unable to delete old database");
- WrapperUtil.AddNotification(Language.Options_Database_Old_Delete_Error, NotificationType.Error);
+ try
+ {
+ if (old.Exists)
+ old.Delete();
+ else
+ migratedOld.Delete();
+ WrapperUtil.AddNotification(Language.Options_Database_Old_Delete_Success, NotificationType.Success);
+ }
+ catch (Exception e)
+ {
+ Plugin.Log.Error(e, "Unable to delete old database");
+ WrapperUtil.AddNotification(Language.Options_Database_Old_Delete_Error, NotificationType.Error);
+ }
}
}
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
}
+ }
+
+ private void DrawViewerSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Database_Viewer_Heading);
+ if (!tree.Success)
+ return;
- ImGui.TextUnformatted(Language.Options_Database_Metadata_Heading);
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
{
// Refresh the database size and message count every 5 seconds to avoid
@@ -132,28 +152,34 @@ internal sealed class Database : ISettingsTab
WrapperUtil.AddNotification(Language.Options_ClearDatabase_Success, NotificationType.Info);
}
}
+ }
- ImGui.Spacing();
-
+ private void DrawStatsSection()
+ {
if (!ShowAdvanced)
return;
- using var treeNode = ImRaii.TreeNode(Language.Options_Database_Advanced);
- using var wrap = ImRaii.TextWrapPos(0.0f);
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Database_Stats_Heading);
+ if (!tree.Success)
+ return;
- ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning);
- if (ImGuiUtil.CtrlShiftButton("Perform maintenance", "Ctrl+Shift: MessageManager.Store.PerformMaintenance()"))
- Plugin.MessageManager.Store.PerformMaintenance();
-
- if (ImGuiUtil.CtrlShiftButton("Reload messages from database", "Ctrl+Shift: MessageManager.FilterAllTabs()"))
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
{
- Plugin.MessageManager.ClearAllTabs();
- Plugin.MessageManager.FilterAllTabsAsync();
- }
+ using var wrap = ImRaii.TextWrapPos(0.0f);
- if (ImGuiUtil.CtrlShiftButton("Inject 10,000 messages", "Ctrl+Shift: creates 10,000 unique messages (async)"))
- new Thread(() => InsertMessages(10_000)).Start();
- ImGui.Spacing();
+ ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning);
+ if (ImGuiUtil.CtrlShiftButton("Perform maintenance", "Ctrl+Shift: MessageManager.Store.PerformMaintenance()"))
+ Plugin.MessageManager.Store.PerformMaintenance();
+
+ if (ImGuiUtil.CtrlShiftButton("Reload messages from database", "Ctrl+Shift: MessageManager.FilterAllTabs()"))
+ {
+ Plugin.MessageManager.ClearAllTabs();
+ Plugin.MessageManager.FilterAllTabsAsync();
+ }
+
+ if (ImGuiUtil.CtrlShiftButton("Inject 10,000 messages", "Ctrl+Shift: creates 10,000 unique messages (async)"))
+ new Thread(() => InsertMessages(10_000)).Start();
+ }
}
private void InsertMessages(int count)
diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs
deleted file mode 100755
index 1f2ec64..0000000
--- a/ChatTwo/Ui/SettingsTabs/Display.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud.Interface.Utility.Raii;
-using Dalamud.Bindings.ImGui;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-internal sealed class Display : ISettingsTab
-{
- private Configuration Mutable { get; }
-
- public string Name => Language.Options_Display_Tab + "###tabs-display";
-
- internal Display(Configuration mutable)
- {
- Mutable = mutable;
- }
-
- public void Draw(bool changed)
- {
- using var wrap = ImRaii.TextWrapPos(0.0f);
-
- ImGuiUtil.OptionCheckbox(ref Mutable.HideChat, Language.Options_HideChat_Name, Language.Options_HideChat_Description);
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.HideDuringCutscenes, Language.Options_HideDuringCutscenes_Name, string.Format(Language.Options_HideDuringCutscenes_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.HideWhenNotLoggedIn, Language.Options_HideWhenNotLoggedIn_Name, string.Format(Language.Options_HideWhenNotLoggedIn_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.HideWhenUiHidden, Language.Options_HideWhenUiHidden_Name, string.Format(Language.Options_HideWhenUiHidden_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.HideInLoadingScreens, Language.Options_HideInLoadingScreens_Name, string.Format(Language.Options_HideInLoadingScreens_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.HideInBattle, Language.Options_HideInBattle_Name, Language.Options_HideInBattle_Description);
- ImGui.Spacing();
-
- ImGui.Separator();
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.HideWhenInactive, Language.Options_HideWhenInactive_Name, Language.Options_HideWhenInactive_Description);
- ImGui.Spacing();
-
- if (Mutable.HideWhenInactive)
- {
- using var _ = ImRaii.PushIndent();
- ImGuiUtil.InputIntVertical(Language.Options_InactivityHideTimeout_Name,
- Language.Options_InactivityHideTimeout_Description, ref Mutable.InactivityHideTimeout, 1, 10);
- // Enforce a minimum of 2 seconds to avoid people soft locking
- // themselves.
- Mutable.InactivityHideTimeout = Math.Max(2, Mutable.InactivityHideTimeout);
- ImGui.Spacing();
-
- // This setting conflicts with HideInBattle, so it's disabled.
- using (ImRaii.Disabled(Mutable.HideInBattle))
- {
- ImGuiUtil.OptionCheckbox(ref Mutable.InactivityHideActiveDuringBattle,
- Language.Options_InactivityHideActiveDuringBattle_Name,
- Language.Options_InactivityHideActiveDuringBattle_Description);
- ImGui.Spacing();
- }
-
- using var channelTree = ImRaii.TreeNode(Language.Options_InactivityHideChannels_Name);
- if (channelTree.Success)
- {
- if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_All_Label, Language.Options_InactivityHideChannels_Button_Tooltip))
- {
- Mutable.InactivityHideChannelsV2 = TabsUtil.AllChannels();
- Mutable.InactivityHideExtraChatAll = true;
- Mutable.InactivityHideExtraChatChannels = [];
- }
-
- ImGui.SameLine();
- if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_None_Label, Language.Options_InactivityHideChannels_Button_Tooltip))
- {
- Mutable.InactivityHideChannelsV2 = [];
- Mutable.InactivityHideExtraChatAll = false;
- Mutable.InactivityHideExtraChatChannels = [];
- }
-
- ImGui.Spacing();
-
- ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, Mutable.InactivityHideChannelsV2);
- ImGuiUtil.ExtraChatSelector(Language.Options_Tabs_ExtraChatChannels,
- ref Mutable.InactivityHideExtraChatAll, Mutable.InactivityHideExtraChatChannels);
- }
- ImGui.Spacing();
- }
-
- ImGui.Separator();
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.Use24HourClock, Language.Options_Use24HourClock_Name, Language.Options_Use24HourClock_Description);
-
- ImGuiUtil.OptionCheckbox(ref Mutable.PrettierTimestamps, Language.Options_PrettierTimestamps_Name, Language.Options_PrettierTimestamps_Description);
-
- if (Mutable.PrettierTimestamps)
- {
- using var _ = ImRaii.PushIndent();
- ImGuiUtil.OptionCheckbox(ref Mutable.MoreCompactPretty, Language.Options_MoreCompactPretty_Name, Language.Options_MoreCompactPretty_Description);
- ImGuiUtil.OptionCheckbox(ref Mutable.HideSameTimestamps, Language.Options_HideSameTimestamps_Name, Language.Options_HideSameTimestamps_Description);
- }
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(ref Mutable.CollapseDuplicateMessages, Language.Options_CollapseDuplicateMessages_Name, Language.Options_CollapseDuplicateMessages_Description);
- if (Mutable.CollapseDuplicateMessages)
- {
- using var _ = ImRaii.PushIndent();
- ImGuiUtil.OptionCheckbox(ref Mutable.CollapseKeepUniqueLinks, Language.Options_CollapseDuplicateMsgUniqueLink_Name, Language.Options_CollapseDuplicateMsgUniqueLink_Description);
- }
- ImGui.Spacing();
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/Emote.cs b/ChatTwo/Ui/SettingsTabs/Emote.cs
deleted file mode 100644
index 64f2411..0000000
--- a/ChatTwo/Ui/SettingsTabs/Emote.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System.Numerics;
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud.Interface;
-using Dalamud.Interface.Colors;
-using Dalamud.Interface.Utility.Raii;
-using Dalamud.Bindings.ImGui;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-internal sealed class Emote : ISettingsTab
-{
- private readonly Plugin Plugin;
- private Configuration Mutable { get; }
-
- public string Name => Language.Options_Emote_Tab + "###tabs-emote";
-
- private static SearchSelector.SelectorPopupOptions? WordPopupOptions;
-
- internal Emote(Plugin plugin, Configuration mutable)
- {
- Plugin = plugin;
- Mutable = mutable;
-
- WordPopupOptions = new SearchSelector.SelectorPopupOptions
- {
- FilteredSheet = EmoteCache.SortedCodeArray.Where(w => !Mutable.BlockedEmotes.Contains(w)).ToArray()
- };
- }
-
- private SearchSelector.SelectorPopupOptions RefillSheet()
- {
- return new SearchSelector.SelectorPopupOptions
- {
- FilteredSheet = EmoteCache.SortedCodeArray.Where(w => !Mutable.BlockedEmotes.Contains(w)).ToArray()
- };
- }
-
- public void Draw(bool changed)
- {
- using var wrap = ImRaii.TextWrapPos(0.0f);
-
- ImGuiUtil.OptionCheckbox(ref Mutable.ShowEmotes, Language.Options_ShowEmotes_Name, Language.Options_ShowEmotes_Desc);
- ImGui.Spacing();
-
- ImGui.TextUnformatted(Language.Options_Emote_BlockedEmotes);
- ImGui.Spacing();
-
- WordPopupOptions ??= RefillSheet();
- if (EmoteCache.State is EmoteCache.LoadingState.Done && WordPopupOptions.FilteredSheet.Length == 0)
- WordPopupOptions = RefillSheet();
-
- var buttonWidth = ImGui.GetContentRegionAvail().X / 3;
- using (Plugin.FontManager.FontAwesome.Push())
- ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(buttonWidth, 0));
-
- if (SearchSelector.SelectorPopup("WordAddPopup", out var newWord, WordPopupOptions))
- Mutable.BlockedEmotes.Add(newWord);
-
- using(var table = ImRaii.Table("##BlockedWords", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInner))
- {
- if (table)
- {
- ImGui.TableSetupColumn(Language.Options_Emote_EmoteTable);
- ImGui.TableSetupColumn("##Del", ImGuiTableColumnFlags.WidthStretch, 0.07f);
-
- ImGui.TableHeadersRow();
-
- var copiedList = Mutable.BlockedEmotes.ToArray();
- foreach (var word in copiedList)
- {
- ImGui.TableNextColumn();
- ImGui.TextUnformatted(word);
-
- ImGui.TableNextColumn();
- if (ImGuiUtil.Button($"##{word}Del", FontAwesomeIcon.Trash, !ImGui.GetIO().KeyCtrl))
- Mutable.BlockedEmotes.Remove(word);
- }
- }
- }
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- ImGui.TextUnformatted(Language.Options_Emote_EmoteStats);
- ImGui.Spacing();
-
- if (EmoteCache.State is EmoteCache.LoadingState.Done)
- ImGui.TextColored(ImGuiColors.HealerGreen, Language.Options_Emote_Ready);
- else
- ImGui.TextColored(ImGuiColors.DPSRed, Language.Options_Emote_NotReady);
-
- ImGui.TextUnformatted($"{Language.Options_Emote_Loaded} {EmoteCache.SortedCodeArray.Length}");
- using (var emoteTable = ImRaii.Table("##LoadedEmotes", 5, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInner))
- {
- if (emoteTable)
- {
- ImGui.TableSetupColumn("##word1");
- ImGui.TableSetupColumn("##word2");
- ImGui.TableSetupColumn("##word3");
- ImGui.TableSetupColumn("##word4");
- ImGui.TableSetupColumn("##word5");
-
- foreach (var word in EmoteCache.SortedCodeArray)
- {
- ImGui.TableNextColumn();
- ImGui.TextUnformatted(word);
- }
- }
- }
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/Fonts.cs b/ChatTwo/Ui/SettingsTabs/Fonts.cs
deleted file mode 100755
index a55543a..0000000
--- a/ChatTwo/Ui/SettingsTabs/Fonts.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud;
-using Dalamud.Interface.FontIdentifier;
-using Dalamud.Bindings.ImGui;
-using Dalamud.Interface.Utility.Raii;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-public class Fonts : ISettingsTab
-{
- private Configuration Mutable { get; }
-
- public string Name => Language.Options_Fonts_Tab + "###tabs-fonts";
-
- internal Fonts(Configuration mutable)
- {
- Mutable = mutable;
- }
-
- public void Draw(bool _)
- {
- using var wrap = ImRaii.TextWrapPos(0.0f);
-
- ImGui.Checkbox(Language.Options_FontsEnabled, ref Mutable.FontsEnabled);
- ImGui.Spacing();
-
- if (!Mutable.FontsEnabled)
- {
- ImGuiUtil.FontSizeCombo(Language.Options_FontSize_Name, ref Mutable.FontSizeV2);
- }
- else
- {
- var globalChooser = ImGuiUtil.FontChooser(Language.Options_Font_Name, Mutable.GlobalFontV2, false, ref _);
- globalChooser?.ResultTask.ContinueWith(r =>
- {
- if (r.IsCompletedSuccessfully)
- Mutable.GlobalFontV2 = r.Result;
- });
- ImGui.SameLine();
- if (ImGui.Button("Reset##global"))
- Mutable.GlobalFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular), SizePt = 12.75f };
-
- ImGuiUtil.HelpText(string.Format(Language.Options_Font_Description, Plugin.PluginName));
- ImGuiUtil.WarningText(Language.Options_Font_Warning);
- ImGui.Spacing();
-
- // LocaleNames being null means it is likely a game font which all support JP symbols
- var japaneseChooser = ImGuiUtil.FontChooser(Language.Options_JapaneseFont_Name, Mutable.JapaneseFontV2, false, ref _, id => !id.LocaleNames?.ContainsKey("ja-jp") ?? false, "いろはにほへと ちりぬるを");
- japaneseChooser?.ResultTask.ContinueWith(r =>
- {
- if (r.IsCompletedSuccessfully)
- Mutable.JapaneseFontV2 = r.Result;
- });
- ImGui.SameLine();
- if (ImGui.Button("Reset##japanese"))
- Mutable.JapaneseFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkMedium), SizePt = 12.75f };
-
- ImGuiUtil.HelpText(string.Format(Language.Options_JapaneseFont_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- var italicChooser = ImGuiUtil.FontChooser(Language.Options_ItalicFont_Name, Mutable.ItalicFontV2, true, ref Mutable.ItalicEnabled);
- italicChooser?.ResultTask.ContinueWith(r =>
- {
- if (r.IsCompletedSuccessfully)
- Mutable.ItalicFontV2 = r.Result;
- });
- ImGui.SameLine();
- if (ImGui.Button("Reset##italic"))
- {
- Mutable.ItalicEnabled = false;
- Mutable.ItalicFontV2 = new SingleFontSpec{ FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular), SizePt = 12.75f };
- }
-
- ImGuiUtil.HelpText(string.Format(Language.Options_Italic_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name))
- {
- ImGuiUtil.HelpText(string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName));
-
- var range = (int) Mutable.ExtraGlyphRanges;
- foreach (var extra in Enum.GetValues())
- ImGui.CheckboxFlags(extra.Name(), ref range, (int) extra);
-
- Mutable.ExtraGlyphRanges = (ExtraGlyphRanges) range;
- }
-
- ImGui.Spacing();
- }
-
- ImGuiUtil.FontSizeCombo(Language.Options_SymbolsFontSize_Name, ref Mutable.SymbolsFontSizeV2);
- ImGuiUtil.HelpText(Language.Options_SymbolsFontSize_Description);
-
- ImGui.Spacing();
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/General.cs b/ChatTwo/Ui/SettingsTabs/General.cs
new file mode 100644
index 0000000..60ea048
--- /dev/null
+++ b/ChatTwo/Ui/SettingsTabs/General.cs
@@ -0,0 +1,158 @@
+using ChatTwo.Resources;
+using ChatTwo.Util;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Bindings.ImGui;
+
+namespace ChatTwo.Ui.SettingsTabs;
+
+internal sealed class General : ISettingsTab
+{
+ private Plugin Plugin { get; }
+ private Configuration Mutable { get; }
+
+ public string Name => HellionStrings.Settings_Tab_General + "###tabs-general";
+
+ internal General(Plugin plugin, Configuration mutable)
+ {
+ Plugin = plugin;
+ Mutable = mutable;
+ }
+
+ public void Draw(bool changed)
+ {
+ DrawInputSection();
+ ImGui.Spacing();
+ DrawAudioSection();
+ ImGui.Spacing();
+ DrawPerformanceSection();
+ ImGui.Spacing();
+ DrawLanguageSection();
+ }
+
+ private void DrawInputSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_General_Input_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_KeepInputFocus_Name, ref Mutable.KeepInputFocus);
+ ImGuiUtil.HelpMarker(Language.Options_KeepInputFocus_Description);
+
+ ImGui.Spacing();
+ ImGui.TextUnformatted(Language.Options_ChatTabForwardKeybind_Name);
+ ImGui.SetNextItemWidth(-1);
+ ImGuiUtil.KeybindInput("ChatTabForwardKeybind", ref Mutable.ChatTabForward);
+
+ ImGui.TextUnformatted(Language.Options_ChatTabBackwardKeybind_Name);
+ ImGui.SetNextItemWidth(-1);
+ ImGuiUtil.KeybindInput("ChatTabBackwardKeybind", ref Mutable.ChatTabBackward);
+ }
+ }
+
+ private void DrawAudioSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_General_Audio_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_PlaySounds_Name, ref Mutable.PlaySounds);
+ ImGuiUtil.HelpMarker(Language.Options_PlaySounds_Description);
+
+ ImGui.Checkbox(Language.Options_ShowNoviceNetwork_Name, ref Mutable.ShowNoviceNetwork);
+ ImGuiUtil.HelpMarker(Language.Options_ShowNoviceNetwork_Description);
+ }
+ }
+
+ private void DrawPerformanceSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_General_Performance_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ if (ImGuiUtil.InputIntVertical(Language.Options_MaxLinesToShow_Name, Language.Options_MaxLinesToShow_Description, ref Mutable.MaxLinesToRender))
+ {
+ Mutable.MaxLinesToRender = Math.Clamp(Mutable.MaxLinesToRender, 1, 10_000);
+ }
+ }
+ }
+
+ private void DrawLanguageSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_General_Language_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Language_Name, Mutable.LanguageOverride.Name()))
+ {
+ if (combo.Success)
+ {
+ foreach (var language in Enum.GetValues())
+ {
+ if (ImGui.Selectable(language.Name()))
+ {
+ Mutable.LanguageOverride = language;
+ }
+ }
+ }
+ }
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_Language_Description, Plugin.PluginName));
+ ImGui.Spacing();
+
+ using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_CommandHelpSide_Name, Mutable.CommandHelpSide.Name()))
+ {
+ if (combo.Success)
+ {
+ foreach (var side in Enum.GetValues())
+ {
+ if (ImGui.Selectable(side.Name(), Mutable.CommandHelpSide == side))
+ {
+ Mutable.CommandHelpSide = side;
+ }
+ }
+ }
+ }
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_CommandHelpSide_Description, Plugin.PluginName));
+ ImGui.Spacing();
+
+ using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_KeybindMode_Name, Mutable.KeybindMode.Name()))
+ {
+ if (combo.Success)
+ {
+ foreach (var mode in Enum.GetValues())
+ {
+ if (ImGui.Selectable(mode.Name(), Mutable.KeybindMode == mode))
+ {
+ Mutable.KeybindMode = mode;
+ }
+
+ if (ImGui.IsItemHovered())
+ {
+ ImGuiUtil.Tooltip(mode.Tooltip() ?? "");
+ }
+ }
+ }
+ }
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_KeybindMode_Description, Plugin.PluginName));
+ ImGui.Spacing();
+
+ ImGui.Checkbox(Language.Options_SortAutoTranslate_Name, ref Mutable.SortAutoTranslate);
+ ImGuiUtil.HelpMarker(Language.Options_SortAutoTranslate_Description);
+ }
+ }
+}
diff --git a/ChatTwo/Ui/SettingsTabs/Information.cs b/ChatTwo/Ui/SettingsTabs/Information.cs
new file mode 100644
index 0000000..38667fb
--- /dev/null
+++ b/ChatTwo/Ui/SettingsTabs/Information.cs
@@ -0,0 +1,188 @@
+using ChatTwo.Resources;
+using ChatTwo.Util;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Utility;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Bindings.ImGui;
+
+namespace ChatTwo.Ui.SettingsTabs;
+
+// Information-Tab vereint die früheren About- und Changelog-Tabs in
+// drei kollabierbaren Sektionen. Der About-Inhalt ist 1:1 aus About.cs
+// übernommen, die Changelog-Render-Logik aus Changelog.cs.
+internal sealed class Information : ISettingsTab
+{
+ private Configuration Mutable { get; }
+
+ public string Name => HellionStrings.Settings_Tab_Information + "###tabs-information";
+
+ private readonly List Translators =
+ [
+ "q673135110", "Akizem", "d0tiKs",
+ "Moonlight_Everlit", "Dark32", "andreycout",
+ "Button_", "Cali666", "cassandra308",
+ "lokinmodar", "jtabox", "AkiraYorumoto",
+ "MKhayle", "elena.space", "imlisa",
+ "andrei5125", "ShivaMaheshvara", "aislinn87",
+ "nishinatsu051", "lichuyuan", "Risu64",
+ "yummypillow", "witchymary", "Yuzumi",
+ "zomsakura", "Sirayuki"
+ ];
+
+ internal Information(Configuration mutable)
+ {
+ Mutable = mutable;
+ Translators.Sort((a, b) => string.Compare(a.ToLowerInvariant(), b.ToLowerInvariant(), StringComparison.Ordinal));
+ }
+
+ public void Draw(bool changed)
+ {
+ using var wrap = ImRaii.TextWrapPos(0.0f);
+
+ DrawVersionInfoSection();
+ ImGui.Spacing();
+ DrawAboutSection();
+ ImGui.Spacing();
+ DrawChangelogSection();
+ }
+
+ private void DrawVersionInfoSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Information_VersionInfo_Heading);
+ if (!tree.Success)
+ return;
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName));
+
+ ImGuiHelpers.ScaledDummy(10.0f);
+
+ ImGui.TextUnformatted(Language.Options_About_Authors);
+ ImGui.SameLine();
+ ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Interface.Manifest.Author);
+
+ ImGui.TextUnformatted(Language.Options_About_Discord);
+ ImGui.SameLine();
+ ImGui.TextColored(ImGuiColors.ParsedGold, "@j.j_kazama");
+
+ ImGui.TextUnformatted(Language.Options_About_Version);
+ ImGui.SameLine();
+ ImGui.TextColored(ImGuiColors.ParsedOrange, Plugin.Interface.Manifest.AssemblyVersion.ToString(3));
+
+ ImGuiHelpers.ScaledDummy(10.0f);
+
+ ImGui.TextUnformatted(Language.Options_About_Github_Issues);
+ ImGui.SameLine();
+ if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "githubIssues"))
+ Dalamud.Utility.Util.OpenLink("https://github.com/JonKazama-Hellion/HellionChat/issues");
+ }
+ }
+
+ private void DrawAboutSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Information_About_Heading);
+ if (!tree.Success)
+ return;
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ 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");
+
+ ImGuiHelpers.ScaledDummy(10.0f);
+
+ 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, 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, 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, HellionStrings.About_SE_Heading);
+ ImGui.TextUnformatted(HellionStrings.About_SE_P1);
+ ImGui.TextUnformatted(HellionStrings.About_SE_P2);
+
+ ImGui.Spacing();
+
+ ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_Localization_Heading);
+ ImGui.TextUnformatted(HellionStrings.About_Localization_P1);
+ ImGui.TextUnformatted(HellionStrings.About_Localization_P2);
+
+ ImGui.Spacing();
+
+ using (var translatorTree = ImRaii.TreeNode(HellionStrings.About_Translators_TreeNode))
+ {
+ if (translatorTree)
+ {
+ using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
+ foreach (var translator in Translators)
+ ImGui.TextUnformatted(translator);
+ }
+ }
+ }
+ }
+
+ private void DrawChangelogSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Information_Changelog_Heading);
+ if (!tree.Success)
+ return;
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_PrintChangelog_Name, ref Mutable.PrintChangelog);
+ ImGuiUtil.HelpMarker(Language.Options_PrintChangelog_Description);
+
+ ImGui.Spacing();
+ ImGui.Separator();
+ ImGui.Spacing();
+
+ var changelog = Plugin.Interface.Manifest.Changelog;
+ if (changelog == null)
+ return;
+
+ ImGui.TextUnformatted(Language.Options_Changelog_Header);
+ ImGui.TextUnformatted($"Version {Plugin.Interface.Manifest.AssemblyVersion.ToString(3)}");
+ ImGui.Spacing();
+ foreach (var sentence in changelog.Split("\n"))
+ {
+ if (sentence == string.Empty)
+ {
+ ImGui.NewLine();
+ continue;
+ }
+
+ var indented = sentence.StartsWith('-') || sentence.StartsWith(" -");
+ using var indent = ImRaii.PushIndent(10.0f, true, indented);
+ ImGui.TextUnformatted(sentence);
+ }
+ }
+ }
+}
diff --git a/ChatTwo/Ui/SettingsTabs/Miscellaneous.cs b/ChatTwo/Ui/SettingsTabs/Miscellaneous.cs
deleted file mode 100755
index e802d5e..0000000
--- a/ChatTwo/Ui/SettingsTabs/Miscellaneous.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud.Bindings.ImGui;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-internal sealed class Miscellaneous(Configuration mutable) : ISettingsTab
-{
- private Configuration Mutable { get; } = mutable;
- public string Name => Language.Options_Miscellaneous_Tab + "###tabs-miscellaneous";
-
- public void Draw(bool changed)
- {
- using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Language_Name, Mutable.LanguageOverride.Name()))
- {
- if (combo.Success)
- {
- foreach (var language in Enum.GetValues())
- if (ImGui.Selectable(language.Name()))
- Mutable.LanguageOverride = language;
- }
- }
-
- ImGuiUtil.HelpText(string.Format(Language.Options_Language_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_CommandHelpSide_Name, Mutable.CommandHelpSide.Name()))
- {
- if (combo.Success)
- {
- foreach (var side in Enum.GetValues())
- if (ImGui.Selectable(side.Name(), Mutable.CommandHelpSide == side))
- Mutable.CommandHelpSide = side;
- }
- }
-
- ImGuiUtil.HelpText(string.Format(Language.Options_CommandHelpSide_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_KeybindMode_Name, Mutable.KeybindMode.Name()))
- {
- if (combo.Success)
- {
- foreach (var mode in Enum.GetValues())
- {
- if (ImGui.Selectable(mode.Name(), Mutable.KeybindMode == mode))
- Mutable.KeybindMode = mode;
-
- if (ImGui.IsItemHovered())
- ImGuiUtil.Tooltip(mode.Tooltip() ?? "");
- }
- }
- }
-
- ImGuiUtil.HelpText(string.Format(Language.Options_KeybindMode_Description, Plugin.PluginName));
- ImGui.Spacing();
-
- ImGui.Checkbox(Language.Options_SortAutoTranslate_Name, ref Mutable.SortAutoTranslate);
- ImGuiUtil.HelpText(Language.Options_SortAutoTranslate_Description);
- ImGui.Spacing();
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/Preview.cs b/ChatTwo/Ui/SettingsTabs/Preview.cs
deleted file mode 100644
index 8cc0119..0000000
--- a/ChatTwo/Ui/SettingsTabs/Preview.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using ChatTwo.Resources;
-using ChatTwo.Util;
-using Dalamud.Bindings.ImGui;
-using Dalamud.Interface.Utility.Raii;
-
-namespace ChatTwo.Ui.SettingsTabs;
-
-internal sealed class Preview : ISettingsTab
-{
- private Configuration Mutable { get; }
-
- public string Name => $"{Language.Options_Preview_Tab}###tabs-preview";
-
- internal Preview(Configuration mutable)
- {
- Mutable = mutable;
- }
-
- public void Draw(bool changed)
- {
- using var wrap = ImRaii.TextWrapPos(0.0f);
-
- using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Preview_Name, Mutable.PreviewPosition.Name()))
- {
- if (combo)
- {
- foreach (var position in Enum.GetValues())
- if (ImGui.Selectable(position.Name(), Mutable.PreviewPosition == position))
- Mutable.PreviewPosition = position;
- }
- }
- ImGuiUtil.HelpText(Language.Options_Preview_Description);
- ImGui.Spacing();
-
- if (ImGuiUtil.InputIntVertical(Language.Options_PreviewMinimum_Name, Language.Options_PreviewMinimum_Description, ref Mutable.PreviewMinimum))
- Mutable.PreviewMinimum = Math.Clamp(Mutable.PreviewMinimum, 1, 250);
- ImGui.Spacing();
- ImGuiUtil.OptionCheckbox(ref Mutable.OnlyPreviewIf, Language.Options_PreviewOnlyIf_Name, Language.Options_PreviewOnlyIf_Description);
-
- ImGui.Spacing();
- }
-}
diff --git a/ChatTwo/Ui/SettingsTabs/Privacy.cs b/ChatTwo/Ui/SettingsTabs/Privacy.cs
index 46d3ca9..eb3bea3 100644
--- a/ChatTwo/Ui/SettingsTabs/Privacy.cs
+++ b/ChatTwo/Ui/SettingsTabs/Privacy.cs
@@ -74,101 +74,7 @@ internal sealed class Privacy : ISettingsTab
Plugin.FirstRunWizard.IsOpen = true;
ImGui.Spacing();
- ImGui.TextUnformatted(HellionStrings.Theme_Heading);
- using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
- {
- ImGuiUtil.OptionCheckbox(
- ref Mutable.HellionThemeEnabled,
- HellionStrings.Theme_Enabled_Name,
- HellionStrings.Theme_Enabled_Description);
-
- using (ImRaii.Disabled(!Mutable.HellionThemeEnabled))
- {
- ImGui.Spacing();
- var opacity = Mutable.HellionThemeWindowOpacity;
- if (ImGui.SliderFloat($"{HellionStrings.Theme_WindowOpacity_Label}##theme-opacity", ref opacity, 0.5f, 1.0f, "%.2f"))
- Mutable.HellionThemeWindowOpacity = Math.Clamp(opacity, 0.5f, 1.0f);
- ImGuiUtil.HelpText(HellionStrings.Theme_WindowOpacity_Help);
- }
-
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(
- ref Mutable.UseHellionFont,
- HellionStrings.Theme_UseHellionFont_Name,
- HellionStrings.Theme_UseHellionFont_Description);
- }
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(
- ref Mutable.PrivacyFilterEnabled,
- HellionStrings.Privacy_FilterEnabled_Name,
- HellionStrings.Privacy_FilterEnabled_Description);
-
- ImGuiUtil.HelpText(HellionStrings.Privacy_FilterEnabled_StorageOnly_Help);
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- using (ImRaii.Disabled(!Mutable.PrivacyFilterEnabled))
- {
- ImGuiUtil.HelpText(HellionStrings.Privacy_Whitelist_Help);
-
- ImGui.Spacing();
-
- if (ImGui.Button(HellionStrings.Privacy_Preset_PrivacyFirst))
- Mutable.PrivacyPersistChannels = [..PrivacyDefaults.PrivacyFirstWhitelist];
-
- ImGui.SameLine();
- if (ImGui.Button(HellionStrings.Privacy_Preset_ClearAll))
- Mutable.PrivacyPersistChannels.Clear();
-
- ImGui.SameLine();
- if (ImGui.Button(HellionStrings.Privacy_Preset_SelectAll))
- foreach (var group in Groups)
- foreach (var t in group.Types)
- Mutable.PrivacyPersistChannels.Add(t);
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- foreach (var (heading, types) in Groups)
- {
- using var tree = ImRaii.TreeNode(heading());
- if (!tree.Success)
- continue;
-
- using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
- {
- foreach (var type in types)
- {
- var enabled = Mutable.PrivacyPersistChannels.Contains(type);
- var label = type.ToString();
- if (ImGui.Checkbox($"{label}##privacy-{(int)type}", ref enabled))
- {
- if (enabled)
- Mutable.PrivacyPersistChannels.Add(type);
- else
- Mutable.PrivacyPersistChannels.Remove(type);
- }
- }
- }
- }
-
- ImGui.Spacing();
- ImGui.Separator();
- ImGui.Spacing();
-
- ImGuiUtil.OptionCheckbox(
- ref Mutable.PrivacyPersistUnknownChannels,
- HellionStrings.Privacy_PersistUnknown_Name,
- HellionStrings.Privacy_PersistUnknown_Description);
- }
+ DrawPrivacyFilterSection();
ImGui.Spacing();
ImGui.Separator();
@@ -216,6 +122,82 @@ internal sealed class Privacy : ISettingsTab
}
}
+ private void DrawPrivacyFilterSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Privacy_Filter_Tree_Heading);
+ if (!tree.Success)
+ return;
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGuiUtil.OptionCheckbox(
+ ref Mutable.PrivacyFilterEnabled,
+ HellionStrings.Privacy_FilterEnabled_Name,
+ HellionStrings.Privacy_FilterEnabled_Description);
+ ImGuiUtil.HelpMarker(HellionStrings.Privacy_FilterEnabled_StorageOnly_Help);
+
+ ImGui.Spacing();
+ ImGui.Separator();
+ ImGui.Spacing();
+
+ using (ImRaii.Disabled(!Mutable.PrivacyFilterEnabled))
+ {
+ ImGuiUtil.HelpText(HellionStrings.Privacy_Whitelist_Help);
+
+ ImGui.Spacing();
+
+ if (ImGui.Button(HellionStrings.Privacy_Preset_PrivacyFirst))
+ Mutable.PrivacyPersistChannels = [..PrivacyDefaults.PrivacyFirstWhitelist];
+
+ ImGui.SameLine();
+ if (ImGui.Button(HellionStrings.Privacy_Preset_ClearAll))
+ Mutable.PrivacyPersistChannels.Clear();
+
+ ImGui.SameLine();
+ if (ImGui.Button(HellionStrings.Privacy_Preset_SelectAll))
+ foreach (var group in Groups)
+ foreach (var t in group.Types)
+ Mutable.PrivacyPersistChannels.Add(t);
+
+ ImGui.Spacing();
+ ImGui.Separator();
+ ImGui.Spacing();
+
+ foreach (var (heading, types) in Groups)
+ {
+ using var groupTree = ImRaii.TreeNode(heading());
+ if (!groupTree.Success)
+ continue;
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ foreach (var type in types)
+ {
+ var enabled = Mutable.PrivacyPersistChannels.Contains(type);
+ var label = type.ToString();
+ if (ImGui.Checkbox($"{label}##privacy-{(int)type}", ref enabled))
+ {
+ if (enabled)
+ Mutable.PrivacyPersistChannels.Add(type);
+ else
+ Mutable.PrivacyPersistChannels.Remove(type);
+ }
+ }
+ }
+ }
+
+ ImGui.Spacing();
+ ImGui.Separator();
+ ImGui.Spacing();
+
+ ImGuiUtil.OptionCheckbox(
+ ref Mutable.PrivacyPersistUnknownChannels,
+ HellionStrings.Privacy_PersistUnknown_Name,
+ HellionStrings.Privacy_PersistUnknown_Description);
+ }
+ }
+ }
+
private void DrawExportSection()
{
ImGui.TextUnformatted(HellionStrings.Export_Heading);
@@ -363,7 +345,7 @@ internal sealed class Privacy : ISettingsTab
var defaultDays = Mutable.RetentionDefaultDays;
if (ImGui.InputInt(HellionStrings.Retention_Default_Label, ref defaultDays))
Mutable.RetentionDefaultDays = Math.Max(0, defaultDays);
- ImGuiUtil.HelpText(HellionStrings.Retention_Default_Help);
+ ImGuiUtil.HelpMarker(HellionStrings.Retention_Default_Help);
ImGui.Spacing();
@@ -421,6 +403,9 @@ internal sealed class Privacy : ISettingsTab
ImGui.Spacing();
+ ImGuiUtil.HelpText(HellionStrings.Retention_Help_SavedNote);
+ ImGui.Spacing();
+
using (ImRaii.Disabled(RetentionRunning))
{
if (ImGuiUtil.CtrlShiftButton(HellionStrings.Retention_Apply_Label, HellionStrings.Retention_Apply_Tooltip))
diff --git a/ChatTwo/Ui/SettingsTabs/Tabs.cs b/ChatTwo/Ui/SettingsTabs/Tabs.cs
index 5682aaf..518644f 100755
--- a/ChatTwo/Ui/SettingsTabs/Tabs.cs
+++ b/ChatTwo/Ui/SettingsTabs/Tabs.cs
@@ -10,7 +10,7 @@ namespace ChatTwo.Ui.SettingsTabs;
internal sealed class Tabs : ISettingsTab
{
- private readonly Plugin Plugin;
+ private Plugin Plugin { get; }
private Configuration Mutable { get; }
public string Name => Language.Options_Tabs_Tab + "###tabs-tabs";
@@ -27,6 +27,9 @@ internal sealed class Tabs : ISettingsTab
{
const string addTabPopup = "add-tab-popup";
+ ImGuiUtil.HelpText(HellionStrings.Tabs_Presets_Linkshell_Hint);
+ ImGui.Spacing();
+
if (ImGuiUtil.IconButton(FontAwesomeIcon.Plus, tooltip: Language.Options_Tabs_Add))
ImGui.OpenPopup(addTabPopup);
diff --git a/ChatTwo/Ui/SettingsTabs/Window.cs b/ChatTwo/Ui/SettingsTabs/Window.cs
new file mode 100644
index 0000000..109bc78
--- /dev/null
+++ b/ChatTwo/Ui/SettingsTabs/Window.cs
@@ -0,0 +1,163 @@
+using ChatTwo.Resources;
+using ChatTwo.Util;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Bindings.ImGui;
+
+namespace ChatTwo.Ui.SettingsTabs;
+
+internal sealed class Window : ISettingsTab
+{
+ private Plugin Plugin { get; }
+ private Configuration Mutable { get; }
+
+ public string Name => HellionStrings.Settings_Tab_Window + "###tabs-window";
+
+ internal Window(Plugin plugin, Configuration mutable)
+ {
+ Plugin = plugin;
+ Mutable = mutable;
+ }
+
+ public void Draw(bool changed)
+ {
+ DrawHideSection();
+ ImGui.Spacing();
+ DrawInactivityHideSection();
+ ImGui.Spacing();
+ DrawFrameSection();
+ ImGui.Spacing();
+ DrawTooltipsSection();
+ }
+
+ private void DrawHideSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Window_Hide_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_HideChat_Name, ref Mutable.HideChat);
+ ImGuiUtil.HelpMarker(Language.Options_HideChat_Description);
+
+ ImGui.Checkbox(Language.Options_HideDuringCutscenes_Name, ref Mutable.HideDuringCutscenes);
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_HideDuringCutscenes_Description, Plugin.PluginName));
+
+ ImGui.Checkbox(Language.Options_HideWhenNotLoggedIn_Name, ref Mutable.HideWhenNotLoggedIn);
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_HideWhenNotLoggedIn_Description, Plugin.PluginName));
+
+ ImGui.Checkbox(Language.Options_HideWhenUiHidden_Name, ref Mutable.HideWhenUiHidden);
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_HideWhenUiHidden_Description, Plugin.PluginName));
+
+ ImGui.Checkbox(Language.Options_HideInLoadingScreens_Name, ref Mutable.HideInLoadingScreens);
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_HideInLoadingScreens_Description, Plugin.PluginName));
+
+ ImGui.Checkbox(Language.Options_HideInBattle_Name, ref Mutable.HideInBattle);
+ ImGuiUtil.HelpMarker(Language.Options_HideInBattle_Description);
+ }
+ }
+
+ private void DrawInactivityHideSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Window_InactivityHide_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_HideWhenInactive_Name, ref Mutable.HideWhenInactive);
+ ImGuiUtil.HelpMarker(Language.Options_HideWhenInactive_Description);
+
+ if (!Mutable.HideWhenInactive)
+ {
+ return;
+ }
+
+ ImGuiUtil.InputIntVertical(Language.Options_InactivityHideTimeout_Name, Language.Options_InactivityHideTimeout_Description, ref Mutable.InactivityHideTimeout, 1, 10);
+ // Untergrenze von 2 Sekunden gegen Selbst-Soft-Lock.
+ Mutable.InactivityHideTimeout = Math.Max(2, Mutable.InactivityHideTimeout);
+
+ using (ImRaii.Disabled(Mutable.HideInBattle))
+ {
+ ImGui.Checkbox(Language.Options_InactivityHideActiveDuringBattle_Name, ref Mutable.InactivityHideActiveDuringBattle);
+ ImGuiUtil.HelpMarker(Language.Options_InactivityHideActiveDuringBattle_Description);
+ }
+
+ using var channelTree = ImRaii.TreeNode(Language.Options_InactivityHideChannels_Name);
+ if (!channelTree.Success)
+ {
+ return;
+ }
+
+ if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_All_Label, Language.Options_InactivityHideChannels_Button_Tooltip))
+ {
+ Mutable.InactivityHideChannelsV2 = TabsUtil.AllChannels();
+ Mutable.InactivityHideExtraChatAll = true;
+ Mutable.InactivityHideExtraChatChannels = [];
+ }
+
+ ImGui.SameLine();
+ if (ImGuiUtil.CtrlShiftButton(Language.Options_InactivityHideChannels_None_Label, Language.Options_InactivityHideChannels_Button_Tooltip))
+ {
+ Mutable.InactivityHideChannelsV2 = [];
+ Mutable.InactivityHideExtraChatAll = false;
+ Mutable.InactivityHideExtraChatChannels = [];
+ }
+
+ ImGui.Spacing();
+
+ ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, Mutable.InactivityHideChannelsV2);
+ ImGuiUtil.ExtraChatSelector(Language.Options_Tabs_ExtraChatChannels, ref Mutable.InactivityHideExtraChatAll, Mutable.InactivityHideExtraChatChannels);
+ }
+ }
+
+ private void DrawFrameSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Window_Frame_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_CanMove_Name, ref Mutable.CanMove);
+
+ ImGui.Checkbox(Language.Options_CanResize_Name, ref Mutable.CanResize);
+
+ ImGui.Checkbox(Language.Options_ShowTitleBar_Name, ref Mutable.ShowTitleBar);
+
+ ImGui.Checkbox(Language.Options_ShowPopOutTitleBar_Name, ref Mutable.ShowPopOutTitleBar);
+
+ ImGui.Checkbox(Language.Options_ShowHideButton_Name, ref Mutable.ShowHideButton);
+ ImGuiUtil.HelpMarker(Language.Options_ShowHideButton_Description);
+
+ ImGui.Checkbox(Language.Options_SidebarTabView_Name, ref Mutable.SidebarTabView);
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_SidebarTabView_Description, Plugin.PluginName));
+ }
+ }
+
+ private void DrawTooltipsSection()
+ {
+ using var tree = ImRaii.TreeNode(HellionStrings.Settings_Window_Tooltips_Heading);
+ if (!tree.Success)
+ {
+ return;
+ }
+
+ using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
+ {
+ ImGui.Checkbox(Language.Options_NativeItemTooltips_Name, ref Mutable.NativeItemTooltips);
+ ImGuiUtil.HelpMarker(string.Format(Language.Options_NativeItemTooltips_Description, Plugin.PluginName));
+
+ if (Mutable.NativeItemTooltips)
+ {
+ ImGuiUtil.DragFloatVertical(Language.Options_TooltipOffset_Name, Language.Options_TooltipOffset_Desc, ref Mutable.TooltipOffset, 1, 0f, 400f, $"{Mutable.TooltipOffset:N0}px", ImGuiSliderFlags.AlwaysClamp);
+ }
+ }
+ }
+}
diff --git a/ChatTwo/Util/ImGuiUtil.cs b/ChatTwo/Util/ImGuiUtil.cs
index 366dcce..7379001 100755
--- a/ChatTwo/Util/ImGuiUtil.cs
+++ b/ChatTwo/Util/ImGuiUtil.cs
@@ -225,7 +225,11 @@ internal static class ImGuiUtil
using (ImRaii.PushColor(ImGuiCol.Text, ImGui.GetStyle().Colors[(int) ImGuiCol.TextDisabled]))
ImGui.TextUnformatted("(?)");
- if (!ImGui.IsItemHovered())
+ // AllowWhenDisabled — ohne das Flag liefert IsItemHovered bei
+ // ausgegrauten Settings false, der User könnte nicht mehr lesen
+ // warum eine Option nicht aktiv ist. Genau dann braucht er den
+ // Hover-Tooltip aber am dringendsten.
+ if (!ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
return;
using var tooltip = ImRaii.Tooltip();
diff --git a/ChatTwo/Util/TabsUtil.cs b/ChatTwo/Util/TabsUtil.cs
index 984d168..b143a5c 100755
--- a/ChatTwo/Util/TabsUtil.cs
+++ b/ChatTwo/Util/TabsUtil.cs
@@ -14,21 +14,20 @@ public static class TabsUtil
return channels;
}
+ // Hellion-tuned General preset. The pure player-talk catch-all plus
+ // the active-gameplay event streams (loot, crafting, gathering, NPC
+ // dialogue, party-finder pings). Pure technical noise (System, Error,
+ // Login/Logout spam, retainer sales, alarms, sign messages) lives in
+ // the dedicated System tab so it doesn't bury actual conversation.
public static Tab VanillaGeneral => new()
{
Name = Language.Tabs_Presets_General,
SelectedChannels = new Dictionary
{
- // Special
- [ChatType.Debug] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.Urgent] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.Notice] = (ChatSourceExt.All, ChatSourceExt.All),
- // Chat
+ // Player chat
[ChatType.Say] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Yell] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Shout] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.TellIncoming] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Party] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.CrossParty] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Alliance] = (ChatSourceExt.All, ChatSourceExt.All),
@@ -50,33 +49,13 @@ public static class TabsUtil
[ChatType.Linkshell6] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Linkshell7] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Linkshell8] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.NoviceNetwork] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.StandardEmote] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.CustomEmote] = (ChatSourceExt.All, ChatSourceExt.All),
- // Announcements
- [ChatType.System] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.GatheringSystem] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.Error] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.Echo] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.NoviceNetworkSystem] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.FreeCompanyAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.PvpTeamAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.FreeCompanyLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.PvpTeamLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.RetainerSale] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.NpcAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All),
+ // Active-gameplay events
+ [ChatType.NpcDialogue] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.LootNotice] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.Progress] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.LootRoll] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Crafting] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Gathering] = (ChatSource.LocalPlayer, ChatSource.LocalPlayer),
[ChatType.PeriodicRecruitmentNotification] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.Sign] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.RandomNumber] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.Orchestrion] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.MessageBook] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.Alarm] = (ChatSourceExt.All, ChatSourceExt.All),
- [ChatType.GlamourNotifications] = (ChatSourceExt.All, ChatSourceExt.All),
}
};
@@ -98,6 +77,102 @@ public static class TabsUtil
AllSenderMessages = true,
};
+ // Hellion default-tab presets used by the v10 wipe migration. Names are
+ // kept in HellionStrings (EN+DE) instead of Language.* so the upstream
+ // resource files stay untouched. Channel selections cover the channels
+ // a typical Eorzea raider uses without forcing the user to hand-tick
+ // each box on first start.
+ public static Tab HellionFreeCompany => new()
+ {
+ Name = HellionStrings.Tabs_Presets_FreeCompany,
+ SelectedChannels = new Dictionary
+ {
+ [ChatType.FreeCompany] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.FreeCompanyAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.FreeCompanyLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All),
+ },
+ Channel = InputChannel.FreeCompany,
+ };
+
+ public static Tab HellionParty => new()
+ {
+ Name = HellionStrings.Tabs_Presets_Party,
+ SelectedChannels = new Dictionary
+ {
+ [ChatType.Party] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossParty] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Alliance] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.PvpTeam] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.PvpTeamAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.PvpTeamLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.LootNotice] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.LootRoll] = (ChatSourceExt.All, ChatSourceExt.All),
+ },
+ Channel = InputChannel.Party,
+ };
+
+ public static Tab HellionBeginner => new()
+ {
+ Name = HellionStrings.Tabs_Presets_Beginner,
+ SelectedChannels = new Dictionary
+ {
+ [ChatType.NoviceNetwork] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.NoviceNetworkSystem] = (ChatSourceExt.All, ChatSourceExt.All),
+ },
+ Channel = InputChannel.NoviceNetwork,
+ };
+
+ public static Tab HellionSystem => new()
+ {
+ Name = HellionStrings.Tabs_Presets_System,
+ SelectedChannels = new Dictionary
+ {
+ [ChatType.Debug] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Urgent] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Notice] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.System] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Error] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Echo] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.GatheringSystem] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.NoviceNetworkSystem] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.NpcAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.FreeCompanyLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.PvpTeamLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.RetainerSale] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Progress] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.RandomNumber] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Orchestrion] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.MessageBook] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Alarm] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Sign] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.GlamourNotifications] = (ChatSourceExt.All, ChatSourceExt.All),
+ },
+ };
+
+ public static Tab HellionLinkshell => new()
+ {
+ Name = HellionStrings.Tabs_Presets_Linkshell,
+ SelectedChannels = new Dictionary
+ {
+ [ChatType.Linkshell1] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Linkshell2] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Linkshell3] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Linkshell4] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Linkshell5] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Linkshell6] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Linkshell7] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.Linkshell8] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossLinkshell1] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossLinkshell2] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossLinkshell3] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossLinkshell4] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossLinkshell5] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossLinkshell6] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossLinkshell7] = (ChatSourceExt.All, ChatSourceExt.All),
+ [ChatType.CrossLinkshell8] = (ChatSourceExt.All, ChatSourceExt.All),
+ },
+ };
+
public static Dictionary MostlyPlayer => new()
{
// Special
diff --git a/repo.json b/repo.json
index 5d7f239..738e50d 100644
--- a/repo.json
+++ b/repo.json
@@ -3,7 +3,7 @@
"Author": "JonKazama-Hellion",
"Name": "Hellion Chat",
"InternalName": "HellionChat",
- "AssemblyVersion": "0.4.0.0",
+ "AssemblyVersion": "0.5.0.0",
"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",
@@ -20,12 +20,12 @@
"CanUnloadAsync": false,
"LoadPriority": 0,
"Punchline": "Chat 2 with privacy controls aligned to EU, US and JP rules",
- "Changelog": "**Hellion Chat 0.4.0 — Auto-Tell-Tabs**\n\nAuto-Tell-Tabs lets you turn each /tell into a session-only tab\ndedicated to that conversation partner. The original use case is\nthe FFXIV club greeter who has to track 5–15 parallel \"hi, welcome\"\nexchanges; everyone else can disable the feature in one click and\ngo back to a single Tell Exclusive tab.\n\nWhat lands in this release:\n\n- Auto-spawn temp tab \"Name@World\" on /tell (incoming and outgoing)\n- Tab limit (default 15, range 1–50) with LRU drop that prefers\n greeted tabs first, then sorts by last activity\n- History preload from the local message store (default 20 tells,\n range 0–100) with a \"— Earlier conversations —\" separator above\n the live tell that triggered the spawn\n- Optional \"mark as greeted\" toggle button (off by default,\n greeter-specific) that dims the tab name and lets you flip the\n status\n- Section header \"Active Tells (n)\" or compact-mode separator in\n the sidebar between persistent tabs and the temp tabs\n- Settings UI under Chat (toggle / limit / compact / greeted-toggle)\n and Privacy (history preload count), with hover-tooltip help\n markers replacing the previous wall-of-text descriptions for the\n new sections\n- Save and load filters strip temp tabs from the on-disk config so\n a crash or a sidebar-mode toggle never persists or wipes them\n\nCompatibility note: if XIV Messanger or another plugin is\nsuppressing direct messages, disable its \"Suppress DMs\" option so\nHellion Chat can receive tells and open the auto tabs.\n\nConfiguration version bumps from 8 to 9. Existing users get a one-\nshot notification on the first start, defaults are seeded by\nproperty initializers, persistent tabs are untouched.\n\nThe vertical sidebar tab view becomes the default for fresh\ninstalls; existing users keep their saved preference.\n\nInspired by the per-sender tab pattern in XIV InstantMessenger\n(Limiana, AGPL-3.0). No code was ported across the licence\nboundary; only the architectural concept influenced this design.\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n**Hellion Chat 0.3.1 — Upstream emote regression fix**\n\nCherry-picks Infi's upstream commit ff899ff \"Fix a regression\nfrom API 15 updates\" which changes the BetterTTV emote DTOs\n(Emote and Top100) from public fields to public properties.\nSystem.Text.Json under the API 15 toolchain only honours the\n[JsonPropertyName] attribute on properties, so the previous\nfield-based version deserialised every fetched emote into empty\ndefault values. Result: BetterTTV emotes were silently broken\non fresh installs. The fix is six lines and applies cleanly on\ntop of our defensive null-check from earlier; the EmoteCache\npath-traversal hardening from 0.3.0 stays as it is.\n\nAuthorship of the fix is preserved with git cherry-pick -x, so\nInfi shows up as the author on the commit. Thanks to him for\ncatching it in the upstream codebase.\n\n**Hellion Chat 0.3.0 — Audit hardening, brand sweep and rebrand of slash commands**\n\nThis release closes the remaining audit follow-ups from the\n0.2.0 cleanup and finishes turning Hellion Chat into a properly\nbranded fork rather than a Chat 2 with a different name.\n\nSlash commands have been renamed across the board so they no\nlonger collide with the upstream plugin and tell you which\nplugin owns them at a glance:\n\n- /chat2 becomes /hellion\n- /chat2Viewer becomes /hellionView\n- /clearlog2 becomes /clearhellion\n- /chat2Debugger becomes /hellionDebugger (internal)\n- /chat2SeString becomes /hellionSeString (internal)\n\nThis is a breaking change for anyone with macros bound to the\nold command names. The upstream Chat 2 commands keep working\nif you also have that plugin installed.\n\nPrivacy and storage hardening based on the post-0.2.0 audit:\n\n- Privacy filter master switch now states explicitly that the\n filter only governs storage, not the live chat log\n- Emote cache refuses to write outside its own directory if a\n third-party API ever returns a path that escapes\n- Retention sweep is serialised so the 24h auto-sweep and the\n manual button cannot launch in parallel and race for the\n SQLite connection\n- DbViewer paging uses an int constant and the matching SQL\n parameter name (the upstream code passed a float and a name\n without the parameter prefix; both worked in practice but\n were inconsistent)\n\nVisual identity now matches the Hellion Online Media website:\n\n- Theme palette switched to Arctic Cyan plus Ember Orange,\n matching the website's BRANDING.md tokens\n- Active tabs and window title bars use a brand-color-dark teal\n variation as identity colour, replacing the previous slate\n violet that did not appear in the brand\n- Resize grips and scrollbar grabs picked up Ember Orange\n instead of industrial amber on hover and active states\n\nAbout tab rewritten and properly localised:\n\n- New \"Why this fork exists\" block sets out the mission in\n neutral terms, framing Chat 2's full-history default as the\n right one for most users while explaining the narrower\n default footprint this fork chose\n- All Hellion-specific About copy now lives in HellionStrings\n in EN and DE, so German users see the Hellion sections in\n German rather than the upstream English fallback\n- Webinterface absence is described as a focus mismatch\n (different use case, substantial rebuild) rather than as\n a security issue with the upstream code\n- Translator list at the bottom of the About tab is reachable\n again on smaller settings windows\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n**Hellion Chat 0.2.0 — Webinterface removed**\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.5.0 — Settings UX polish**\n\nThe settings window has been pulled apart and rebuilt around eight\nthemed tabs instead of the twelve organic ones it grew into.\nSettings now sit where they belong and the wall-of-text descriptions\nhave been replaced with hover help markers across every section.\n\nWhat changed in this release:\n\n- Twelve tabs collapsed into eight: General, Appearance, Window,\n Chat, Tabs, Privacy, Database and Information\n- Theme and font controls moved out of the Privacy tab into\n Appearance where they belong\n- Auto-Tell-Tabs settings, message preview and emote controls now\n live under one Chat tab with collapsible sections\n- About and Changelog merged into a single Information tab\n- Disabled settings keep their tooltip help marker visible so you\n can still read why an option is greyed out\n- Section headings start collapsed by default, the same pattern\n used for the Auto-Tell-Tabs preload section in 0.4.0\n\nConfiguration version bumps from 9 to 10 as a wipe migration. The\nold config file is copied to HellionChat.json.pre-v10-backup before\nthe new defaults are written, so you can restore your previous\nsetup by hand if anything looks off. A one-shot notification on\nfirst start explains the reset.\n\nNo changes to message storage, retention sweep, the privacy filter\nor the export pipeline. Tabs and chat history are untouched by the\nmigration.\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n**Hellion Chat 0.4.0 — Auto-Tell-Tabs**\n\nAuto-Tell-Tabs lets you turn each /tell into a session-only tab\ndedicated to that conversation partner. The original use case is\nthe FFXIV club greeter who has to track 5–15 parallel \"hi, welcome\"\nexchanges; everyone else can disable the feature in one click and\ngo back to a single Tell Exclusive tab.\n\nWhat lands in this release:\n\n- Auto-spawn temp tab \"Name@World\" on /tell (incoming and outgoing)\n- Tab limit (default 15, range 1–50) with LRU drop that prefers\n greeted tabs first, then sorts by last activity\n- History preload from the local message store (default 20 tells,\n range 0–100) with a \"— Earlier conversations —\" separator above\n the live tell that triggered the spawn\n- Optional \"mark as greeted\" toggle button (off by default,\n greeter-specific) that dims the tab name and lets you flip the\n status\n- Section header \"Active Tells (n)\" or compact-mode separator in\n the sidebar between persistent tabs and the temp tabs\n- Settings UI under Chat (toggle / limit / compact / greeted-toggle)\n and Privacy (history preload count), with hover-tooltip help\n markers replacing the previous wall-of-text descriptions for the\n new sections\n- Save and load filters strip temp tabs from the on-disk config so\n a crash or a sidebar-mode toggle never persists or wipes them\n\nCompatibility note: if XIV Messanger or another plugin is\nsuppressing direct messages, disable its \"Suppress DMs\" option so\nHellion Chat can receive tells and open the auto tabs.\n\nConfiguration version bumps from 8 to 9. Existing users get a one-\nshot notification on the first start, defaults are seeded by\nproperty initializers, persistent tabs are untouched.\n\nThe vertical sidebar tab view becomes the default for fresh\ninstalls; existing users keep their saved preference.\n\nInspired by the per-sender tab pattern in XIV InstantMessenger\n(Limiana, AGPL-3.0). No code was ported across the licence\nboundary; only the architectural concept influenced this design.\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n**Hellion Chat 0.3.1 — Upstream emote regression fix**\n\nCherry-picks Infi's upstream commit ff899ff \"Fix a regression\nfrom API 15 updates\" which changes the BetterTTV emote DTOs\n(Emote and Top100) from public fields to public properties.\nSystem.Text.Json under the API 15 toolchain only honours the\n[JsonPropertyName] attribute on properties, so the previous\nfield-based version deserialised every fetched emote into empty\ndefault values. Result: BetterTTV emotes were silently broken\non fresh installs. The fix is six lines and applies cleanly on\ntop of our defensive null-check from earlier; the EmoteCache\npath-traversal hardening from 0.3.0 stays as it is.\n\nAuthorship of the fix is preserved with git cherry-pick -x, so\nInfi shows up as the author on the commit. Thanks to him for\ncatching it in the upstream codebase.\n\n**Hellion Chat 0.3.0 — Audit hardening, brand sweep and rebrand of slash commands**\n\nThis release closes the remaining audit follow-ups from the\n0.2.0 cleanup and finishes turning Hellion Chat into a properly\nbranded fork rather than a Chat 2 with a different name.\n\nSlash commands have been renamed across the board so they no\nlonger collide with the upstream plugin and tell you which\nplugin owns them at a glance:\n\n- /chat2 becomes /hellion\n- /chat2Viewer becomes /hellionView\n- /clearlog2 becomes /clearhellion\n- /chat2Debugger becomes /hellionDebugger (internal)\n- /chat2SeString becomes /hellionSeString (internal)\n\nThis is a breaking change for anyone with macros bound to the\nold command names. The upstream Chat 2 commands keep working\nif you also have that plugin installed.\n\nPrivacy and storage hardening based on the post-0.2.0 audit:\n\n- Privacy filter master switch now states explicitly that the\n filter only governs storage, not the live chat log\n- Emote cache refuses to write outside its own directory if a\n third-party API ever returns a path that escapes\n- Retention sweep is serialised so the 24h auto-sweep and the\n manual button cannot launch in parallel and race for the\n SQLite connection\n- DbViewer paging uses an int constant and the matching SQL\n parameter name (the upstream code passed a float and a name\n without the parameter prefix; both worked in practice but\n were inconsistent)\n\nVisual identity now matches the Hellion Online Media website:\n\n- Theme palette switched to Arctic Cyan plus Ember Orange,\n matching the website's BRANDING.md tokens\n- Active tabs and window title bars use a brand-color-dark teal\n variation as identity colour, replacing the previous slate\n violet that did not appear in the brand\n- Resize grips and scrollbar grabs picked up Ember Orange\n instead of industrial amber on hover and active states\n\nAbout tab rewritten and properly localised:\n\n- New \"Why this fork exists\" block sets out the mission in\n neutral terms, framing Chat 2's full-history default as the\n right one for most users while explaining the narrower\n default footprint this fork chose\n- All Hellion-specific About copy now lives in HellionStrings\n in EN and DE, so German users see the Hellion sections in\n German rather than the upstream English fallback\n- Webinterface absence is described as a focus mismatch\n (different use case, substantial rebuild) rather than as\n a security issue with the upstream code\n- Translator list at the bottom of the About tab is reachable\n again on smaller settings windows\n\nBased on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).\n\n**Hellion Chat 0.2.0 — Webinterface removed**\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.4.0/latest.zip",
- "DownloadLinkUpdate": "https://github.com/JonKazama-Hellion/HellionChat/releases/download/v0.4.0/latest.zip",
- "DownloadLinkTesting": "https://github.com/JonKazama-Hellion/HellionChat/releases/download/v0.4.0/latest.zip",
- "TestingAssemblyVersion": "0.4.0.0",
+ "DownloadLinkInstall": "https://github.com/JonKazama-Hellion/HellionChat/releases/download/v0.5.0/latest.zip",
+ "DownloadLinkUpdate": "https://github.com/JonKazama-Hellion/HellionChat/releases/download/v0.5.0/latest.zip",
+ "DownloadLinkTesting": "https://github.com/JonKazama-Hellion/HellionChat/releases/download/v0.5.0/latest.zip",
+ "TestingAssemblyVersion": "0.5.0.0",
"IconUrl": "https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/ChatTwo/images/icon.png",
"ImageUrls": [],
"DownloadCount": 0,