About box shows only the newest 5 releases (screen-reader crash fix)

The About box's release notes had accumulated the ENTIRE version history back to v1.0 —
~70 KB in a single TextBox — and reading a control value that size crashes some screen
readers (user reports, 2026-08-11). The box now displays only the newest five version
blocks via a pure trim (TrimToLastVersions), ending with a plain pointer to the full
history on the GitHub releases page. The full constant stays in source as the archive.

Gate: new step pins the trim logic on synthetic notes AND the real shipped text (exactly
5 versions, size well under the crashing range — 5,516 chars vs ~70,000) so the crash
can't quietly return as releases accumulate. 73/73. Readme About section updated + MANUAL
regenerated; 5.8 notes mention the change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-08-11 09:35:13 +01:00
co-authored by Claude Fable 5
parent e0dc2fec94
commit 72ebfb50a8
5 changed files with 63 additions and 3 deletions
+32 -1
View File
@@ -28,6 +28,8 @@ internal sealed class AboutDialog : Form
The service's log files are also readable again from every account on the machine, so you can always open them in Notepad if you're curious or need to send one in. And if a service action ever fails, the message now says what actually went wrong instead of just showing a bare error code.
This About box also went on a diet: it now shows the newest five releases instead of the whole history, which had grown large enough to upset some screen readers. The full history is on the RemSound releases page on GitHub.
RemSound v5.7
Stronger security, and it works with every version again.
@@ -1348,6 +1350,35 @@ internal sealed class AboutDialog : Form
on every control, the keyboard shortcuts, and the troubleshooting guide.
""";
/// <summary>How many version blocks the About box actually displays. The full constant above is
/// the archive; the box shows only the newest few. The complete history reached ~70 KB in one
/// TextBox, and reading a control value that size crashes some screen readers (reported
/// 2026-08-11) — the box's job is "what's new", not the whole biography.</summary>
private const int ShownVersions = 5;
/// <summary>The full notes constant, exposed for the self-test that pins the About box's displayed
/// size (the screen-reader-crash regression guard).</summary>
internal static string ReleaseNotesForTest => ReleaseNotes;
/// <summary>Pure, testable: cut <paramref name="notes"/> after its first <paramref name="maxVersions"/>
/// version blocks (lines starting "RemSound v"), appending a plain pointer to the full history.
/// Notes with fewer blocks pass through unchanged.</summary>
internal static string TrimToLastVersions(string notes, int maxVersions)
{
var count = 0;
var lines = notes.Split('\n');
for (var i = 0; i < lines.Length; i++)
{
if (!lines[i].TrimEnd('\r').StartsWith("RemSound v", StringComparison.Ordinal)) continue;
if (++count <= maxVersions) continue;
return string.Join("\n", lines, 0, i).TrimEnd()
+ "\r\n\r\nThat's the newest "
+ maxVersions
+ " releases. Notes for every version back to the beginning are on the RemSound releases page on GitHub.";
}
return notes;
}
public AboutDialog()
{
Text = "About RemSound";
@@ -1377,7 +1408,7 @@ internal sealed class AboutDialog : Form
Dock = DockStyle.Fill,
ScrollBars = ScrollBars.Vertical,
BorderStyle = BorderStyle.FixedSingle,
Text = ReleaseNotes,
Text = TrimToLastVersions(ReleaseNotes, ShownVersions),
AccessibleName = "Release notes (tab into and arrow to read)",
};
+28
View File
@@ -128,6 +128,7 @@ internal static class SelfTest
RunStep(results, "Service folder lockdown args (cross-user LPE hardening)", ServiceDirHardeningArgs);
RunStep(results, "Service folder repair (reproduce wrong-owner lockout → detect → repair → verify)", ServiceAccessRepairLoop);
RunStep(results, "Elevated verbs carry the real user (SID pass-through) + logs stay readable", ElevatedIdentityPassThrough);
RunStep(results, "About box shows only the newest releases (screen-reader-safe size)", AboutBoxNotesTrimmed);
RunStep(results, "Long-run hygiene (log rotation, crash-report cap, priority-mode scope)", LongRunHygiene);
RunStep(results, "Service startup volume (boot-once decision + settings round-trip)", ServiceStartupVolume);
RunStep(results, "Update install window (same-day, wraparound, retry timing)", UpdateInstallWindow);
@@ -2921,6 +2922,33 @@ internal static class SelfTest
return "identity travels by argument and only real user SIDs are trusted; logs readable by every account, read-only";
}
/// <summary>The About box's release-notes text must stay small: the full history reached ~70 KB in
/// one TextBox and reading a control value that size CRASHES some screen readers (reported
/// 2026-08-11). The box shows only the newest five version blocks, with a pointer to the full
/// history; this pins both the trim logic and the real shipped text's size so the crash can't
/// quietly come back as releases accumulate.</summary>
private static string? AboutBoxNotesTrimmed()
{
// The trim logic on synthetic notes: 7 blocks in, 5 out, pointer appended; fewer than the
// limit passes through untouched.
var synthetic = string.Join("\r\n\r\n", Enumerable.Range(1, 7).Select(i => $"RemSound v9.{i}\r\n\r\nNotes for release {i}."));
var trimmed = AboutDialog.TrimToLastVersions(synthetic, 5);
Check(trimmed.Split('\n').Count(l => l.TrimEnd('\r').StartsWith("RemSound v", StringComparison.Ordinal)) == 5,
"seven version blocks must trim to exactly five");
Check(trimmed.Contains("RemSound v9.5") && !trimmed.Contains("RemSound v9.6"),
"the fifth-newest block stays, the sixth is cut");
Check(trimmed.Contains("releases page"), "the cut must end with a pointer to the full history");
Check(AboutDialog.TrimToLastVersions("RemSound v1.0\r\nOnly one.", 5) == "RemSound v1.0\r\nOnly one.",
"notes with fewer blocks than the limit pass through unchanged");
// The REAL shipped text: exactly five versions shown, and small enough to be read safely.
var shown = AboutDialog.TrimToLastVersions(AboutDialog.ReleaseNotesForTest, 5);
var versions = shown.Split('\n').Count(l => l.TrimEnd('\r').StartsWith("RemSound v", StringComparison.Ordinal));
Check(versions == 5, $"the shipped About text must show exactly 5 versions (got {versions})");
Check(shown.Length < 10_000, $"the shipped About text must stay well under screen-reader-crashing size (got {shown.Length} chars)");
return $"shipped About text: 5 versions, {shown.Length} chars (was ~70,000 — the screen-reader crash)";
}
/// <summary>Issue #23 boot self-heal decision core. Scenario: at the boot lock screen the machine's
/// speakers audibly play (Windows tune, NVDA) but a capture attached in the first seconds of boot
/// taps an engine mix the logon-session audio was never wired into — the endpoint's own METER shows