ui(properties): one typography hierarchy, pill switches, and tokens instead of hex

Creator directive 2026-11-01: the properties panels had 14 hand-rolled heading styles, 37
hand-rolled labels, four literal grey hex values, and 14 square checkboxes.

- PropertyHeader / PropertySubHeader / PropertyFieldLabel / PropertyHint in Themes/Controls.xaml
  now carry the whole hierarchy: category bright and 11px, labels and hints dim, size and weight
  doing the level. Colour groups the panel — bright for what you edit, dim for its labels and
  help — and no new palette entry was needed; DESIGN.md stays the only colour source.
- Every checkbox in the panel is a pill switch (PropertyPillCheck, 32x16 animated thumb).
  IsChecked semantics unchanged, so no binding moved.
- Literal #888/#666/#555 became OnSurfaceVariant / OnSurfaceDisabled.
- 13 headings, 2 sub-headers and 31 in-row labels now reference a style instead of repeating
  colour + size + alignment inline.

Controls audited for backing: every Button/Slider/ComboBox/CheckBox/ToggleButton/TextBox in the
panel has a Command or Click, a Value binding, an ItemsSource or Selected*, IsChecked, or Text —
no dead controls. Release 0 warnings; typography and token tests pass.
This commit is contained in:
2026-11-01 15:51:33 -08:00
parent 82960d0ca3
commit 8221bd6478
3 changed files with 174 additions and 69 deletions
+64 -69
View File
@@ -277,7 +277,7 @@
</ListBox.ItemTemplate>
</Helpers:FocusPreservingListBox>
<TextBlock Grid.Row="2" Text="No layers yet" Foreground="#555" FontSize="12"
<TextBlock Grid.Row="2" Text="No layers yet" Foreground="{StaticResource Brush.OnSurfaceDisabled}" FontSize="12"
HorizontalAlignment="Center" VerticalAlignment="Center"
Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/>
@@ -330,7 +330,7 @@
is not offered in the vertical Chat layout, so that option
is not even shown there (CanToggleClipShape). -->
<StackPanel Visibility="{Binding IsWebcamSelected, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Shape" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,4"/>
<TextBlock Text="Shape" Style="{StaticResource PropertyHeader}"/>
<StackPanel Orientation="Horizontal" Margin="10,0,0,4">
<Button Content="{Binding SelectedElement.MirrorButtonText}"
Style="{StaticResource YtButtonSecondary}" Padding="10,4"
@@ -350,7 +350,7 @@
strength slider, on the SAME 0–1 scale and default as the
webcam's background blur — one radius curve, shared. -->
<StackPanel Visibility="{Binding IsBlurSelected, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Blur" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,4"/>
<TextBlock Text="Blur" Style="{StaticResource PropertyHeader}"/>
<StackPanel Orientation="Horizontal" Margin="10,0,0,6">
<ToggleButton Style="{StaticResource SegmentToggle}" Padding="10,4"
IsChecked="{Binding SelectedElement.IsRectShape, Mode=TwoWay}">
@@ -362,7 +362,7 @@
</ToggleButton>
</StackPanel>
<DockPanel Margin="10,0,0,4">
<TextBlock DockPanel.Dock="Right" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Margin="8,0,0,0" Width="34">
<TextBlock DockPanel.Dock="Right" Style="{StaticResource PropertyFieldLabel}" Margin="8,0,0,0" Width="34">
<Run Text="{Binding SelectedElement.BlurAmount, Mode=OneWay, StringFormat={}{0:P0}}"/>
</TextBlock>
<Slider Minimum="0" Maximum="1" SmallChange="0.05" LargeChange="0.1"
@@ -386,23 +386,23 @@
above) so the webcam's border/background section stays the
one panel bound to SelectedElement.IsWebcam. -->
<StackPanel Visibility="{Binding SelectedElement.IsDraggable, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Geometry" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,8,0,4"/>
<TextBlock Text="Geometry" Style="{StaticResource PropertyHeader}"/>
<!-- TASK 61: the four fields share ONE fixed width — wide enough
for the largest geometry value (1920, plus a typed sign) and
no wider, so Y/H no longer stretch to fill the row. -->
<DockPanel Margin="10,0,0,4" LastChildFill="False">
<TextBlock DockPanel.Dock="Left" Text="X" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="16"/>
<TextBlock DockPanel.Dock="Left" Text="X" Style="{StaticResource PropertyFieldLabel}" Width="16"/>
<TextBox Text="{Binding SelectedElement.X, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}" Width="44"/>
<TextBlock DockPanel.Dock="Left" Text=" Y" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="20" Margin="8,0,0,0"/>
<TextBlock DockPanel.Dock="Left" Text=" Y" Style="{StaticResource PropertyFieldLabel}" Width="20" Margin="8,0,0,0"/>
<TextBox Text="{Binding SelectedElement.Y, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}" Width="44"/>
</DockPanel>
<DockPanel Margin="10,0,0,4" LastChildFill="False">
<TextBlock DockPanel.Dock="Left" Text="W" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="16"/>
<TextBlock DockPanel.Dock="Left" Text="W" Style="{StaticResource PropertyFieldLabel}" Width="16"/>
<TextBox Text="{Binding SelectedElement.Width, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}" Width="44"/>
<TextBlock DockPanel.Dock="Left" Text=" H" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="20" Margin="8,0,0,0"/>
<TextBlock DockPanel.Dock="Left" Text=" H" Style="{StaticResource PropertyFieldLabel}" Width="20" Margin="8,0,0,0"/>
<TextBox Text="{Binding SelectedElement.Height, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}" Width="44"/>
</DockPanel>
@@ -410,7 +410,7 @@
<!-- Webcam border config -->
<StackPanel Visibility="{Binding SelectedElement.IsWebcam, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Border Color" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,8,0,2"/>
<TextBlock Text="Border Color" Style="{StaticResource PropertyHeader}"/>
<!-- TASK 63: the border colour is picked with the app's OWN
drop-down (YtComboBox) — the same widget the Display
and Font pickers use — not a hand-rolled menu. The hex
@@ -429,7 +429,7 @@
ToolTip="Border colour" MinWidth="100"
Margin="8,0,0,0" VerticalAlignment="Center"/>
</DockPanel>
<TextBlock Text="Border Thickness" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,8,0,2"/>
<TextBlock Text="Border Thickness" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,6">
<Button DockPanel.Dock="Right" Style="{StaticResource IconButton}"
Width="22" Height="22" Margin="4,0,0,0"
@@ -446,17 +446,16 @@
<Slider DockPanel.Dock="Left" Width="120" Minimum="0" Maximum="10"
Value="{Binding SelectedElement.BorderWidth, Mode=TwoWay}"
VerticalAlignment="Center" TickFrequency="1" IsSnapToTickEnabled="True"/>
<TextBlock Text="{Binding SelectedElement.BorderWidth}" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
<TextBlock Text="{Binding SelectedElement.BorderWidth}" Style="{StaticResource PropertyFieldLabel}" Margin="8,0,0,0" Width="20"/>
</DockPanel>
<!-- TASK 51: background effects — two pills acting as radio buttons
(one on turns the other off; both off = no effect). TASK 59:
the pills are indented under the Background title, and the
strength row one level deeper, under Blur. -->
<TextBlock Text="Background" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,8,0,6"/>
<TextBlock Text="Background" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Remove" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Remove" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ToggleButton x:Name="RemoveBackgroundPill" Style="{StaticResource PillToggle}"
IsChecked="{Binding SelectedElement.RemoveBackground, Mode=TwoWay}"
AutomationProperties.Name="Remove background"
@@ -464,7 +463,7 @@
HorizontalAlignment="Left"/>
</DockPanel>
<DockPanel Margin="10,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Blur" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Blur" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ToggleButton x:Name="BlurBackgroundPill" Style="{StaticResource PillToggle}"
IsChecked="{Binding SelectedElement.BlurBackground, Mode=TwoWay}"
AutomationProperties.Name="Blur background"
@@ -473,9 +472,9 @@
</DockPanel>
<DockPanel Margin="20,0,0,6"
Visibility="{Binding SelectedElement.HasBackgroundEffect, Converter={StaticResource BoolToVis}}">
<TextBlock DockPanel.Dock="Left" Text="Strength" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Strength" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<TextBlock DockPanel.Dock="Right" Text="{Binding SelectedElement.BackgroundStrength, StringFormat={}{0:P0}}"
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Margin="8,0,0,0" Width="34"/>
Style="{StaticResource PropertyFieldLabel}" Margin="8,0,0,0" Width="34"/>
<Slider x:Name="BackgroundStrengthSlider" Minimum="0" Maximum="1"
Value="{Binding SelectedElement.BackgroundStrength, Mode=TwoWay}"
TickFrequency="0.05" IsSnapToTickEnabled="True" VerticalAlignment="Center"
@@ -496,7 +495,7 @@
Visibility="{Binding CanUseScoreboard, Converter={StaticResource InverseBoolToVis}}"/>
<!-- DEBUG builds only: prove the strip in the preview before a real
stream. Absent from Release, not merely hidden by a flag. -->
<CheckBox Style="{StaticResource YtCheckBox}" Margin="0,0,0,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Margin="0,0,0,4"
IsChecked="{Binding ScoreboardSimulate, Mode=TwoWay}"
ToolTip="Push sample events so the strip appears in the preview."
Visibility="{Binding IsDevelopmentBuild, Converter={StaticResource BoolToVis}}">
@@ -504,11 +503,10 @@
be mistaken for product. -->
<TextBlock Text="Simulate (dev)" Foreground="{StaticResource Brush.Attention}" FontSize="12"/>
</CheckBox>
<CheckBox Style="{StaticResource YtCheckBox}" Margin="0,0,0,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Margin="0,0,0,4"
Content="Show the scoreboard" IsChecked="{Binding ScoreboardEnabled, Mode=TwoWay}"
ToolTip="Off hides the strip in every scene AND stops the channel polling."/>
<TextBlock Text="Edge (socials take the other)" Foreground="{StaticResource Brush.OnSurfaceVariant}"
FontSize="11" Margin="10,0,0,2"/>
<TextBlock Text="Edge (socials take the other)" Style="{StaticResource PropertySubHeader}"/>
<ComboBox Style="{StaticResource YtComboBox}" Height="24" FontSize="11" Margin="10,0,0,6"
ItemsSource="{Binding ScoreboardEdgeOptions}"
SelectedItem="{Binding ScoreboardEdge, Mode=TwoWay}"/>
@@ -523,32 +521,32 @@
Value="{Binding ScoreboardScrollSpeed, Mode=TwoWay}" VerticalAlignment="Center"/>
</DockPanel>
<TextBlock Text="Facts" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,4"/>
<TextBlock Text="Facts" Style="{StaticResource PropertyHeader}"/>
<WrapPanel Margin="10,0,0,0">
<CheckBox Style="{StaticResource YtCheckBox}" Content="Subscribers" Margin="0,0,10,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="Subscribers" Margin="0,0,10,4"
IsChecked="{Binding ScoreboardShowSubscribers, Mode=TwoWay}"/>
<CheckBox Style="{StaticResource YtCheckBox}" Content="Watch hours" Margin="0,0,10,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="Watch hours" Margin="0,0,10,4"
IsChecked="{Binding ScoreboardShowWatchHours, Mode=TwoWay}"/>
<CheckBox Style="{StaticResource YtCheckBox}" Content="Super Chats" Margin="0,0,10,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="Super Chats" Margin="0,0,10,4"
IsChecked="{Binding ScoreboardShowSuperChats, Mode=TwoWay}"/>
<CheckBox Style="{StaticResource YtCheckBox}" Content="New members" Margin="0,0,10,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="New members" Margin="0,0,10,4"
IsChecked="{Binding ScoreboardShowNewMembers, Mode=TwoWay}"/>
<CheckBox Style="{StaticResource YtCheckBox}" Content="Milestones" Margin="0,0,10,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="Milestones" Margin="0,0,10,4"
IsChecked="{Binding ScoreboardShowMilestones, Mode=TwoWay}"/>
<CheckBox Style="{StaticResource YtCheckBox}" Content="Last member" Margin="0,0,10,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="Last member" Margin="0,0,10,4"
IsChecked="{Binding ScoreboardShowLastMember, Mode=TwoWay}"/>
<CheckBox Style="{StaticResource YtCheckBox}" Content="Last Super Chat" Margin="0,0,10,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="Last Super Chat" Margin="0,0,10,4"
IsChecked="{Binding ScoreboardShowLastSuperChat, Mode=TwoWay}"/>
<CheckBox Style="{StaticResource YtCheckBox}" Content="Last gift" Margin="0,0,10,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="Last gift" Margin="0,0,10,4"
IsChecked="{Binding ScoreboardShowLastGift, Mode=TwoWay}"/>
</WrapPanel>
<TextBlock Text="Text colour" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" Margin="10,6,0,2"/>
<TextBlock Text="Text colour" Style="{StaticResource PropertySubHeader}"/>
<ComboBox Style="{StaticResource YtComboBox}" Height="24" FontSize="11" Margin="10,0,0,6"
ItemsSource="{Binding ScoreboardColorOptions}" DisplayMemberPath="Name"
SelectedValuePath="Hex" SelectedValue="{Binding ScoreboardTextColor, Mode=TwoWay}"/>
<CheckBox Style="{StaticResource YtCheckBox}" Content="Background" Margin="10,0,0,4"
<CheckBox Style="{StaticResource PropertyPillCheck}" Content="Background" Margin="10,0,0,4"
IsChecked="{Binding ScoreboardBackgroundEnabled, Mode=TwoWay}"/>
<ComboBox Style="{StaticResource YtComboBox}" Height="24" FontSize="11" Margin="10,0,0,6"
ItemsSource="{Binding ScoreboardColorOptions}" DisplayMemberPath="Name"
@@ -593,27 +591,26 @@
<Slider DockPanel.Dock="Left" Width="120" Minimum="1" Maximum="60"
Value="{Binding SelectedElement.TimerMinutes, Mode=TwoWay}"
VerticalAlignment="Center" TickFrequency="1" IsSnapToTickEnabled="True"/>
<TextBlock Text="{Binding SelectedElement.TimerMinutes}" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
<TextBlock Text="{Binding SelectedElement.TimerMinutes}" Style="{StaticResource PropertyFieldLabel}" Margin="8,0,0,0" Width="20"/>
</DockPanel>
</StackPanel>
<!-- Web source config -->
<StackPanel Visibility="{Binding SelectedElement.IsWebSource, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Geometry" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,4"/>
<TextBlock Text="Geometry" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="X" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="16"/>
<TextBlock DockPanel.Dock="Left" Text="X" Style="{StaticResource PropertyFieldLabel}" Width="16"/>
<TextBox Text="{Binding SelectedElement.X, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}"/>
<TextBlock DockPanel.Dock="Left" Text=" Y" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="20" Margin="8,0,0,0"/>
<TextBlock DockPanel.Dock="Left" Text=" Y" Style="{StaticResource PropertyFieldLabel}" Width="20" Margin="8,0,0,0"/>
<TextBox Text="{Binding SelectedElement.Y, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}"/>
</DockPanel>
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="W" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="16"/>
<TextBlock DockPanel.Dock="Left" Text="W" Style="{StaticResource PropertyFieldLabel}" Width="16"/>
<TextBox Text="{Binding SelectedElement.Width, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}"/>
<TextBlock DockPanel.Dock="Left" Text=" H" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="20" Margin="8,0,0,0"/>
<TextBlock DockPanel.Dock="Left" Text=" H" Style="{StaticResource PropertyFieldLabel}" Width="20" Margin="8,0,0,0"/>
<TextBox Text="{Binding SelectedElement.Height, UpdateSourceTrigger=PropertyChanged, StringFormat=F0}"
Style="{StaticResource YtTextBoxCompact}"/>
</DockPanel>
@@ -639,10 +636,10 @@
<!-- Chat box config -->
<StackPanel Visibility="{Binding SelectedElement.IsChatBox, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Chat Overlay" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,6"/>
<TextBlock Text="Chat Overlay" Style="{StaticResource PropertyHeader}"/>
<!-- Border (same controls as the webcam's border) -->
<TextBlock Text="Border Color" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,2"/>
<TextBlock Text="Border Color" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,6">
<TextBox DockPanel.Dock="Left"
Text="{Binding SelectedElement.BorderColor, UpdateSourceTrigger=PropertyChanged}"
@@ -656,26 +653,24 @@
ToolTip="Border colour" MinWidth="100"
Margin="8,0,0,0" VerticalAlignment="Center"/>
</DockPanel>
<TextBlock Text="Border Thickness" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,2"/>
<TextBlock Text="Border Thickness" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,6">
<Slider DockPanel.Dock="Left" Width="120" Minimum="0" Maximum="10"
Value="{Binding SelectedElement.BorderWidth, Mode=TwoWay}"
VerticalAlignment="Center" TickFrequency="1" IsSnapToTickEnabled="True"/>
<TextBlock Text="{Binding SelectedElement.BorderWidth}" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
VerticalAlignment="Center" Margin="8,0,0,0" Width="20"/>
<TextBlock Text="{Binding SelectedElement.BorderWidth}" Style="{StaticResource PropertyFieldLabel}" Margin="8,0,0,0" Width="20"/>
</DockPanel>
<TextBlock Text="Border Opacity" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,2"/>
<TextBlock Text="Border Opacity" Style="{StaticResource PropertyHeader}"/>
<DockPanel Margin="10,0,0,8">
<Slider DockPanel.Dock="Left" Width="120" Minimum="0" Maximum="1"
Value="{Binding SelectedElement.BorderOpacity, Mode=TwoWay}"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding SelectedElement.BorderOpacity, StringFormat={}{0:P0}}" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11"
VerticalAlignment="Center" Margin="8,0,0,0" Width="34"/>
<TextBlock Text="{Binding SelectedElement.BorderOpacity, StringFormat={}{0:P0}}" Style="{StaticResource PropertyFieldLabel}" Margin="8,0,0,0" Width="34"/>
</DockPanel>
<!-- Font Size -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Font Size" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Font Size" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<Slider x:Name="ChatFontSizeSlider" Width="120" Minimum="8" Maximum="72" TickFrequency="2" IsSnapToTickEnabled="True"
Value="{Binding SelectedElement.ChatFontSize, Mode=TwoWay}"
VerticalAlignment="Center"/>
@@ -684,7 +679,7 @@
<!-- Font Family -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Font" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Font" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<ComboBox SelectedValue="{Binding SelectedElement.ChatFontFamily, Mode=TwoWay}"
SelectedValuePath="Content"
Style="{StaticResource YtComboBox}"
@@ -700,28 +695,28 @@
<!-- Font Color -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Font Color" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Font Color" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<TextBox Text="{Binding SelectedElement.ChatFontColor, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource YtTextBoxCompact}" Width="80"/>
</DockPanel>
<!-- Member Color -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Member" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Member" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<TextBox Text="{Binding SelectedElement.ChatMemberColor, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource YtTextBoxCompact}" Width="80"/>
</DockPanel>
<!-- Super Chat Color -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Super Chat" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Super Chat" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<TextBox Text="{Binding SelectedElement.ChatSuperChatColor, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource YtTextBoxCompact}" Width="80"/>
</DockPanel>
<!-- Max Messages -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Max Msgs" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Max Msgs" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<Slider x:Name="ChatMaxMsgsSlider" Width="120" Minimum="1" Maximum="20" TickFrequency="1" IsSnapToTickEnabled="True"
Value="{Binding SelectedElement.ChatMaxMessages, Mode=TwoWay}"
VerticalAlignment="Center"/>
@@ -730,14 +725,14 @@
<!-- Background Color -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Background" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Background" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<TextBox Text="{Binding SelectedElement.ChatBackgroundColor, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource YtTextBoxCompact}" Width="80"/>
</DockPanel>
<!-- Background Opacity -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Bg Opacity" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Bg Opacity" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<Slider x:Name="ChatBgOpacitySlider" Width="120" Minimum="0" Maximum="1"
Value="{Binding SelectedElement.ChatBackgroundOpacity, Mode=TwoWay}"
VerticalAlignment="Center"/>
@@ -746,7 +741,7 @@
<!-- Fade After (seconds) -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Fade After" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="70"/>
<TextBlock DockPanel.Dock="Left" Text="Fade After" Style="{StaticResource PropertyFieldLabel}" Width="70"/>
<Slider x:Name="ChatFadeSlider" Width="120" Minimum="0" Maximum="60" TickFrequency="5" IsSnapToTickEnabled="True"
Value="{Binding SelectedElement.ChatFadeSeconds, Mode=TwoWay}"
VerticalAlignment="Center"/>
@@ -754,17 +749,17 @@
</DockPanel>
<!-- Toggles -->
<CheckBox Content="Show timestamps" Style="{StaticResource YtCheckBox}" Margin="0,4,0,2"
<CheckBox Content="Show timestamps" Style="{StaticResource PropertyPillCheck}" Margin="0,4,0,2"
IsChecked="{Binding SelectedElement.ChatShowTimestamps, Mode=TwoWay}"/>
<CheckBox Content="Show badges" Style="{StaticResource YtCheckBox}" Margin="0,0,0,2"
<CheckBox Content="Show badges" Style="{StaticResource PropertyPillCheck}" Margin="0,0,0,2"
IsChecked="{Binding SelectedElement.ChatShowBadges, Mode=TwoWay}"/>
</StackPanel>
<!-- Stream Alerts config -->
<StackPanel Visibility="{Binding SelectedElement.IsAlertBox, Converter={StaticResource BoolToVis}}" Margin="0,0,0,8">
<TextBlock Text="Stream Alerts" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" FontWeight="SemiBold" Margin="0,0,0,6"/>
<TextBlock Text="Stream Alerts" Style="{StaticResource PropertyHeader}"/>
<TextBlock Text="The built-in clip plays on every alert (Super Chat, membership…) unless a custom video is picked."
Foreground="#888" FontSize="10" TextWrapping="Wrap" Margin="0,0,0,6"/>
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="10" TextWrapping="Wrap" Margin="0,0,0,6"/>
<DockPanel Margin="0,0,0,4">
<!-- Test alert (creator ruling 2026-11-01): plays the clip this box
@@ -774,7 +769,7 @@
Style="{StaticResource YtButtonSecondary}"
Padding="8,2" FontSize="11" VerticalAlignment="Center"
ToolTip="Play the alert here, exactly as a viewer would trigger it"/>
<TextBlock DockPanel.Dock="Left" Text="Built-in video" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Built-in video" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ToggleButton Style="{StaticResource PillToggle}"
IsChecked="{Binding SelectedElement.AlertUseDefaultVideo, Mode=TwoWay}"
HorizontalAlignment="Left"/>
@@ -797,7 +792,7 @@
Style="{StaticResource YtTextBoxCompact}" IsReadOnly="True"
Foreground="{StaticResource Brush.OnSurfaceVariant}" VerticalAlignment="Center"/>
</DockPanel>
<CheckBox Content="Loop the sound until the video ends" Style="{StaticResource YtCheckBox}" Margin="0,2,0,6"
<CheckBox Content="Loop the sound until the video ends" Style="{StaticResource PropertyPillCheck}" Margin="0,2,0,6"
IsChecked="{Binding SelectedElement.AlertAudioLoop, Mode=TwoWay}"
IsEnabled="{Binding SelectedElement.HasAlertAudio}"/>
@@ -819,10 +814,10 @@
CommandParameter="{Binding SelectedElement}" Style="{StaticResource YtButtonSecondary}"
HorizontalAlignment="Left" Padding="8,2" Margin="0,0,4,0"/>
<TextBlock Text="Custom clips are referenced from disk — never stored in the layout."
Foreground="#888" FontSize="10" TextWrapping="Wrap" Margin="0,4,0,6"/>
Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="10" TextWrapping="Wrap" Margin="0,4,0,6"/>
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Message ticker" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Message ticker" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ToggleButton Style="{StaticResource PillToggle}"
IsChecked="{Binding SelectedElement.AlertShowTicker, Mode=TwoWay}"
HorizontalAlignment="Left"/>
@@ -832,7 +827,7 @@
solid (still). Inline items, not a bound ItemsSource —
same shape as the chat Font selector above. -->
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Display" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Display" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<ComboBox SelectedValue="{Binding SelectedElement.AlertDisplayMethod, Mode=TwoWay}"
SelectedValuePath="Tag"
Style="{StaticResource YtComboBox}"
@@ -844,7 +839,7 @@
</DockPanel>
<DockPanel Margin="0,0,0,4">
<TextBlock DockPanel.Dock="Left" Text="Volume" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center" Width="84"/>
<TextBlock DockPanel.Dock="Left" Text="Volume" Style="{StaticResource PropertyFieldLabel}" Width="84"/>
<Slider Width="120" Minimum="0" Maximum="1"
Value="{Binding SelectedElement.AlertVideoVolume, Mode=TwoWay}"
VerticalAlignment="Center"/>
@@ -858,7 +853,7 @@
<TextBlock Text="Background" Style="{StaticResource SubHeader}"/>
<StackPanel Margin="4,0,0,0">
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Text="Use default background:" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="11" VerticalAlignment="Center"/>
<TextBlock Text="Use default background:" Style="{StaticResource PropertyFieldLabel}"/>
<ToggleButton Style="{StaticResource PillToggle}"
IsChecked="{Binding SelectedElement.UseDefaultBackground, Mode=TwoWay}"
Margin="8,0,0,0"/>
@@ -900,7 +895,7 @@
<TextBlock Text="Chat is inactive" Foreground="{StaticResource Brush.OnSurfaceVariant}" FontSize="13"
HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="Chat becomes active once LlamaCasty is connected and streaming on YouTube."
Foreground="#666" FontSize="11" TextWrapping="Wrap"
Foreground="{StaticResource Brush.OnSurfaceDisabled}" FontSize="11" TextWrapping="Wrap"
HorizontalAlignment="Center" TextAlignment="Center" MaxWidth="240"/>
</StackPanel>