feat(branding): replace ASCII fox banner with embedded image

This commit is contained in:
2026-05-21 18:39:57 +02:00
parent ce7dda9e48
commit 4f6c916bd9
9 changed files with 95 additions and 105 deletions
+22
View File
@@ -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
);
}
+3 -10
View File
@@ -1,25 +1,18 @@
namespace HellionChat.Branding; namespace HellionChat.Branding;
// Lazy-loaded provenance art that ships embedded with the DLL. Two // Lazy-loaded ASCII art that ships embedded with the DLL.
// variants:
// //
// - 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 // - FoxMini: the four-line fox-head + curly-tail that gets stitched
// into the DI-logger bootstrap line so an xllog reader sees the // into the DI-logger bootstrap line so an xllog reader sees the
// same signature on every plugin load. // same signature on every plugin load.
// //
// Both files live as embedded resources under HellionChat.Branding.* so // The file lives as an embedded resource under HellionChat.Branding.* so
// the plugin DLL is self-contained no on-disk asset lookup that could // the plugin DLL is self-contained; no on-disk asset lookup that could
// silently miss after a partial deploy. // silently miss after a partial deploy.
internal static class HellionForgeAscii internal static class HellionForgeAscii
{ {
private static string? _foxBanner;
private static string? _foxMini; 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"); public static string FoxMini => _foxMini ??= Load("HellionChat.Branding.fox-mini.txt");
private static string Load(string resourceName) private static string Load(string resourceName)
+2 -2
View File
@@ -58,8 +58,8 @@
<EmbeddedResource Include="Resources\Inter-OFL.txt"> <EmbeddedResource Include="Resources\Inter-OFL.txt">
<LogicalName>Inter-OFL.txt</LogicalName> <LogicalName>Inter-OFL.txt</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Resources\Branding\fox-banner.txt"> <EmbeddedResource Include="Resources\Branding\fox-banner.png">
<LogicalName>HellionChat.Branding.fox-banner.txt</LogicalName> <LogicalName>HellionChat.Branding.fox-banner.png</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Resources\Branding\fox-mini.txt"> <EmbeddedResource Include="Resources\Branding\fox-mini.txt">
<LogicalName>HellionChat.Branding.fox-mini.txt</LogicalName> <LogicalName>HellionChat.Branding.fox-mini.txt</LogicalName>
+1
View File
@@ -337,6 +337,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
new SelfTests.FontPushSmokeStep(this), new SelfTests.FontPushSmokeStep(this),
new SelfTests.WizardStateSmokeStep(this), new SelfTests.WizardStateSmokeStep(this),
new SelfTests.QuickPickerSelfTestStep(this), new SelfTests.QuickPickerSelfTestStep(this),
new SelfTests.FoxBannerTextureSmokeStep(this),
]); ]);
// Re-surface the wizard for existing users when a major UX // Re-surface the wizard for existing users when a major UX
Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

@@ -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;
@@ -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() { }
}
+14 -17
View File
@@ -1,6 +1,7 @@
using System.Globalization; using System.Globalization;
using System.Numerics; using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using HellionChat.Branding; using HellionChat.Branding;
@@ -171,24 +172,20 @@ public sealed class FirstRunWizard : Window
ImGui.TextUnformatted(HellionStrings.Wizard_Step1_Title); ImGui.TextUnformatted(HellionStrings.Wizard_Step1_Title);
ImGui.Spacing(); ImGui.Spacing();
// Banner is opt-in: the full silhouette dominates the wizard window // Fox-banner image: the embedded Hellion Forge fox artwork, replacing
// at the default size, so the TreeNode is folded by default and the // the ASCII FoxBanner. Async load renders nothing until the texture is
// onboarding copy stays the primary focus. Mirrors the pre-rewrite // ready (a few frames). No theme tinting, the fox keeps its identity.
// collapsible anchor from v1.5.1. var banner = FoxBannerTexture.Shared.GetWrapOrDefault();
using (var tree = ImRaii.TreeNode("Hellion Forge")) if (banner is not null)
{ {
if (tree.Success) var height = 120f * ImGuiHelpers.GlobalScale;
{ var width = height * banner.Size.X / banner.Size.Y;
using (Plugin.Interface.UiBuilder.MonoFontHandle.Push()) // Anchor the centering offset to the current cursor X. Avoids a
{ // negative position if the banner is ever wider than the region.
// CalcTextSize must run inside the MonoFont push so the var offsetX = (ImGui.GetContentRegionAvail().X - width) * 0.5f;
// measurement matches the glyph width actually used for if (offsetX > 0f)
// rendering. ImGui.SetCursorPosX(ImGui.GetCursorPosX() + offsetX);
var bannerSize = ImGui.CalcTextSize(HellionForgeAscii.FoxBanner); ImGui.Image(banner.Handle, new Vector2(width, height));
ImGui.SetCursorPosX((ImGui.GetContentRegionAvail().X - bannerSize.X) * 0.5f);
ImGui.TextUnformatted(HellionForgeAscii.FoxBanner);
}
}
} }
ImGui.Spacing(); ImGui.Spacing();
+6 -8
View File
@@ -1,3 +1,4 @@
using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
@@ -68,18 +69,15 @@ internal sealed class Information : ISettingsTab
DrawChangelogSection(); 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() private void DrawHellionForgeSection()
{ {
using var tree = ImRaii.TreeNode("Hellion Forge"); var banner = FoxBannerTexture.Shared.GetWrapOrDefault();
if (!tree.Success) if (banner is null)
return; return;
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false)) var height = 120f * ImGuiHelpers.GlobalScale;
using (Plugin.Interface.UiBuilder.MonoFontHandle.Push()) var width = height * banner.Size.X / banner.Size.Y;
ImGui.TextUnformatted(HellionForgeAscii.FoxBanner); ImGui.Image(banner.Handle, new Vector2(width, height));
} }
private void DrawVersionInfoSection() private void DrawVersionInfoSection()