diff --git a/HellionChat/Branding/FoxBannerTexture.cs b/HellionChat/Branding/FoxBannerTexture.cs
new file mode 100644
index 0000000..353dc31
--- /dev/null
+++ b/HellionChat/Branding/FoxBannerTexture.cs
@@ -0,0 +1,22 @@
+using Dalamud.Interface.Textures;
+
+namespace HellionChat.Branding;
+
+// UI sibling of HellionForgeAscii.FoxMini: the embedded Hellion Forge fox
+// banner PNG. Uses ITextureProvider.GetFromManifestResource, a "Get" shared
+// texture, so Dalamud owns the cache and lifetime. No manual dispose, no async
+// handling in the plugin. Static to mirror HellionForgeAscii (zero injectable
+// deps; Plugin.TextureProvider is a static [PluginService]).
+internal static class FoxBannerTexture
+{
+ private const string ResourceName = "HellionChat.Branding.fox-banner.png";
+
+ // Resolved fresh on every access. Dalamud keeps the shared texture cached
+ // internally and decodes it asynchronously, so GetWrapOrDefault() returns
+ // null for the first few frames until the decode finishes.
+ public static ISharedImmediateTexture Shared =>
+ Plugin.TextureProvider.GetFromManifestResource(
+ typeof(FoxBannerTexture).Assembly,
+ ResourceName
+ );
+}
diff --git a/HellionChat/Branding/HellionForgeAscii.cs b/HellionChat/Branding/HellionForgeAscii.cs
index da98572..7bbf04d 100644
--- a/HellionChat/Branding/HellionForgeAscii.cs
+++ b/HellionChat/Branding/HellionForgeAscii.cs
@@ -1,25 +1,18 @@
namespace HellionChat.Branding;
-// Lazy-loaded provenance art that ships embedded with the DLL. Two
-// variants:
+// Lazy-loaded ASCII art that ships embedded with the DLL.
//
-// - FoxBanner: the full-size silhouette with "Hellion Forge" inside
-// the body — rendered in the first-run wizard and the Information
-// tab as a small "about the makers" anchor.
// - FoxMini: the four-line fox-head + curly-tail that gets stitched
// into the DI-logger bootstrap line so an xllog reader sees the
// same signature on every plugin load.
//
-// Both files live as embedded resources under HellionChat.Branding.* so
-// the plugin DLL is self-contained — no on-disk asset lookup that could
+// The file lives as an embedded resource under HellionChat.Branding.* so
+// the plugin DLL is self-contained; no on-disk asset lookup that could
// silently miss after a partial deploy.
internal static class HellionForgeAscii
{
- private static string? _foxBanner;
private static string? _foxMini;
- public static string FoxBanner => _foxBanner ??= Load("HellionChat.Branding.fox-banner.txt");
-
public static string FoxMini => _foxMini ??= Load("HellionChat.Branding.fox-mini.txt");
private static string Load(string resourceName)
diff --git a/HellionChat/HellionChat.csproj b/HellionChat/HellionChat.csproj
index a9071a9..97467fd 100644
--- a/HellionChat/HellionChat.csproj
+++ b/HellionChat/HellionChat.csproj
@@ -58,8 +58,8 @@
Inter-OFL.txt
-
- HellionChat.Branding.fox-banner.txt
+
+ HellionChat.Branding.fox-banner.png
HellionChat.Branding.fox-mini.txt
diff --git a/HellionChat/Plugin.cs b/HellionChat/Plugin.cs
index aa739d9..67e2399 100755
--- a/HellionChat/Plugin.cs
+++ b/HellionChat/Plugin.cs
@@ -337,6 +337,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
new SelfTests.FontPushSmokeStep(this),
new SelfTests.WizardStateSmokeStep(this),
new SelfTests.QuickPickerSelfTestStep(this),
+ new SelfTests.FoxBannerTextureSmokeStep(this),
]);
// Re-surface the wizard for existing users when a major UX
diff --git a/HellionChat/Resources/Branding/fox-banner.png b/HellionChat/Resources/Branding/fox-banner.png
new file mode 100644
index 0000000..135acf6
Binary files /dev/null and b/HellionChat/Resources/Branding/fox-banner.png differ
diff --git a/HellionChat/Resources/Branding/fox-banner.txt b/HellionChat/Resources/Branding/fox-banner.txt
deleted file mode 100644
index 8351a91..0000000
--- a/HellionChat/Resources/Branding/fox-banner.txt
+++ /dev/null
@@ -1,68 +0,0 @@
- .:;+xXXX$$$$$$$$XXx+;:
-.X$+ .;+X$$$$$$$$$$$$$$$$$$$$$$$$$$$x:
-;$xx$$X+:... .....::+X$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$;.
-X$; .:+xXXX$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X:
-$$; :++xX$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X;
-$$x. .+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X.
-x$$; ;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X+;::::::;x$$$$$:
-:$$$; .+$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X+:. .+$$$$$$$$$X+;;:
- ;$$$+. :X$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X;: :$$$$$$$$$$$$$$$$X;.
- .+$$$X: ..;X$$$$$$$$$$$$$$$$$$$$$$$$$$X;.. :$$$$$$$$$$$$$$$$$$$$X:
- ;$$$$$X+::::+X$$$$$$$$$$$$$$$$$$$$$X;. .$$$$$$$$$$$$$$$$$$$$$$$X;
- +$$$$$$$$$$$$$$$$$$$$$$$$$$$$X+: Hellion Forge x$$$$$$$$$$$$$$$$$$$$$$$$$X:
- .;x$$$$$$$$$$$$$$$$$$$$$x;: .X$$$$$$$$$$$$$$$$$$$$$$$$$$$+
- .;+$$$$$$$$$$X+;:.. .X$$$$$$$$$$$$$$$$$$$$$$$$$$$$+
- .X$$$$$$$$$$$$$$$$$$$$$$$$$$$$$;
- .X$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X
- x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X
- ;$$$$$$xx$$$$$$$$$$$$$$$$$$$$$x
- .$$$$$$x+$$$$$$$$$$$$$$$$$$$$$x
- :+X$$$$$$X;$$$$$$$$$$$$$$$$$$$$$$:
- ;$$$$$$$$$$;$$$$$$$$$$$$$$$$$$$$$$X.
- +$$$$$$$$$$;x$$$$$$$$$$$$$$$$$$$$$$+
- x$$$$$$$$$$:$$$$$$$$$$$$$$$$$$$$$$X:
- .X$$$$$$$$$.:$$$$$$$$$$$$$$$$$$$$$$;
- :X$$X;;;;: .$$$$$$$$$$$$$$$$$$$$$$X.
- .$$$$X .$$$$$$$$$$$$$$$$$$$$$$$:
- .$$$$+ .X$$$$$$$$$$$$$$$$$$$$$$;
- ;$$$$: .X$$$$$$$$$$$$$$$$$$$$$$x
- :X$$$+ .$$$$$$$$$$$$$$$$$$$$$$$X
- +$$$x :$$$$$$$$$$$$$$$$$$$$$$$X
- ;$$X: $$$$$$$$$$$$$$$$$$$$$$$$X
- x$$$$$$$$$$$$$$$$$$$$$$$$X
- +$$$$$$$$$$$$$$$$$$$$$$$$$+
- .+$$$$$$$$$$$$$$$$$$$$$$$$$$;
- . ;$$$$$$$$$$$$$$$$$$$$$$$$$$$$:
- :X$x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
- .XX$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+
- ;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+$;
- .. ++X$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$:+$:
- :$$+. ;$$$$$$$$$$$$$$X$$$$$$$$$$$$$$$$$$$$$;:$$+
- .x+X$X: X$$$$$$$$$$x::;:;$$$$$$$$$$$$$$$$$$X: ;$X.
- :X.x$$$:.::::::;x+:X$$$$;$$$$$$$$$$$$$$$$$$: :X;
- :x.x$$$$$$$$$$$$$$$$$;;$:$$$$$$$$$$$$$$$$$: :$+
- :Xx$$$$$$$$$$$$$$$$$: ;X;$$$$$$$$$$$$$$$$: .+$$;
- ;$$$$$$$$$$$$$$$$$$; .X+X$$$$$$$$$$$$$$$+ .+$+.
- +$$$$$$$$$$$$$$$$$$$$$$$;+$$$$$$$$$$$$$$X: .+X:
- +$$$$$$$$$$$$$$$$$$$$$$$$$+:$$$$$$$$$$$$$+.+$+.
- ;$$$$$$$$$$$$$$$$$$$$$$$$$$$X;$$$$$$$$$$$$$$X:
- +X: .:X$$$$$$$$x+++x$$$$$$$$;:X$$$$$$$$$$$X:
- :x.;$;+$$$$$:. :X$$$$X :$$$$$$$$$$X:
- ;x :X$$$; .x$$x X$$; .:+.$$$$$$$$$$x
- xx.X$$X: X$;.:$X:.X$$$$$$$$$:
- +$$$$X. ;$;::: .$$$$$$$$$:
- ;$$$; :+X$$$$XX$; X$$$$$$$$:
- ;$$X: .:x$x$$$$$X. x$$$$$$$$:
- :X$X: :+x; :$$$$$: +$$$$$$$X:
- :++$X+xXX;. +$$$$. +$$$$$$$+.
- ... .X$$$X. +$$$$$$$:
- ;$$$$; .X$$$$$$x.
- ;$$X; :X$$$$$$;
- ;$$$$$$x.
- .X$$$$$$;
- ;$$$$$$+
- +$$$$$;
- :X$$$$;.
- ;$$$$+.
- .x$$$X:
- .+$$X;
diff --git a/HellionChat/SelfTests/FoxBannerTextureSmokeStep.cs b/HellionChat/SelfTests/FoxBannerTextureSmokeStep.cs
new file mode 100644
index 0000000..45b5f87
--- /dev/null
+++ b/HellionChat/SelfTests/FoxBannerTextureSmokeStep.cs
@@ -0,0 +1,47 @@
+using Dalamud.Bindings.ImGui;
+using Dalamud.Plugin.SelfTest;
+using HellionChat.Branding;
+
+namespace HellionChat.SelfTests;
+
+// Verifies the embedded fox-banner PNG decodes into a usable texture. The load
+// is async, so the step returns Waiting until Dalamud finishes the decode and
+// the self-test runner re-polls. A decode or resource error is a build defect
+// and fails the step hard. The resource lives in the DLL, it cannot be a
+// runtime miss.
+internal sealed class FoxBannerTextureSmokeStep : ISelfTestStep
+{
+ private readonly Plugin plugin;
+
+ public FoxBannerTextureSmokeStep(Plugin plugin)
+ {
+ this.plugin = plugin;
+ }
+
+ public string Name => "Hellion Chat - Fox banner texture smoke";
+
+ public SelfTestStepResult RunStep()
+ {
+ if (!FoxBannerTexture.Shared.TryGetWrap(out var wrap, out var ex))
+ {
+ if (ex is not null)
+ {
+ ImGui.Text($"Fox banner load failed: {ex.Message}");
+ return SelfTestStepResult.Fail;
+ }
+
+ ImGui.Text("Fox banner still loading...");
+ return SelfTestStepResult.Waiting;
+ }
+
+ if (wrap.Size.X <= 0 || wrap.Size.Y <= 0)
+ {
+ ImGui.Text($"Fox banner has degenerate size {wrap.Size}");
+ return SelfTestStepResult.Fail;
+ }
+
+ return SelfTestStepResult.Pass;
+ }
+
+ public void CleanUp() { }
+}
diff --git a/HellionChat/Ui/FirstRunWizard.cs b/HellionChat/Ui/FirstRunWizard.cs
index c991fc3..0d81fc9 100644
--- a/HellionChat/Ui/FirstRunWizard.cs
+++ b/HellionChat/Ui/FirstRunWizard.cs
@@ -1,6 +1,7 @@
using System.Globalization;
using System.Numerics;
using Dalamud.Bindings.ImGui;
+using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using HellionChat.Branding;
@@ -171,24 +172,20 @@ public sealed class FirstRunWizard : Window
ImGui.TextUnformatted(HellionStrings.Wizard_Step1_Title);
ImGui.Spacing();
- // Banner is opt-in: the full silhouette dominates the wizard window
- // at the default size, so the TreeNode is folded by default and the
- // onboarding copy stays the primary focus. Mirrors the pre-rewrite
- // collapsible anchor from v1.5.1.
- using (var tree = ImRaii.TreeNode("Hellion Forge"))
+ // Fox-banner image: the embedded Hellion Forge fox artwork, replacing
+ // the ASCII FoxBanner. Async load renders nothing until the texture is
+ // ready (a few frames). No theme tinting, the fox keeps its identity.
+ var banner = FoxBannerTexture.Shared.GetWrapOrDefault();
+ if (banner is not null)
{
- if (tree.Success)
- {
- using (Plugin.Interface.UiBuilder.MonoFontHandle.Push())
- {
- // CalcTextSize must run inside the MonoFont push so the
- // measurement matches the glyph width actually used for
- // rendering.
- var bannerSize = ImGui.CalcTextSize(HellionForgeAscii.FoxBanner);
- ImGui.SetCursorPosX((ImGui.GetContentRegionAvail().X - bannerSize.X) * 0.5f);
- ImGui.TextUnformatted(HellionForgeAscii.FoxBanner);
- }
- }
+ var height = 120f * ImGuiHelpers.GlobalScale;
+ var width = height * banner.Size.X / banner.Size.Y;
+ // Anchor the centering offset to the current cursor X. Avoids a
+ // negative position if the banner is ever wider than the region.
+ var offsetX = (ImGui.GetContentRegionAvail().X - width) * 0.5f;
+ if (offsetX > 0f)
+ ImGui.SetCursorPosX(ImGui.GetCursorPosX() + offsetX);
+ ImGui.Image(banner.Handle, new Vector2(width, height));
}
ImGui.Spacing();
diff --git a/HellionChat/Ui/SettingsTabs/Information.cs b/HellionChat/Ui/SettingsTabs/Information.cs
index 9a2ba87..774e755 100644
--- a/HellionChat/Ui/SettingsTabs/Information.cs
+++ b/HellionChat/Ui/SettingsTabs/Information.cs
@@ -1,3 +1,4 @@
+using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
@@ -68,18 +69,15 @@ internal sealed class Information : ISettingsTab
DrawChangelogSection();
}
- // Provenance anchor — folded by default so the tab opens to the
- // version-info section as before. Expands to show the full Hellion
- // Forge silhouette in monospace.
private void DrawHellionForgeSection()
{
- using var tree = ImRaii.TreeNode("Hellion Forge");
- if (!tree.Success)
+ var banner = FoxBannerTexture.Shared.GetWrapOrDefault();
+ if (banner is null)
return;
- using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
- using (Plugin.Interface.UiBuilder.MonoFontHandle.Push())
- ImGui.TextUnformatted(HellionForgeAscii.FoxBanner);
+ var height = 120f * ImGuiHelpers.GlobalScale;
+ var width = height * banner.Size.X / banner.Size.Y;
+ ImGui.Image(banner.Handle, new Vector2(width, height));
}
private void DrawVersionInfoSection()