feat(ui): the creator's six corrections
1. Stream Info + YPP rails are YouTube surfaces → new `youtube` token #ff0000. It is documented as brand-only: live state stays `primary`, so the two reds never mean the same thing. 2. EDIT toggle: the checked trigger set the SAME colour as the resting face, so clicking it changed nothing visible. Resting is now the neutral surface with muted text; armed is blue with dark text, and the label follows the fill. 3. Active scene outline is orange again (`attention`) — it marks the scene you work on, not a selection; blue made it read as "selected". Live stays red. 4. Scene names wear the same pill as the hotkey chips (black, 10px radius, 8×2 padding), so both labels stay readable over any thumbnail. 5. REC / ON-AIR pills and the running stop button read red while armed/live — including test mode, where the top bar is gold. New `RecordPillToggle` style (its own template, because SegmentToggle hard-codes a transparent face); the stop button's grey `#333` literal is gone. 6. The properties list had `MaxHeight="420"` inside an Auto row, so it scrolled while the window had space left. The row is `*` now and the cap is gone. Guard: UiShellTests pins all six to the markup and names the one that regresses.
This commit is contained in:
@@ -49,7 +49,7 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- LAYERS header + Add button -->
|
||||
@@ -300,7 +300,7 @@
|
||||
<TextBlock Grid.Row="6" Text="Select an item to edit its properties" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="12"
|
||||
Margin="8,4,0,0" TextWrapping="Wrap"
|
||||
Visibility="{Binding SelectedElement, Converter={StaticResource NullToVis}}"/>
|
||||
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="6" MaxHeight="420" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
||||
<ScrollViewer x:Name="ElementsScrollViewer" Grid.Row="6" VerticalScrollBarVisibility="Auto" Background="Transparent"
|
||||
Visibility="{Binding SelectedElement, Converter={StaticResource NotNullToVis}}">
|
||||
<StackPanel x:Name="ElementsPanel" Margin="8,4,0,0">
|
||||
<!-- Opacity + mirror/shape (draggable elements: image, webcam) -->
|
||||
|
||||
@@ -878,7 +878,7 @@
|
||||
</Border>
|
||||
<!-- The stacked white tabs -->
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Stretch">
|
||||
<Border Width="30" VerticalAlignment="Center" Background="{StaticResource Brush.Attention}"
|
||||
<Border Width="30" VerticalAlignment="Center" Background="{StaticResource Brush.Youtube}"
|
||||
CornerRadius="4,0,0,4" Padding="0,10" Cursor="Hand" Margin="0,4,0,0"
|
||||
ToolTip="Broadcast text fields">
|
||||
<Border.InputBindings>
|
||||
@@ -899,7 +899,7 @@
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Width="30" VerticalAlignment="Center" Background="{StaticResource Brush.Attention}"
|
||||
<Border Width="30" VerticalAlignment="Center" Background="{StaticResource Brush.Youtube}"
|
||||
CornerRadius="4,0,0,4" Padding="0,10" Cursor="Hand" Margin="0,4,0,0"
|
||||
ToolTip="YouTube Partner Program progress">
|
||||
<Border.InputBindings>
|
||||
|
||||
@@ -18,8 +18,12 @@
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Outline}"/>
|
||||
<Style.Triggers>
|
||||
<!-- Active scene = orange (creator ruling 2026-10-30): it marks
|
||||
the stage you are working on, not a selection — blue is
|
||||
the interactive accent and read as "selected". Live
|
||||
stays red. -->
|
||||
<DataTrigger Binding="{Binding IsStaged}" Value="True">
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Tertiary}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Attention}"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsLive}" Value="True">
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Primary}"/>
|
||||
@@ -67,13 +71,13 @@
|
||||
<TextBlock Text="{Binding HotkeyLabel, Mode=OneWay}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
|
||||
</Border>
|
||||
|
||||
<TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="White" FontSize="16" FontWeight="SemiBold"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="6"
|
||||
TextTrimming="CharacterEllipsis">
|
||||
<TextBlock.Effect>
|
||||
<DropShadowEffect BlurRadius="4" ShadowDepth="0" Opacity="0.9"/>
|
||||
</TextBlock.Effect>
|
||||
</TextBlock>
|
||||
<!-- The scene name wears the same pill as the hotkey chip, so both
|
||||
labels stay readable over any thumbnail. -->
|
||||
<Border HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="6"
|
||||
Padding="8,2" CornerRadius="10" Background="#000000">
|
||||
<TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="White" FontSize="15" FontWeight="SemiBold"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
|
||||
+16
-8
@@ -54,18 +54,21 @@
|
||||
reality line + End so nothing mid-session can be re-armed. -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<!-- EDIT: explicit toggle. Blue = off (chat), orange = on (layers/properties). -->
|
||||
<!-- EDIT: explicit toggle. Neutral = off (chat), blue = on (layers/properties). The
|
||||
trigger used to set the SAME colour as the resting state, so the button never
|
||||
looked pressed. -->
|
||||
<ToggleButton IsChecked="{Binding EditingActive, Mode=TwoWay}" Content="EDIT"
|
||||
Cursor="Hand" Height="32" Padding="14,0" Margin="0,0,24,0" VerticalAlignment="Center"
|
||||
FontWeight="Bold" FontSize="12" Foreground="{StaticResource Brush.OnTertiary}" BorderThickness="0">
|
||||
FontWeight="Bold" FontSize="12" Foreground="{StaticResource Brush.OnSurfaceVariant}" BorderThickness="0">
|
||||
<ToggleButton.Template>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border x:Name="EditBody" Background="{StaticResource Brush.Tertiary}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<Border x:Name="EditBody" Background="{StaticResource Brush.SurfaceContainerHigh}" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="EditBody" Property="Background" Value="{StaticResource Brush.Tertiary}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource Brush.OnTertiary}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
@@ -78,13 +81,17 @@
|
||||
Visibility="{Binding ShowIdleWorld, Converter={StaticResource BoolToVis}}">
|
||||
<Border Background="{StaticResource Brush.SurfaceContainerLowest}" CornerRadius="4" Padding="2" Height="32" VerticalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ToggleButton Style="{StaticResource SegmentToggle}"
|
||||
<ToggleButton Style="{StaticResource RecordPillToggle}"
|
||||
IsChecked="{Binding RecordPillOn}">
|
||||
<TextBlock Text="REC" Style="{StaticResource SegmentLabel}"/>
|
||||
<!-- the label follows the pill's own foreground: muted when armed-off,
|
||||
dark on red when armed (SegmentLabel pins its own colour) -->
|
||||
<TextBlock Text="REC" FontSize="12" FontWeight="Bold" VerticalAlignment="Center"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
|
||||
</ToggleButton>
|
||||
<ToggleButton Style="{StaticResource SegmentToggle}"
|
||||
<ToggleButton Style="{StaticResource RecordPillToggle}"
|
||||
IsChecked="{Binding OnAirPillOn}">
|
||||
<TextBlock Text="ON-AIR" Style="{StaticResource SegmentLabel}"/>
|
||||
<TextBlock Text="ON-AIR" FontSize="12" FontWeight="Bold" VerticalAlignment="Center"
|
||||
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -112,7 +119,8 @@
|
||||
<TextBlock Text="{Binding LiveElapsedText}" Foreground="White" FontSize="14"
|
||||
FontFamily="Consolas" VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||
<Button Content="{Binding EndButtonText}" Style="{StaticResource YtButton}"
|
||||
Background="#333" Margin="14,0,0,0" Command="{Binding EndStreamCommand}"/>
|
||||
Background="{StaticResource Brush.Primary}" Margin="14,0,0,0"
|
||||
Command="{Binding EndStreamCommand}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
| `success-container` | `#1d5c38` | Success notice surface (success toast) |
|
||||
| `warning-container` | `#b8860b` | Warning surface — health banner, warning toast |
|
||||
| `test-container` | `#9c6f1c` | Test-stream chrome: the gold top bar and glow |
|
||||
| `youtube` | `#ff0000` | **YouTube-branded surfaces only** — the Stream Info and YPP rails, the YouTube account row. Not for state: live stays `primary` |
|
||||
|
||||
**The rule:** red means "live or recording" and nothing else. Never a Send button, a delete action,
|
||||
a brand flourish, or a general accent — that was the defect in the first Stitch draft.
|
||||
|
||||
@@ -654,6 +654,31 @@
|
||||
<Setter Property="Margin" Value="8,0,0,0"/>
|
||||
</Style>
|
||||
|
||||
<!-- REC / ON-AIR segment pills: an armed session control reads red, because red is what
|
||||
"live or recording" means (DESIGN.md). Kept separate from SegmentToggle so the Rect|Circle
|
||||
choice in the properties panel stays the interactive blue. -->
|
||||
<Style x:Key="RecordPillToggle" TargetType="ToggleButton" BasedOn="{StaticResource SegmentToggle}">
|
||||
<!-- SegmentToggle's template hard-codes a Transparent face and paints the checked state
|
||||
itself, so an armed pill needs its own template that honours Background. -->
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border x:Name="Seg" Background="{TemplateBinding Background}" CornerRadius="3"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="Background" Value="{StaticResource Brush.Primary}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource Brush.OnError}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- Sliding pill toggle: OFF = neutral, ON = blue. Red is reserved for live/record
|
||||
(DESIGN.md), so a settings toggle can never use it. -->
|
||||
<Style x:Key="PillToggle" TargetType="ToggleButton">
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<SolidColorBrush x:Key="Brush.SuccessContainer" Color="#1d5c38"/>
|
||||
<SolidColorBrush x:Key="Brush.WarningContainer" Color="#b8860b"/>
|
||||
<SolidColorBrush x:Key="Brush.TestContainer" Color="#9c6f1c"/>
|
||||
<SolidColorBrush x:Key="Brush.Youtube" Color="#ff0000"/>
|
||||
|
||||
<!-- Colours (for gradients that cannot take a brush) -->
|
||||
<Color x:Key="Color.Background">#0b1326</Color>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace ytLive.Tests;
|
||||
|
||||
/// <summary>The creator's six corrections (2026-10-30) are visual rulings, so the only honest guard
|
||||
/// is one that pins the ruling to the markup: if a later edit quietly reverts one of them, this
|
||||
/// fails and names it.
|
||||
///
|
||||
/// <para>1. Stream Info + YPP rails are YouTube surfaces (YouTube red). 2. EDIT shows a real
|
||||
/// off/on difference. 3. The active scene outline is orange again. 4. Scene labels wear the same
|
||||
/// pill as the hotkey chips. 5. REC / ON-AIR and the running stop button read red. 6. The
|
||||
/// properties list no longer caps its height, so no scrollbar appears while space remains.</para></summary>
|
||||
public class UiShellTests
|
||||
{
|
||||
[Fact]
|
||||
public void The_creators_six_ui_corrections_are_in_the_markup()
|
||||
{
|
||||
var preview = SourceAssert.Text("Controls", "PreviewPane.xaml");
|
||||
var topBar = SourceAssert.Text("Controls", "TopBar.xaml");
|
||||
var strip = SourceAssert.Text("Controls", "SceneThumbnailStrip.xaml");
|
||||
var leftPanel = SourceAssert.Text("Controls", "LeftPanel.xaml");
|
||||
var theme = SourceAssert.Text("Themes", "Controls.xaml");
|
||||
var tokens = SourceAssert.Text("Themes", "Tokens.xaml");
|
||||
|
||||
// 1. YouTube-branded rails.
|
||||
Assert.Contains("Brush.Youtube", tokens);
|
||||
Assert.Equal(2, Count(preview, "Background=\"{StaticResource Brush.Youtube}\""));
|
||||
|
||||
// 2. EDIT: resting face differs from the armed face, and the label follows it.
|
||||
Assert.Contains("x:Name=\"EditBody\" Background=\"{StaticResource Brush.SurfaceContainerHigh}\"", topBar);
|
||||
Assert.Contains("<Setter TargetName=\"EditBody\" Property=\"Background\" Value=\"{StaticResource Brush.Tertiary}\"/>", topBar);
|
||||
Assert.Contains("<Setter Property=\"Foreground\" Value=\"{StaticResource Brush.OnTertiary}\"/>", topBar);
|
||||
|
||||
// 3. Active scene = orange; live stays red.
|
||||
Assert.Contains("IsStaged}\" Value=\"True\">\n <Setter Property=\"BorderBrush\" Value=\"{StaticResource Brush.Attention}\"/>", strip);
|
||||
|
||||
// 4. Scene name pill, same shape as the hotkey chip.
|
||||
Assert.Contains("Padding=\"8,2\" CornerRadius=\"10\" Background=\"#000000\"", strip);
|
||||
Assert.Contains("Text=\"{Binding Name, Mode=OneWay}\"", strip);
|
||||
|
||||
// 5. Armed session controls are red, and the running stop button is red — not a grey literal.
|
||||
Assert.Contains("x:Key=\"RecordPillToggle\"", theme);
|
||||
Assert.Contains("Brush.Primary", theme);
|
||||
Assert.Contains("Style=\"{StaticResource RecordPillToggle}\"", topBar);
|
||||
Assert.Contains("Background=\"{StaticResource Brush.Primary}\" Margin=\"14,0,0,0\"", topBar);
|
||||
Assert.DoesNotContain("Background=\"#333\"", topBar);
|
||||
|
||||
// 6. No artificial height cap on the properties list: the list fills the row, so a
|
||||
// scrollbar appears only when the content genuinely exceeds the space.
|
||||
var listLine = leftPanel.Split('\n').First(line => line.Contains("ElementsScrollViewer"));
|
||||
Assert.DoesNotContain("MaxHeight", listLine);
|
||||
}
|
||||
|
||||
private static int Count(string haystack, string needle)
|
||||
{
|
||||
var hits = 0;
|
||||
for (var i = haystack.IndexOf(needle, System.StringComparison.Ordinal); i >= 0;
|
||||
i = haystack.IndexOf(needle, i + needle.Length, System.StringComparison.Ordinal))
|
||||
hits++;
|
||||
return hits;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user