feat(ui): add current-launch logs tab

Add Live Chat and Logs tabs to the non-edit left panel. Expose a bounded process-local AppLog feed so the newest messages appear first without importing earlier startup.log contents or changing file logging.

Ref: https://obsproject.com/forum/resources/understanding-obs-studio-log.473
This commit is contained in:
2026-11-02 11:06:43 -08:00
parent 20f059422b
commit 9fa06c67c2
11 changed files with 305 additions and 77 deletions
+85 -57
View File
@@ -913,69 +913,97 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="LIVE CHAT" Style="{StaticResource SectionHeader}"/>
<!-- Inactive overlay (shown when NOT live) -->
<StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource BoolToVis}}">
<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="{StaticResource Brush.OnSurfaceDisabled}" FontSize="11" TextWrapping="Wrap"
HorizontalAlignment="Center" TextAlignment="Center" MaxWidth="240"/>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,8">
<ToggleButton x:Name="LiveChatTabButton" Style="{StaticResource SegmentToggle}"
IsChecked="{Binding IsLiveChatTabSelected, Mode=TwoWay}" Padding="10,5">
<TextBlock Text="Live Chat" Style="{StaticResource SegmentLabel}"/>
</ToggleButton>
<ToggleButton x:Name="LogsTabButton" Style="{StaticResource SegmentToggle}"
IsChecked="{Binding IsLogsTabSelected, Mode=TwoWay}" Padding="10,5" Margin="4,0,0,0">
<TextBlock Text="Logs" Style="{StaticResource SegmentLabel}"/>
</ToggleButton>
</StackPanel>
<!-- Pinned message -->
<Border Grid.Row="1" VerticalAlignment="Top" Margin="4,4,4,0" Padding="6,4" CornerRadius="4"
Background="{StaticResource Brush.SurfaceContainerHigh}" BorderBrush="{StaticResource Brush.Tertiary}" BorderThickness="1"
Visibility="{Binding PinnedChatMessage, Converter={StaticResource NotNullToVis}}">
<StackPanel>
<TextBlock Text="{Binding PinnedChatMessage.AuthorName}" Foreground="{StaticResource Brush.Tertiary}" FontWeight="SemiBold" FontSize="11"/>
<TextBlock Text="{Binding PinnedChatMessage.Message}" Foreground="{StaticResource Brush.OnSurface}" FontSize="11" TextWrapping="Wrap"/>
</StackPanel>
</Border>
<Grid Grid.Row="1">
<Grid Visibility="{Binding IsLiveChatTabSelected, Converter={StaticResource BoolToVis}}">
<!-- Inactive overlay (shown when NOT live) -->
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center"
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource BoolToVis}}">
<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="{StaticResource Brush.OnSurfaceDisabled}" FontSize="11" TextWrapping="Wrap"
HorizontalAlignment="Center" TextAlignment="Center" MaxWidth="240"/>
</StackPanel>
<!-- Message feed (shown when live) -->
<ListBox Grid.Row="1" Background="Transparent" BorderThickness="0" Margin="0,40,0,0"
ItemsSource="{Binding ChatMessages}"
ItemContainerStyle="{StaticResource YtListBoxItem}"
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource InverseBoolToVis}}"
x:Name="ChatListBox">
<ListBox.ItemTemplate><DataTemplate>
<Border x:Name="MessageBorder" Padding="6,4" CornerRadius="4">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSuperChat}" Value="True">
<Setter Property="Background" Value="{StaticResource Brush.SurfaceContainerHigh}"/>
<Setter Property="BorderBrush" Value="{StaticResource Brush.Secondary}"/>
<Setter Property="BorderThickness" Value="1,0,1,0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<!-- Pinned message -->
<Border VerticalAlignment="Top" Margin="4,4,4,0" Padding="6,4" CornerRadius="4"
Background="{StaticResource Brush.SurfaceContainerHigh}" BorderBrush="{StaticResource Brush.Tertiary}" BorderThickness="1"
Visibility="{Binding PinnedChatMessage, Converter={StaticResource NotNullToVis}}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,2">
<TextBlock Text="&#x2B50;" FontSize="10" Margin="0,0,4,0"
VerticalAlignment="Center"
Visibility="{Binding IsMember, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="{Binding AuthorName}" Foreground="{StaticResource Brush.Tertiary}"
FontWeight="Bold" FontSize="12" VerticalAlignment="Center"/>
<TextBlock Text="{Binding SuperChatAmount, StringFormat=${0:F2}}"
Foreground="{StaticResource Brush.Secondary}" FontWeight="Bold" FontSize="12"
Margin="8,0,0,0" VerticalAlignment="Center"
Visibility="{Binding IsSuperChat, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="{Binding SuperChatCurrency}" Foreground="{StaticResource Brush.Secondary}"
FontSize="11" Margin="2,0,0,0" VerticalAlignment="Center"
Visibility="{Binding IsSuperChat, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
<TextBlock Text="{Binding Message}" Foreground="{StaticResource Brush.OnSurface}"
TextWrapping="Wrap" Margin="0,2,0,0"/>
<TextBlock Text="{Binding PinnedChatMessage.AuthorName}" Foreground="{StaticResource Brush.Tertiary}" FontWeight="SemiBold" FontSize="11"/>
<TextBlock Text="{Binding PinnedChatMessage.Message}" Foreground="{StaticResource Brush.OnSurface}" FontSize="11" TextWrapping="Wrap"/>
</StackPanel>
</Border>
</DataTemplate></ListBox.ItemTemplate>
</ListBox>
<!-- Message feed (shown when live) -->
<ListBox Background="Transparent" BorderThickness="0" Margin="0,40,0,0"
ItemsSource="{Binding ChatMessages}"
ItemContainerStyle="{StaticResource YtListBoxItem}"
Visibility="{Binding ShowChatInactiveMessage, Converter={StaticResource InverseBoolToVis}}"
x:Name="ChatListBox">
<ListBox.ItemTemplate><DataTemplate>
<Border x:Name="MessageBorder" Padding="6,4" CornerRadius="4">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSuperChat}" Value="True">
<Setter Property="Background" Value="{StaticResource Brush.SurfaceContainerHigh}"/>
<Setter Property="BorderBrush" Value="{StaticResource Brush.Secondary}"/>
<Setter Property="BorderThickness" Value="1,0,1,0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,2">
<TextBlock Text="&#x2B50;" FontSize="10" Margin="0,0,4,0"
VerticalAlignment="Center"
Visibility="{Binding IsMember, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="{Binding AuthorName}" Foreground="{StaticResource Brush.Tertiary}"
FontWeight="Bold" FontSize="12" VerticalAlignment="Center"/>
<TextBlock Text="{Binding SuperChatAmount, StringFormat=${0:F2}}"
Foreground="{StaticResource Brush.Secondary}" FontWeight="Bold" FontSize="12"
Margin="8,0,0,0" VerticalAlignment="Center"
Visibility="{Binding IsSuperChat, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="{Binding SuperChatCurrency}" Foreground="{StaticResource Brush.Secondary}"
FontSize="11" Margin="2,0,0,0" VerticalAlignment="Center"
Visibility="{Binding IsSuperChat, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
<TextBlock Text="{Binding Message}" Foreground="{StaticResource Brush.OnSurface}"
TextWrapping="Wrap" Margin="0,2,0,0"/>
</StackPanel>
</Border>
</DataTemplate></ListBox.ItemTemplate>
</ListBox>
</Grid>
<ListBox x:Name="LogListBox" Background="Transparent" BorderThickness="0"
ItemsSource="{Binding CurrentLaunchLogEntries}"
ItemContainerStyle="{StaticResource YtListBoxItem}"
Visibility="{Binding IsLogsTabSelected, Converter={StaticResource BoolToVis}}"
ScrollViewer.CanContentScroll="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Style="{StaticResource PropertyHint}"
TextWrapping="Wrap" Margin="6,4"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Grid>
</Border>
+3 -1
View File
@@ -19,7 +19,9 @@ See [`schema.md`](../schema.md) for the memory-map conventions.
The Add Layer menu exposes one standalone recent-alert Ticker per scene. The
properties panel edits its independent `Ticker` settings; Stream Alerts retains
its separate current-event ticker controls.
its separate current-event ticker controls. Outside edit mode, the left panel
offers Live Chat first and Logs second; Logs shows this process's newest AppLog
entries first and excludes earlier file contents.
## Window ↔ control contract