diff --git a/Controls/LeftPanel.xaml b/Controls/LeftPanel.xaml
index c8f4c73..f384da6 100644
--- a/Controls/LeftPanel.xaml
+++ b/Controls/LeftPanel.xaml
@@ -81,12 +81,9 @@
Width="14" Height="14" Stretch="Uniform" VerticalAlignment="Top"
Margin="6,2,2,0" ToolTip="Standing indicator"
Visibility="{Binding ShowsPin, Converter={StaticResource BoolToVis}}"/>
-
-
-
-
+
diff --git a/Models/AttentionAlert.cs b/Models/AttentionAlert.cs
index 1b3a2af..9313663 100644
--- a/Models/AttentionAlert.cs
+++ b/Models/AttentionAlert.cs
@@ -8,10 +8,11 @@ namespace ytLive.Models;
/// becomes dismissible — and its dismissal permanent — once ownership is verified).
/// Clicking a row runs its action: the licence indicator opens About; an event alert opens the
/// Logs tab with that event's lines highlighted.
+/// Rows are text only — a heading line above the message was redundant and pushed the stack past
+/// its two-line budget (creator, 2026-11-05).
///
public sealed record AttentionAlert(
string Id,
- string Label,
string Text,
bool IsPinned,
AttentionAlertAction Action,
diff --git a/ViewModels/MainViewModel.Alerts.cs b/ViewModels/MainViewModel.Alerts.cs
index fceb2fd..8ee5ad7 100644
--- a/ViewModels/MainViewModel.Alerts.cs
+++ b/ViewModels/MainViewModel.Alerts.cs
@@ -46,8 +46,6 @@ public partial class MainViewModel
return $"Steam ID Limit: {entitled}fps\nClick for More Info";
}
- private string SteamLicenceLabel() => IsAppOwned ? "STEAM ID PRESENT" : "STEAM ID NOT PRESENT";
-
/// Recomputes row 1 in place. Called at startup and whenever ownership changes.
private void RefreshSteamLicenceAlert()
{
@@ -63,7 +61,6 @@ public partial class MainViewModel
var alert = new AttentionAlert(
SteamLicenceAlertId,
- SteamLicenceLabel(),
SteamLicenceText(),
IsPinned: !IsAppOwned,
AttentionAlertAction.About);
@@ -76,9 +73,9 @@ public partial class MainViewModel
/// Pushes an event alert. Newest sits directly under the licence indicator; older ones
/// stack beneath, so the newest is the one nearest the eye.
- private AttentionAlert? PushAttentionAlert(string id, string label, string text, long logFrom, long logTo)
+ private AttentionAlert? PushAttentionAlert(string id, string text, long logFrom, long logTo)
{
- var alert = new AttentionAlert(id, label, text, IsPinned: false, AttentionAlertAction.Logs, logFrom, logTo);
+ var alert = new AttentionAlert(id, text, IsPinned: false, AttentionAlertAction.Logs, logFrom, logTo);
var insertAt = _attentionAlerts.Count(alert => alert.Id == SteamLicenceAlertId);
var existing = _attentionAlerts.FirstOrDefault(candidate => candidate.Id == id);
if (existing != null)
@@ -95,8 +92,8 @@ public partial class MainViewModel
}
/// Test seam: pushes an event row without a stream session behind it.
- internal AttentionAlert? PushAttentionAlertForTest(string id, string label, string text, long logFrom, long logTo)
- => PushAttentionAlert(id, label, text, logFrom, logTo);
+ internal AttentionAlert? PushAttentionAlertForTest(string id, string text, long logFrom, long logTo)
+ => PushAttentionAlert(id, text, logFrom, logTo);
private void RemoveAttentionAlert(string id)
{
diff --git a/ViewModels/MainViewModel.Recovery.cs b/ViewModels/MainViewModel.Recovery.cs
index 876eb2f..22b5b09 100644
--- a/ViewModels/MainViewModel.Recovery.cs
+++ b/ViewModels/MainViewModel.Recovery.cs
@@ -87,7 +87,6 @@ public partial class MainViewModel
: logFrom;
PushAttentionAlert(
"stream-recovery",
- "STREAM",
text,
logFrom,
Math.Max(logFrom, to));
diff --git a/docs/ai/ui-surface.md b/docs/ai/ui-surface.md
index 568c545..d84da1b 100644
--- a/docs/ai/ui-surface.md
+++ b/docs/ai/ui-surface.md
@@ -4,9 +4,11 @@ A stack of red rows at the top of the left panel, **above both mode grids** (so
and while live) and pushing the panel's content down. Only red rows belong there — it is the surface for
"something needs you", never chatter.
+- **Rows are text only, two lines at most** — no heading line above the message (creator, 2026-11-05:
+ the heading was redundant and pushed the stack past its budget).
- **Row 1 is always the Steam licence indicator** and nothing displaces it. It reads as status, not a
- warning: `Entitlement 30 fps · Maximum 60 fps`, both numbers derived from `QualityOptions` and
- `HasProOutput`. It shows a **pin** (standing indicator, no close control) while higher tiers are
+ warning: `Steam ID Limit: 30fps` / `Click for More Info` (60fps once owned), both numbers derived from
+ `QualityOptions` and `HasProOutput`. It shows a **pin** (standing indicator, no close control) while higher tiers are
unavailable, and a **close control once ownership is verified** — that close is persisted
(`Steam.LicenceIndicatorDismissed`) and permanent *while ownership holds*; losing ownership restores
the row as pinned, because that is a change of state rather than a preference. Click → About.
diff --git a/ytLive.Tests/AboutHubTests.cs b/ytLive.Tests/AboutHubTests.cs
index f635a2c..e344f86 100644
--- a/ytLive.Tests/AboutHubTests.cs
+++ b/ytLive.Tests/AboutHubTests.cs
@@ -99,7 +99,7 @@ public sealed class AboutHubTests
// The old red panel under the quality picker is gone (2026-11-05): the licence state
// lives in the left panel's alert stack now, as a pinned status indicator.
Assert.Null(window.FindName("EntitlementErrorPanel"));
- var licenceRow = vm.AttentionAlerts.Single(alert => alert.Label == "STEAM ID NOT PRESENT");
+ var licenceRow = vm.AttentionAlerts.Single(alert => alert.Id == "steam-licence");
Assert.True(licenceRow.IsPinned);
Assert.Contains("Steam ID Limit: 30fps", licenceRow.Text);
Assert.Contains("Click for More Info", licenceRow.Text);
diff --git a/ytLive.Tests/AttentionAlertStackTests.cs b/ytLive.Tests/AttentionAlertStackTests.cs
index c1ee44d..4ac8a85 100644
--- a/ytLive.Tests/AttentionAlertStackTests.cs
+++ b/ytLive.Tests/AttentionAlertStackTests.cs
@@ -32,11 +32,13 @@ public sealed class AttentionAlertStackTests
vm.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
var row = Assert.Single(vm.AttentionAlerts);
- Assert.Equal("STEAM ID NOT PRESENT", row.Label);
+ Assert.Equal("steam-licence", row.Id);
Assert.True(row.IsPinned);
Assert.True(row.ShowsPin);
Assert.False(row.CanDismiss);
Assert.Equal(AttentionAlertAction.About, row.Action);
+ // Two lines at most: the heading above the message was redundant.
+ Assert.DoesNotContain("\n\n", row.Text);
// Creator wording: the limit this build runs at, and where to find out why.
Assert.Contains("Steam ID Limit: 30fps", row.Text);
@@ -63,11 +65,11 @@ public sealed class AttentionAlertStackTests
// Permanent: a fresh view-model on the same profile must not bring it back…
var reloaded = new MainViewModel();
- Assert.DoesNotContain(reloaded.AttentionAlerts, alert => alert.Label.StartsWith("STEAM ID"));
+ Assert.DoesNotContain(reloaded.AttentionAlerts, alert => alert.Id == "steam-licence");
// …but losing ownership restores it as a pinned indicator: a change of state, not a preference.
reloaded.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
- var restored = reloaded.AttentionAlerts.First(alert => alert.Label == "STEAM ID NOT PRESENT");
+ var restored = reloaded.AttentionAlerts.First(alert => alert.Id == "steam-licence");
Assert.True(restored.IsPinned);
});
}
@@ -90,22 +92,22 @@ public sealed class AttentionAlertStackTests
WithVm(vm =>
{
vm.EntitlementOverride = new FakeEntitlement(false, "Steam licensing is not configured in this build.");
- vm.PushAttentionAlertForTest("drop-1", "STREAM", "Stream dropped — reconnecting (attempt 1 of 4)", 10, 12);
- vm.PushAttentionAlertForTest("drop-2", "STREAM", "Stream dropped — reconnecting (attempt 2 of 4)", 13, 15);
+ vm.PushAttentionAlertForTest("drop-1", "Stream dropped — reconnecting (attempt 1 of 4)", 10, 12);
+ vm.PushAttentionAlertForTest("drop-2", "Stream dropped — reconnecting (attempt 2 of 4)", 13, 15);
- Assert.Equal(new[] { "STEAM ID NOT PRESENT", "STREAM", "STREAM" }, vm.AttentionAlerts.Select(a => a.Label));
+ Assert.Equal(new[] { "steam-licence", "drop-2", "drop-1" }, vm.AttentionAlerts.Select(a => a.Id));
// Newest event sits directly under the indicator.
Assert.Contains("attempt 2", vm.AttentionAlerts[1].Text);
Assert.Contains("attempt 1", vm.AttentionAlerts[2].Text);
// The same event advancing updates its row instead of adding another.
- vm.PushAttentionAlertForTest("drop-2", "STREAM", "Stream recovered after 1.4 s", 13, 16);
+ vm.PushAttentionAlertForTest("drop-2", "Stream recovered after 1.4 s", 13, 16);
Assert.Equal(3, vm.AttentionAlerts.Count);
Assert.Contains("recovered", vm.AttentionAlerts[1].Text);
vm.DismissAlertCommand.Execute(vm.AttentionAlerts[1]);
Assert.Equal(2, vm.AttentionAlerts.Count);
- Assert.Equal("STEAM ID NOT PRESENT", vm.AttentionAlerts[0].Label);
+ Assert.Equal("steam-licence", vm.AttentionAlerts[0].Id);
});
}
@@ -114,7 +116,7 @@ public sealed class AttentionAlertStackTests
{
WithVm(vm =>
{
- vm.PushAttentionAlertForTest("drop-1", "STREAM", "Stream dropped", 41, 47);
+ vm.PushAttentionAlertForTest("drop-1", "Stream dropped", 41, 47);
vm.ActivateAlertCommand.Execute(vm.AttentionAlerts[1]);