refactor: extract OverlayHost (settings/bug/feature/about dialogs) — Phase 2, Commit 18

OverlayHost.xaml(.cs) takes the window-root overlay dialog grid (settings/
bug/feature/about panels); BackToAbout_Click moved in via inherited DataContext.
WebViewHostPanel + ToastArea stay as thin window-root stubs so the WebView2 ctor
init and NotificationService InArea('ToastArea') name lookups are untouched.
This completes Phase 2: MainWindow.xaml is now a 123-line shell.

Zero behavior change; build 0 warnings; 246 pass, only the 2 known failures.
Docs: ViewModels/index.md Phase 2 tracker updated in same commit.
This commit is contained in:
2026-08-30 21:07:49 -07:00
parent c9fd1bdaaa
commit 544208e002
5 changed files with 196 additions and 176 deletions
+172
View File
@@ -0,0 +1,172 @@
<UserControl x:Class="ytLive.Controls.OverlayHost"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Helpers="clr-namespace:ytLive.Helpers">
<Grid Grid.RowSpan="3"
Visibility="{Binding IsAnyOverlayOpen, Converter={StaticResource BoolToVis}}">
<Button Background="#99000000" BorderThickness="0" Cursor="Hand"
Command="{Binding CloseOverlayCommand}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"/>
</ControlTemplate>
</Button.Template>
</Button>
<Border Background="#0A0C1A" CornerRadius="8" Width="650" MaxHeight="700"
VerticalAlignment="Center" HorizontalAlignment="Center"
Padding="28" BorderBrush="#333" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Header -->
<Grid Grid.Row="0">
<TextBlock Text="{Binding OverlayTitle}" FontSize="18" FontWeight="Bold"
Foreground="#e0e0e0" VerticalAlignment="Center"/>
<Button HorizontalAlignment="Right" ToolTip="Close" Style="{StaticResource IconButton}"
Command="{Binding CloseOverlayCommand}">
<Path Data="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"
Fill="#d0d0d0" Width="14" Height="14" Stretch="Uniform"/>
</Button>
</Grid>
<!-- App Settings: broadcast fields live in the preview's
Text pull-out now — this panel keeps app-level prefs only.
(Currently empty; the overlay shell stays for future use.) -->
<StackPanel Grid.Row="1" Margin="0,16,0,0"
Visibility="{Binding IsSettingsOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Default Location for Recordings" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}"
Text="{Binding RecordFolderDisplay, Mode=OneWay}"
IsReadOnly="True" Margin="0,0,0,12" Foreground="#a0a0b0"/>
<StackPanel Orientation="Horizontal">
<Button Content="Browse…" Command="{Binding ChooseRecordFolderCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0"/>
<Button Content="Use Downloads" Command="{Binding ResetRecordFolderCommand}"
Style="{StaticResource YtButtonSecondary}"/>
</StackPanel>
</StackPanel>
<!-- Report Bug -->
<StackPanel Grid.Row="1" Margin="0,16,0,0"
Visibility="{Binding IsBugOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="What went wrong?" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Height="110" AcceptsReturn="True" TextWrapping="Wrap"
Margin="0,0,0,12"
Text="{Binding BugReportText, UpdateSourceTrigger=PropertyChanged}"
VerticalScrollBarVisibility="Auto"/>
<TextBlock Text="Email (optional)" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
Text="{Binding BugReportEmail, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="Opens a pre-filled email to the LlamaCasty team." Style="{StaticResource YtLabel}"/>
<Button Content="Send Report" Command="{Binding SubmitBugCommand}"
Style="{StaticResource YtButton}" HorizontalAlignment="Right" Margin="0,16,0,0"/>
</StackPanel>
<!-- Feature Request -->
<StackPanel Grid.Row="1" Margin="0,16,0,0"
Visibility="{Binding IsFeatureOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="What would you like to see?" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Height="110" AcceptsReturn="True" TextWrapping="Wrap"
Margin="0,0,0,12"
Text="{Binding FeatureRequestText, UpdateSourceTrigger=PropertyChanged}"
VerticalScrollBarVisibility="Auto"/>
<TextBlock Text="Email (optional)" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
Text="{Binding FeatureRequestEmail, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="Opens a pre-filled email to the LlamaCasty team." Style="{StaticResource YtLabel}"/>
<Button Content="Send Request" Command="{Binding SubmitFeatureCommand}"
Style="{StaticResource YtButton}" HorizontalAlignment="Right" Margin="0,16,0,0"/>
</StackPanel>
<!-- About -->
<Grid Grid.Row="1" Margin="0,16,0,0"
Background="#171928"
Visibility="{Binding IsAboutOpen, Converter={StaticResource BoolToVis}}">
<!-- Hub view -->
<StackPanel Visibility="{Binding IsHubVisible, Converter={StaticResource BoolToVis}}">
<Image Source="/ytLive;component/Assets/llamacasty-logo.jpg" Height="125" Stretch="Uniform"
HorizontalAlignment="Center" Margin="0,0,0,12"/>
<TextBlock Text="{Binding AppVersionLabel}" Foreground="#a0a0b0" FontSize="12"
HorizontalAlignment="Center" Margin="0,4,0,0"/>
<TextBlock Text="Live streaming for YouTube — simple enough that even the most right-brained person can intuit it."
Foreground="#a0a0b0" FontSize="13" TextWrapping="Wrap" MaxWidth="380"
TextAlignment="Center" Margin="0,16,0,0"/>
<TextBlock Text="Free forever — live streams carry a brief &quot;made with LlamaCasty!&quot; flash. A subscription removes it and adds alerts."
Foreground="#888" FontSize="12" TextWrapping="Wrap" MaxWidth="380"
TextAlignment="Center" Margin="0,12,0,0"/>
<TextBlock Text="Made by gramps · llama chile shop" Foreground="#a0a0b0" FontSize="12"
HorizontalAlignment="Center" Margin="0,16,0,8"/>
<!-- Creator hub links -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,8">
<Button Content="llama chile shop on YouTube" Command="{Binding OpenChannelCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0"/>
<Button Content="Mastodon" Command="{Binding OpenMastodonCommand}"
Style="{StaticResource YtButtonSecondary}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Buy me a coffee" Command="{Binding OpenCoffeeCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0"/>
<Button Content="Unlock Premium" Command="{Binding OpenPremiumCommand}"
Style="{StaticResource YtButtonSecondary}"
IsEnabled="{Binding IsPremiumAvailable}"
ToolTip="Open the LlamaCasty subscription page"/>
<Button Content="Enter License Key" Command="{Binding OpenLicenseEntryCommand}"
Style="{StaticResource YtButtonSecondary}"
ToolTip="Activate a license key to remove the watermark"/>
</StackPanel>
<Button Content="Licenses &amp; legal" Command="{Binding OpenLicensesCommand}"
Style="{StaticResource YtButtonSecondary}" HorizontalAlignment="Center"
Margin="0,16,0,0"/>
</StackPanel>
<!-- Licensing/legal panel -->
<StackPanel Visibility="{Binding IsLicensesOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Third-party notices &amp; license texts" FontSize="15" FontWeight="Bold"
Foreground="#e0e0e0" Margin="0,0,0,8"/>
<Border Background="#10101e" CornerRadius="4" Padding="10" MaxHeight="360"
BorderBrush="#333" BorderThickness="1">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock Text="{Binding LicensesText}" Foreground="#b8b8c8" FontSize="11"
FontFamily="Consolas" TextWrapping="Wrap"/>
</ScrollViewer>
</Border>
<Button Content="← Back to About" Style="{StaticResource YtButtonSecondary}"
HorizontalAlignment="Left" Margin="0,12,0,0"
Click="BackToAbout_Click"/>
</StackPanel>
<!-- License key entry panel -->
<StackPanel Visibility="{Binding IsLicenseEntryOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Activate LlamaCasty Premium" FontSize="15" FontWeight="Bold"
Foreground="#e0e0e0" Margin="0,0,0,8"/>
<TextBlock Text="Enter your license key to remove the branding watermark."
Foreground="#a0a0b0" FontSize="12" TextWrapping="Wrap" Margin="0,0,0,12"/>
<TextBlock Text="License Key" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
Text="{Binding LicenseKey, UpdateSourceTrigger=PropertyChanged}"
MaxLength="100"/>
<Button Content="Activate" Command="{Binding ActivateLicenseCommand}"
Style="{StaticResource YtButton}" HorizontalAlignment="Right"
Margin="0,0,0,8"/>
<TextBlock Text="{Binding LicenseMessage}" Foreground="#a0a0b0" FontSize="12"
TextWrapping="Wrap" Margin="0,0,0,8"/>
<Button Content="← Back to About" Style="{StaticResource YtButtonSecondary}"
HorizontalAlignment="Left" Margin="0,8,0,0"
Click="BackToAbout_Click"/>
</StackPanel>
</Grid>
</Grid>
</Border>
</Grid>
</UserControl>
+22
View File
@@ -0,0 +1,22 @@
using System.Windows;
using System.Windows.Controls;
using ytLive.ViewModels;
namespace ytLive.Controls;
public partial class OverlayHost : UserControl
{
public OverlayHost()
{
InitializeComponent();
}
private void BackToAbout_Click(object sender, RoutedEventArgs e)
{
if (DataContext is MainViewModel vm)
{
vm.IsLicensesOpen = false;
vm.IsLicenseEntryOpen = false;
}
}
}
+1 -167
View File
@@ -108,173 +108,7 @@
<controls:BottomBar Grid.Row="3"/>
<!-- ═══ OVERLAY: Settings / Bug / Feature / About ═══ -->
<Grid Grid.RowSpan="3"
Visibility="{Binding IsAnyOverlayOpen, Converter={StaticResource BoolToVis}}">
<Button Background="#99000000" BorderThickness="0" Cursor="Hand"
Command="{Binding CloseOverlayCommand}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"/>
</ControlTemplate>
</Button.Template>
</Button>
<Border Background="#0A0C1A" CornerRadius="8" Width="650" MaxHeight="700"
VerticalAlignment="Center" HorizontalAlignment="Center"
Padding="28" BorderBrush="#333" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Header -->
<Grid Grid.Row="0">
<TextBlock Text="{Binding OverlayTitle}" FontSize="18" FontWeight="Bold"
Foreground="#e0e0e0" VerticalAlignment="Center"/>
<Button HorizontalAlignment="Right" ToolTip="Close" Style="{StaticResource IconButton}"
Command="{Binding CloseOverlayCommand}">
<Path Data="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"
Fill="#d0d0d0" Width="14" Height="14" Stretch="Uniform"/>
</Button>
</Grid>
<!-- App Settings: broadcast fields live in the preview's
Text pull-out now — this panel keeps app-level prefs only.
(Currently empty; the overlay shell stays for future use.) -->
<StackPanel Grid.Row="1" Margin="0,16,0,0"
Visibility="{Binding IsSettingsOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Default Location for Recordings" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}"
Text="{Binding RecordFolderDisplay, Mode=OneWay}"
IsReadOnly="True" Margin="0,0,0,12" Foreground="#a0a0b0"/>
<StackPanel Orientation="Horizontal">
<Button Content="Browse…" Command="{Binding ChooseRecordFolderCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0"/>
<Button Content="Use Downloads" Command="{Binding ResetRecordFolderCommand}"
Style="{StaticResource YtButtonSecondary}"/>
</StackPanel>
</StackPanel>
<!-- Report Bug -->
<StackPanel Grid.Row="1" Margin="0,16,0,0"
Visibility="{Binding IsBugOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="What went wrong?" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Height="110" AcceptsReturn="True" TextWrapping="Wrap"
Margin="0,0,0,12"
Text="{Binding BugReportText, UpdateSourceTrigger=PropertyChanged}"
VerticalScrollBarVisibility="Auto"/>
<TextBlock Text="Email (optional)" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
Text="{Binding BugReportEmail, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="Opens a pre-filled email to the LlamaCasty team." Style="{StaticResource YtLabel}"/>
<Button Content="Send Report" Command="{Binding SubmitBugCommand}"
Style="{StaticResource YtButton}" HorizontalAlignment="Right" Margin="0,16,0,0"/>
</StackPanel>
<!-- Feature Request -->
<StackPanel Grid.Row="1" Margin="0,16,0,0"
Visibility="{Binding IsFeatureOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="What would you like to see?" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Height="110" AcceptsReturn="True" TextWrapping="Wrap"
Margin="0,0,0,12"
Text="{Binding FeatureRequestText, UpdateSourceTrigger=PropertyChanged}"
VerticalScrollBarVisibility="Auto"/>
<TextBlock Text="Email (optional)" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
Text="{Binding FeatureRequestEmail, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="Opens a pre-filled email to the LlamaCasty team." Style="{StaticResource YtLabel}"/>
<Button Content="Send Request" Command="{Binding SubmitFeatureCommand}"
Style="{StaticResource YtButton}" HorizontalAlignment="Right" Margin="0,16,0,0"/>
</StackPanel>
<!-- About -->
<Grid Grid.Row="1" Margin="0,16,0,0"
Background="#171928"
Visibility="{Binding IsAboutOpen, Converter={StaticResource BoolToVis}}">
<!-- Hub view -->
<StackPanel Visibility="{Binding IsHubVisible, Converter={StaticResource BoolToVis}}">
<Image Source="/ytLive;component/Assets/llamacasty-logo.jpg" Height="125" Stretch="Uniform"
HorizontalAlignment="Center" Margin="0,0,0,12"/>
<TextBlock Text="{Binding AppVersionLabel}" Foreground="#a0a0b0" FontSize="12"
HorizontalAlignment="Center" Margin="0,4,0,0"/>
<TextBlock Text="Live streaming for YouTube — simple enough that even the most right-brained person can intuit it."
Foreground="#a0a0b0" FontSize="13" TextWrapping="Wrap" MaxWidth="380"
TextAlignment="Center" Margin="0,16,0,0"/>
<TextBlock Text="Free forever — live streams carry a brief &quot;made with LlamaCasty!&quot; flash. A subscription removes it and adds alerts."
Foreground="#888" FontSize="12" TextWrapping="Wrap" MaxWidth="380"
TextAlignment="Center" Margin="0,12,0,0"/>
<TextBlock Text="Made by gramps · llama chile shop" Foreground="#a0a0b0" FontSize="12"
HorizontalAlignment="Center" Margin="0,16,0,8"/>
<!-- Creator hub links -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,8">
<Button Content="llama chile shop on YouTube" Command="{Binding OpenChannelCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0"/>
<Button Content="Mastodon" Command="{Binding OpenMastodonCommand}"
Style="{StaticResource YtButtonSecondary}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Buy me a coffee" Command="{Binding OpenCoffeeCommand}"
Style="{StaticResource YtButtonSecondary}" Margin="0,0,8,0"/>
<Button Content="Unlock Premium" Command="{Binding OpenPremiumCommand}"
Style="{StaticResource YtButtonSecondary}"
IsEnabled="{Binding IsPremiumAvailable}"
ToolTip="Open the LlamaCasty subscription page"/>
<Button Content="Enter License Key" Command="{Binding OpenLicenseEntryCommand}"
Style="{StaticResource YtButtonSecondary}"
ToolTip="Activate a license key to remove the watermark"/>
</StackPanel>
<Button Content="Licenses &amp; legal" Command="{Binding OpenLicensesCommand}"
Style="{StaticResource YtButtonSecondary}" HorizontalAlignment="Center"
Margin="0,16,0,0"/>
</StackPanel>
<!-- Licensing/legal panel -->
<StackPanel Visibility="{Binding IsLicensesOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Third-party notices &amp; license texts" FontSize="15" FontWeight="Bold"
Foreground="#e0e0e0" Margin="0,0,0,8"/>
<Border Background="#10101e" CornerRadius="4" Padding="10" MaxHeight="360"
BorderBrush="#333" BorderThickness="1">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock Text="{Binding LicensesText}" Foreground="#b8b8c8" FontSize="11"
FontFamily="Consolas" TextWrapping="Wrap"/>
</ScrollViewer>
</Border>
<Button Content="← Back to About" Style="{StaticResource YtButtonSecondary}"
HorizontalAlignment="Left" Margin="0,12,0,0"
Click="BackToAbout_Click"/>
</StackPanel>
<!-- License key entry panel -->
<StackPanel Visibility="{Binding IsLicenseEntryOpen, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Activate LlamaCasty Premium" FontSize="15" FontWeight="Bold"
Foreground="#e0e0e0" Margin="0,0,0,8"/>
<TextBlock Text="Enter your license key to remove the branding watermark."
Foreground="#a0a0b0" FontSize="12" TextWrapping="Wrap" Margin="0,0,0,12"/>
<TextBlock Text="License Key" Style="{StaticResource YtLabel}" Margin="0,0,0,4"/>
<TextBox Style="{StaticResource YtTextBox}" Margin="0,0,0,12"
Text="{Binding LicenseKey, UpdateSourceTrigger=PropertyChanged}"
MaxLength="100"/>
<Button Content="Activate" Command="{Binding ActivateLicenseCommand}"
Style="{StaticResource YtButton}" HorizontalAlignment="Right"
Margin="0,0,0,8"/>
<TextBlock Text="{Binding LicenseMessage}" Foreground="#a0a0b0" FontSize="12"
TextWrapping="Wrap" Margin="0,0,0,8"/>
<Button Content="← Back to About" Style="{StaticResource YtButtonSecondary}"
HorizontalAlignment="Left" Margin="0,8,0,0"
Click="BackToAbout_Click"/>
</StackPanel>
</Grid>
</Grid>
</Border>
</Grid>
<controls:OverlayHost Grid.RowSpan="3"/>
<!-- ═══ WEB SOURCE HOST: hidden panel for WebView2 controls ═══ -->
<Canvas x:Name="WebViewHostPanel" Grid.RowSpan="4"
-9
View File
@@ -135,15 +135,6 @@ public partial class MainWindow : Window
: null;
}
private void BackToAbout_Click(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.MainViewModel vm)
{
vm.IsLicensesOpen = false;
vm.IsLicenseEntryOpen = false;
}
}
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is not DependencyObject original) return;
+1
View File
@@ -60,6 +60,7 @@ Progress (Commit N/12 · tag):
- [x] **Commit 15 · `refactor-commit-15`** — **PreviewPane** → `Controls/PreviewPane.xaml(.cs)` (Row 1 Col 1 center preview pane). Moved the 1920×1080 canvas (OverlayCanvas/SelectionOverlay/BrandFlashLayer/SocialBarElement), text pull-out, and the live-controls row (TRAX/game/mic meters + sliders + speakers + socials button). Code-behind moved: preview drag/select/resize/hit-test (Preview_Mouse*, HitHandle, HitElement), webcam-context-menu handlers (WebcamMenu_*), social-bar glow (SocialBar_*, SocialsButton_*, SocialGlowTick), audio sliders/speakers (VolumeSlider_*, GameVolumeSlider_*, MicSpeaker_*, GameSpeaker_*, SetSliderValueFromClick, FindParent), TraxButton_*. Window keeps the source-list left panel + thumbnail strip + edit UI, and routes selection-overlay via `_previewPane.UpdateSelectionOverlay(bool)` plus `_previewPane.IsClickInsidePreview/Drawer`. `BackgroundMenu_*` stayed in the window (used by the left-panel source context menu).
- [x] **Commit 16 · `refactor-commit-16`** — **SceneThumbnailStrip** → `Controls/SceneThumbnailStrip.xaml(.cs)` (Row 0 scene-picker strip). `Thumbnail_MouseLeftButtonDown` moved into the control (transitions via `DataContext.TransitionToScene`).
- [x] **Commit 17 · `refactor-commit-17`** — **LeftPanel** → `Controls/LeftPanel.xaml(.cs)` (Row 1 Col 0 left panel: layers/properties edit UI + live chat list). Moved ALL property-editor and chat handlers: `SourceNameBox_*`/`CommitSourceEdit`, `AddSourceButton_Click`, `OnChatMessagesChanged` (now self-subscribed on Loaded), `MirrorButton_Click`, `ShapeButton_Click`, `SourceMenu_Remove`, `BackgroundMenu_*`, `OpacitySlider_ValueChanged`, list drag-to-reorder (`_dragStartPoint`+`List_Preview*`+`EndListDrag`+`FindItemContainerAt`), and the snapshot family (`TakeSnapshot`, `WebcamBorder*`, `Countdown*`, `Web*`). Window exposes via `_leftPanel.IsClickInside(original)` (deselect guard) and `_leftPanel.OnSelectionChanged(vm)` (opacity label + snapshot). Pending: overlay host region.
- [x] **Commit 18 · `refactor-commit-18`** — **OverlayHost** → `Controls/OverlayHost.xaml(.cs)` (settings/bug/feature/about dialogs). `BackToAbout_Click` moved with it (uses inherited `DataContext`). Window keeps the thin `WebViewHostPanel` + `ToastArea` root stubs so name lookups are untouched (WebView2 ctor init, `NotificationService.InArea("ToastArea")`) — toast/WebView2 behavior unchanged. **Phase 2 COMPLETE** — MainWindow.xaml carved down to a 123-line shell over 6 controls (TopBar, HealthBanner, BottomBar, SceneThumbnailStrip, LeftPanel, PreviewPane, OverlayHost). Pending: session wrap-up (HANDOFF.md) + push on approval.
The partial `.cs` files land next to `MainViewModel.cs` in this folder as the split
proceeds; each partial carries its own `using`s and re-declares nothing from core.