diff --git a/src/WrapTuneMacOS/MainWindow.axaml.cs b/src/WrapTuneMacOS/MainWindow.axaml.cs
index b3681cd..140918a 100644
--- a/src/WrapTuneMacOS/MainWindow.axaml.cs
+++ b/src/WrapTuneMacOS/MainWindow.axaml.cs
@@ -52,6 +52,7 @@ public MainWindow()
ApplyTheme(_theme);
WireDragDrop();
WireDerivedFields();
+ WireSigningRefresh();
UpdateSigningUi();
UpdateFlowUi();
Opened += (_, _) => StartLaunchUpdateCheck();
@@ -382,6 +383,22 @@ private void WireDerivedFields()
TxtOutputFolder.TextChanged += (_, _) => OnPathsChanged();
}
+ /// Every input reads refreshes the
+ /// footer as it changes; no debounce — these checks are cheap and local,
+ /// unlike the path fields' folder-walk.
+ private void WireSigningRefresh()
+ {
+ TextBox[] validated =
+ [
+ TxtPfxPath, TxtSecret,
+ TxtPkcs11Module,
+ TxtTsEndpoint, TxtTsAccount, TxtTsProfile, TxtTsToken,
+ ];
+ foreach (var box in validated)
+ box.TextChanged += (_, _) => UpdateFlowUi();
+ ChkSignAllFiles.IsCheckedChanged += (_, _) => UpdateFlowUi();
+ }
+
private void OnPathsChanged()
{
UpdateFlowUi();
diff --git a/tests/WrapTuneMacOS.App.Tests/SigningRefreshTests.cs b/tests/WrapTuneMacOS.App.Tests/SigningRefreshTests.cs
new file mode 100644
index 0000000..a804680
--- /dev/null
+++ b/tests/WrapTuneMacOS.App.Tests/SigningRefreshTests.cs
@@ -0,0 +1,99 @@
+using Avalonia.Controls;
+using Avalonia.Headless.XUnit;
+using Avalonia.Threading;
+
+namespace WrapTuneMacOS.UiTests;
+
+///
+/// Editing a signing input must refresh the footer validation and the Package
+/// button immediately. Before the fix, only the three path fields re-ran
+/// UpdateFlowUi, so typing the PFX password left the red "password is required"
+/// footer and a disabled Package button until an unrelated event (e.g. flipping
+/// the Sign toggle) forced a refresh.
+///
+public sealed class SigningRefreshTests : IDisposable
+{
+ private readonly string _root;
+
+ public SigningRefreshTests()
+ {
+ _root = Path.Combine(Path.GetTempPath(), "wraptune-signrefresh-" + Guid.NewGuid().ToString("N"));
+ // Two source folders so each test's setup file is unambiguous for the
+ // derived-info auto-detection (which prefers other extensions over .cmd).
+ Directory.CreateDirectory(Path.Combine(_root, "src-ps1"));
+ Directory.CreateDirectory(Path.Combine(_root, "src-cmd"));
+ Directory.CreateDirectory(Path.Combine(_root, "out"));
+ File.WriteAllText(Path.Combine(_root, "src-ps1", "setup.ps1"), "Write-Host hi");
+ File.WriteAllText(Path.Combine(_root, "src-cmd", "setup.cmd"), "@echo off");
+ // Signing validation only checks the .pfx exists; content is never read.
+ File.WriteAllBytes(Path.Combine(_root, "cert.pfx"), [1, 2, 3]);
+ AppSettings.BaseDirOverride = _root;
+ new AppSettings().Save();
+ }
+
+ [AvaloniaFact]
+ public void Typing_the_pfx_password_immediately_arms_package()
+ {
+ var w = NewStagedWindow("src-ps1", "setup.ps1");
+
+ w.FindControl("ChkSignPayload")!.IsChecked = true;
+ w.FindControl("TxtPfxPath")!.Text = Path.Combine(_root, "cert.pfx");
+ Pump();
+ Assert.False(w.FindControl