From 490da3e908bb3cd4c7115564e9ae1135fffa767a Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 26 May 2026 20:20:53 +0200 Subject: [PATCH] feat(settings): add About tab with brand, links, credits --- HellionChat/Branding/BrandingLinks.cs | 3 + HellionChat/Configuration.cs | 2 +- HellionChat/PluginHostFactory.cs | 4 + .../Ui/Components/Settings/Tabs/AboutTab.cs | 117 ++++++++++++++++++ HellionChat/Ui/Windows/SettingsWindow.cs | 6 + 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 HellionChat/Ui/Components/Settings/Tabs/AboutTab.cs diff --git a/HellionChat/Branding/BrandingLinks.cs b/HellionChat/Branding/BrandingLinks.cs index f3f3a08..7b62899 100644 --- a/HellionChat/Branding/BrandingLinks.cs +++ b/HellionChat/Branding/BrandingLinks.cs @@ -10,6 +10,8 @@ internal static class BrandingLinks public const string HellionForgeGitea = "https://gitea.hellion-forge.cloud/Hellion-Forge"; public const string HellionChatRepo = "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat"; + public const string HellionChatCustomRepoManifest = + "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json"; public const string HellionForgeWebsite = "https://hellion-forge.cloud"; public const string HellionMediaWebsite = "https://hellion-media.de/de"; @@ -26,6 +28,7 @@ internal static class BrandingLinks HellionForgeDiscordInvite, HellionForgeGitea, HellionChatRepo, + HellionChatCustomRepoManifest, HellionForgeWebsite, HellionMediaWebsite ); diff --git a/HellionChat/Configuration.cs b/HellionChat/Configuration.cs index ac6f7da..4e62695 100755 --- a/HellionChat/Configuration.cs +++ b/HellionChat/Configuration.cs @@ -35,7 +35,7 @@ public class ConfigKeyBind [Serializable] public class Configuration : IPluginConfiguration { - private const int LatestVersion = 20; + internal const int LatestVersion = 20; public int Version { get; set; } = LatestVersion; diff --git a/HellionChat/PluginHostFactory.cs b/HellionChat/PluginHostFactory.cs index ece7946..39a2f71 100644 --- a/HellionChat/PluginHostFactory.cs +++ b/HellionChat/PluginHostFactory.cs @@ -183,6 +183,10 @@ internal static class PluginHostFactory services.AddSingleton(sp => new Ui.Components.Settings.Tabs.DataPrivacyTab( sp.GetRequiredService() )); + services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AboutTab( + sp.GetRequiredService(), + sp.GetRequiredService>() + )); services.AddSingleton(sp => new Ui.Components.StatusBar( sp.GetRequiredService(), sp.GetRequiredService() diff --git a/HellionChat/Ui/Components/Settings/Tabs/AboutTab.cs b/HellionChat/Ui/Components/Settings/Tabs/AboutTab.cs new file mode 100644 index 0000000..2506695 --- /dev/null +++ b/HellionChat/Ui/Components/Settings/Tabs/AboutTab.cs @@ -0,0 +1,117 @@ +using System.Diagnostics; +using System.Reflection; +using Dalamud.Bindings.ImGui; +using Dalamud.Interface; +using HellionChat.Branding; +using Microsoft.Extensions.Logging; + +namespace HellionChat.Ui.Components.Settings.Tabs; + +internal sealed class AboutTab +{ + private readonly FontManager _fonts; + private readonly ILogger _logger; + + public AboutTab(FontManager fonts, ILogger logger) + { + _fonts = fonts; + _logger = logger; + } + + public void Draw() + { + DrawPluginInfo(); + DrawSectionHeader("Brand"); + DrawBrand(); + DrawSectionHeader("Links"); + DrawLinks(); + DrawSectionHeader("Credits"); + DrawCredits(); + DrawSectionHeader("License"); + DrawLicense(); + } + + // Dalamud.Bindings.ImGui does not expose ImGui.SeparatorText, so we use + // the Separator + TextUnformatted idiom the rest of the codebase uses. + private static void DrawSectionHeader(string title) + { + ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + ImGui.TextUnformatted(title); + } + + private static void DrawPluginInfo() + { + var version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown"; + ImGui.TextUnformatted("HellionChat"); + // Schema version pulled from Configuration.LatestVersion (single source of + // truth) so future schema bumps don't have to touch this string. + ImGui.TextDisabled($"Version {version} · Schema v{Configuration.LatestVersion}"); + } + + private void DrawBrand() + { + using (_fonts.FontAwesome.Push()) + { + // ImGui's PushStyleColor uint API is ABGR-native. 0xFF0C41C2u packs + // as A=FF B=0C G=41 R=C2 → RGB #C2410C (Forge-Bronze). No swap needed + // because the literal is already ABGR; theme-sourced uints from + // ThemeColors.* (RGBA) would need ColourUtil.RgbaToAbgr first. + ImGui.PushStyleColor(ImGuiCol.Text, 0xFF0C41C2u); + ImGui.TextUnformatted(FontAwesomeIcon.Hammer.ToIconString()); + ImGui.PopStyleColor(); + } + ImGui.SameLine(); + ImGui.TextUnformatted("by Hellion Online Media"); + ImGui.TextDisabled("Hellion Forge — Modding Division"); + } + + private void DrawLinks() + { + DrawLinkButton("Discord (Hellion Forge)", BrandingLinks.HellionForgeDiscordInvite); + DrawLinkButton("Gitea repository", BrandingLinks.HellionChatRepo); + DrawLinkButton("Custom repo manifest", BrandingLinks.HellionChatCustomRepoManifest); + } + + // URLs in v1.7.0 are exclusively hardcoded BrandingLinks.* constants — + // Process.Start with UseShellExecute=true is safe under that constraint. + // If a future cycle ever feeds user-supplied URLs here, add an https/http + // allow-list filter via Uri.TryCreate before Process.Start; without it + // UseShellExecute would happily launch file:// or shell-protocol handlers. + private void DrawLinkButton(string label, string url) + { + if (ImGui.Button(label)) + { + try + { + Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Could not open {Url}, copying to clipboard instead", url); + ImGui.SetClipboardText(url); + } + } + ImGui.SameLine(); + if (ImGui.SmallButton($"Copy##{url}")) + { + ImGui.SetClipboardText(url); + } + } + + private static void DrawCredits() + { + ImGui.BulletText("ChatTwo — original maintainer Anna Clemens, GPL-3.0"); + ImGui.BulletText("Dalamud — goatcorp, AGPL-3.0"); + ImGui.BulletText("ImGui — Omar Cornut, MIT"); + ImGui.BulletText("FontAwesome — Fonticons, OFL-1.1"); + ImGui.BulletText("Inter — rsms, OFL-1.1"); + ImGui.BulletText("NotoSansCJK — Google, OFL-1.1"); + } + + private static void DrawLicense() + { + ImGui.TextUnformatted("GPL-3.0-or-later"); + } +} diff --git a/HellionChat/Ui/Windows/SettingsWindow.cs b/HellionChat/Ui/Windows/SettingsWindow.cs index a52880c..82ec235 100644 --- a/HellionChat/Ui/Windows/SettingsWindow.cs +++ b/HellionChat/Ui/Windows/SettingsWindow.cs @@ -25,6 +25,7 @@ internal sealed class SettingsWindow : Window private readonly WindowTab _window; private readonly ChannelsTab _channels; private readonly DataPrivacyTab _dataPrivacy; + private readonly AboutTab _about; public SettingsWindow( Plugin plugin, @@ -39,6 +40,7 @@ internal sealed class SettingsWindow : Window WindowTab window, ChannelsTab channels, DataPrivacyTab dataPrivacy, + AboutTab about, ILoggerFactory loggerFactory ) : base($"{Language.Settings_Title.Format(Plugin.PluginName)}###chat2-settings") @@ -55,6 +57,7 @@ internal sealed class SettingsWindow : Window _window = window; _channels = channels; _dataPrivacy = dataPrivacy; + _about = about; _ = loggerFactory; Size = new Vector2(720, 540); @@ -98,6 +101,9 @@ internal sealed class SettingsWindow : Window case "data-privacy": _dataPrivacy.Draw(); break; + case "about": + _about.Draw(); + break; case "appearance": _appearance.Draw(); break;