diff --git a/ViewModels/SocialSlotViewModel.cs b/ViewModels/SocialSlotViewModel.cs
new file mode 100644
index 0000000..2b7aecf
--- /dev/null
+++ b/ViewModels/SocialSlotViewModel.cs
@@ -0,0 +1,183 @@
+using System.ComponentModel;
+using ytLive.Helpers;
+using ytLive.Models;
+using ytLive.ViewModels;
+
+namespace ytLive.ViewModels;
+public sealed record DialogEntry(SocialService Service, string Handle, string ProfileUrl, string? FediverseSoftware = null);
+
+///
+/// One of the six fixed dialog rows. Row 0 is always YouTube (signed-in account
+/// or a sign-in prompt); rows 1-5 hold validated entries, an empty slot, or — on
+/// the free tier — a locked "Premium" slot. Rows are a positional projection of
+/// the dialog's working list, so deleting an entry makes the ones above advance up.
+///
+public sealed class SocialSlotViewModel : ViewModelBase
+{
+ public int Index { get; }
+ public bool IsYoutube => Index == 0;
+
+ private SocialService _service = SocialService.Link;
+ public SocialService Service
+ {
+ get => _service;
+ set { if (SetProperty(ref _service, value)) OnPropertyChanged(nameof(LogoData)); }
+ }
+
+ public string LogoData => Service == SocialService.Fediverse
+ ? SocialServiceIcons.LogoDataForFediverse(FediverseSoftware)
+ : SocialServiceIcons.LogoDataFor(Service);
+ public string LockedIconData => SocialServiceIcons.LockedIconData;
+ public string DoNotIconData => SocialServiceIcons.DoNotIconData;
+
+ private bool _isLocked;
+ public bool IsLocked
+ {
+ get => _isLocked;
+ set
+ {
+ if (SetProperty(ref _isLocked, value))
+ {
+ OnPropertyChanged(nameof(ShowAddButton));
+ OnPropertyChanged(nameof(ShowDoNot));
+ OnPropertyChanged(nameof(DisplayText));
+ }
+ }
+ }
+
+ private bool _isFilled;
+ public bool IsFilled
+ {
+ get => _isFilled;
+ set
+ {
+ if (SetProperty(ref _isFilled, value))
+ {
+ OnPropertyChanged(nameof(ShowEditButton));
+ OnPropertyChanged(nameof(ShowDeleteButton));
+ OnPropertyChanged(nameof(ShowAddButton));
+ OnPropertyChanged(nameof(ShowDoNot));
+ OnPropertyChanged(nameof(DisplayText));
+ }
+ }
+ }
+
+ private bool _isSignIn;
+ public bool IsSignIn
+ {
+ get => _isSignIn;
+ set
+ {
+ if (SetProperty(ref _isSignIn, value))
+ {
+ OnPropertyChanged(nameof(ShowSignInButton));
+ OnPropertyChanged(nameof(ShowDeleteButton));
+ OnPropertyChanged(nameof(ShowLogo));
+ OnPropertyChanged(nameof(DisplayText));
+ }
+ }
+ }
+
+ private bool _isEditing;
+ public bool IsEditing
+ {
+ get => _isEditing;
+ set
+ {
+ if (SetProperty(ref _isEditing, value))
+ {
+ OnPropertyChanged(nameof(ShowEditBox));
+ OnPropertyChanged(nameof(ShowDisplay));
+ OnPropertyChanged(nameof(ShowEditButton));
+ OnPropertyChanged(nameof(ShowDeleteButton));
+ OnPropertyChanged(nameof(ShowAddButton));
+ OnPropertyChanged(nameof(ShowDoNot));
+ }
+ }
+ }
+
+ private bool _isValidating;
+ public bool IsValidating
+ {
+ get => _isValidating;
+ set
+ {
+ if (SetProperty(ref _isValidating, value))
+ {
+ OnPropertyChanged(nameof(ShowBusy));
+ OnPropertyChanged(nameof(ShowDoNot));
+ }
+ }
+ }
+
+ private string _handle = string.Empty;
+ public string Handle
+ {
+ get => _handle;
+ set
+ {
+ if (SetProperty(ref _handle, value))
+ OnPropertyChanged(nameof(DisplayText));
+ }
+ }
+
+ private string? _fediverseSoftware;
+ public string? FediverseSoftware
+ {
+ get => _fediverseSoftware;
+ set
+ {
+ if (SetProperty(ref _fediverseSoftware, value))
+ OnPropertyChanged(nameof(LogoData));
+ }
+ }
+
+ public string ProfileUrl { get; set; } = string.Empty;
+
+ private string? _error;
+ public string? Error
+ {
+ get => _error;
+ set
+ {
+ if (SetProperty(ref _error, value))
+ {
+ OnPropertyChanged(nameof(HasError));
+ OnPropertyChanged(nameof(ShowError));
+ OnPropertyChanged(nameof(ShowDoNot));
+ }
+ }
+ }
+
+ private string _editText = string.Empty;
+ public string EditText
+ {
+ get => _editText;
+ set => SetProperty(ref _editText, value);
+ }
+
+ /// The last text submitted for validation (used to skip re-validating
+ /// identical input — e.g. a LostFocus firing as the user clicks Cancel).
+ public string? LastValidatedInput { get; set; }
+
+ public string DisplayText =>
+ IsYoutube
+ ? (IsSignIn ? "Sign in to YouTube" : Handle)
+ : IsLocked ? "Unlock with Premium"
+ : IsFilled ? Handle
+ : "No social yet";
+
+ public bool HasError => !string.IsNullOrEmpty(Error);
+ public bool ShowError => HasError;
+ public bool ShowLogo => IsYoutube ? !IsSignIn : IsFilled;
+ public bool ShowBusy => IsValidating;
+ public bool ShowEditBox => IsEditing;
+ public bool ShowDisplay => !IsEditing && !IsValidating;
+ public bool ShowSignInButton => IsYoutube && IsSignIn && !IsValidating;
+ public bool ShowAddButton => !IsYoutube && !IsLocked && !IsFilled && !IsEditing && !IsValidating;
+ public bool ShowEditButton => !IsYoutube && IsFilled && !IsEditing && !IsValidating;
+ public bool ShowDeleteButton => (IsFilled || IsYoutube) && !IsEditing && !IsValidating;
+ public bool ShowDoNot => !IsYoutube && !IsLocked && (!IsFilled || HasError || IsValidating);
+
+ public SocialSlotViewModel(int index) => Index = index;
+}
diff --git a/ViewModels/SocialsDialogViewModel.cs b/ViewModels/SocialsDialogViewModel.cs
index 08864f2..3903ec5 100644
--- a/ViewModels/SocialsDialogViewModel.cs
+++ b/ViewModels/SocialsDialogViewModel.cs
@@ -8,187 +8,6 @@ using ytLive.Services;
namespace ytLive.ViewModels;
-///
-/// A validated social entry in the dialog's working copy (non-YouTube slots).
-///
-public sealed record DialogEntry(SocialService Service, string Handle, string ProfileUrl, string? FediverseSoftware = null);
-
-///
-/// One of the six fixed dialog rows. Row 0 is always YouTube (signed-in account
-/// or a sign-in prompt); rows 1-5 hold validated entries, an empty slot, or — on
-/// the free tier — a locked "Premium" slot. Rows are a positional projection of
-/// the dialog's working list, so deleting an entry makes the ones above advance up.
-///
-public sealed class SocialSlotViewModel : ViewModelBase
-{
- public int Index { get; }
- public bool IsYoutube => Index == 0;
-
- private SocialService _service = SocialService.Link;
- public SocialService Service
- {
- get => _service;
- set { if (SetProperty(ref _service, value)) OnPropertyChanged(nameof(LogoData)); }
- }
-
- public string LogoData => Service == SocialService.Fediverse
- ? SocialServiceIcons.LogoDataForFediverse(FediverseSoftware)
- : SocialServiceIcons.LogoDataFor(Service);
- public string LockedIconData => SocialServiceIcons.LockedIconData;
- public string DoNotIconData => SocialServiceIcons.DoNotIconData;
-
- private bool _isLocked;
- public bool IsLocked
- {
- get => _isLocked;
- set
- {
- if (SetProperty(ref _isLocked, value))
- {
- OnPropertyChanged(nameof(ShowAddButton));
- OnPropertyChanged(nameof(ShowDoNot));
- OnPropertyChanged(nameof(DisplayText));
- }
- }
- }
-
- private bool _isFilled;
- public bool IsFilled
- {
- get => _isFilled;
- set
- {
- if (SetProperty(ref _isFilled, value))
- {
- OnPropertyChanged(nameof(ShowEditButton));
- OnPropertyChanged(nameof(ShowDeleteButton));
- OnPropertyChanged(nameof(ShowAddButton));
- OnPropertyChanged(nameof(ShowDoNot));
- OnPropertyChanged(nameof(DisplayText));
- }
- }
- }
-
- private bool _isSignIn;
- public bool IsSignIn
- {
- get => _isSignIn;
- set
- {
- if (SetProperty(ref _isSignIn, value))
- {
- OnPropertyChanged(nameof(ShowSignInButton));
- OnPropertyChanged(nameof(ShowDeleteButton));
- OnPropertyChanged(nameof(ShowLogo));
- OnPropertyChanged(nameof(DisplayText));
- }
- }
- }
-
- private bool _isEditing;
- public bool IsEditing
- {
- get => _isEditing;
- set
- {
- if (SetProperty(ref _isEditing, value))
- {
- OnPropertyChanged(nameof(ShowEditBox));
- OnPropertyChanged(nameof(ShowDisplay));
- OnPropertyChanged(nameof(ShowEditButton));
- OnPropertyChanged(nameof(ShowDeleteButton));
- OnPropertyChanged(nameof(ShowAddButton));
- OnPropertyChanged(nameof(ShowDoNot));
- }
- }
- }
-
- private bool _isValidating;
- public bool IsValidating
- {
- get => _isValidating;
- set
- {
- if (SetProperty(ref _isValidating, value))
- {
- OnPropertyChanged(nameof(ShowBusy));
- OnPropertyChanged(nameof(ShowDoNot));
- }
- }
- }
-
- private string _handle = string.Empty;
- public string Handle
- {
- get => _handle;
- set
- {
- if (SetProperty(ref _handle, value))
- OnPropertyChanged(nameof(DisplayText));
- }
- }
-
- private string? _fediverseSoftware;
- public string? FediverseSoftware
- {
- get => _fediverseSoftware;
- set
- {
- if (SetProperty(ref _fediverseSoftware, value))
- OnPropertyChanged(nameof(LogoData));
- }
- }
-
- public string ProfileUrl { get; set; } = string.Empty;
-
- private string? _error;
- public string? Error
- {
- get => _error;
- set
- {
- if (SetProperty(ref _error, value))
- {
- OnPropertyChanged(nameof(HasError));
- OnPropertyChanged(nameof(ShowError));
- OnPropertyChanged(nameof(ShowDoNot));
- }
- }
- }
-
- private string _editText = string.Empty;
- public string EditText
- {
- get => _editText;
- set => SetProperty(ref _editText, value);
- }
-
- /// The last text submitted for validation (used to skip re-validating
- /// identical input — e.g. a LostFocus firing as the user clicks Cancel).
- public string? LastValidatedInput { get; set; }
-
- public string DisplayText =>
- IsYoutube
- ? (IsSignIn ? "Sign in to YouTube" : Handle)
- : IsLocked ? "Unlock with Premium"
- : IsFilled ? Handle
- : "No social yet";
-
- public bool HasError => !string.IsNullOrEmpty(Error);
- public bool ShowError => HasError;
- public bool ShowLogo => IsYoutube ? !IsSignIn : IsFilled;
- public bool ShowBusy => IsValidating;
- public bool ShowEditBox => IsEditing;
- public bool ShowDisplay => !IsEditing && !IsValidating;
- public bool ShowSignInButton => IsYoutube && IsSignIn && !IsValidating;
- public bool ShowAddButton => !IsYoutube && !IsLocked && !IsFilled && !IsEditing && !IsValidating;
- public bool ShowEditButton => !IsYoutube && IsFilled && !IsEditing && !IsValidating;
- public bool ShowDeleteButton => (IsFilled || IsYoutube) && !IsEditing && !IsValidating;
- public bool ShowDoNot => !IsYoutube && !IsLocked && (!IsFilled || HasError || IsValidating);
-
- public SocialSlotViewModel(int index) => Index = index;
-}
-
///
/// View model for the Social Media Site Promotion dialog. Owns the working copy
/// of the social bar config: the six fixed slots, the sign-in gate, per-row
diff --git a/ViewModels/index.md b/ViewModels/index.md
index 4c69042..cce9911 100644
--- a/ViewModels/index.md
+++ b/ViewModels/index.md
@@ -69,5 +69,6 @@ commit, tag `refactor-commit-N`, no per-commit push).
- [x] **Commit A · `refactor-commit-A`** — **core** → `MainViewModel.cs` 758 → **495**. Moved source add/remove/image block to new **`MainViewModel.Sources.cs`** (234 lines: `AddSource`, `NextSourceName`, `AddImage`, `BuildImageCandidates`, `PickImageBytes`, `AddAsset`, `AddImageSource`, `AddReusedImage`, `RemoveElement`) and WebView2 hosting (`RegisterLoadedWebSources`, `InitWebView2`, `OnWebView2PreviewBitmapChanged`) to new **`MainViewModel.Web.cs`**. Pruned now-dead usings (`Microsoft.Win32`, `NAudio.CoreAudioApi`, `System.Windows.Media.Imaging`, `Collections.ObjectModel/Specialized`). Pending: Streaming, Background, SocialsDialogViewModel, LayoutStore.
- [x] **Commit B · `refactor-commit-B`** — **Streaming** → `MainViewModel.Streaming.cs` 789 → **330** + new **`MainViewModel.Streaming.Operations.cs`** (459 lines). Moved the go-live/record/health operation bodies (`StartSession`, `BeginRecordOnly`, `BeginGoLive`, `StartRecordFile`, `PrepareAndStartLiveAsync`, `PollHealthAsync`/`OnHealthPollTick`/`ApplyHealthIssue`, `StopStream`, `FinalizeRecordingAsync`, `UniquePath`, `ChooseRecordFolder`, `ResetRecordFolder`, `DefaultRecordFolder`, `OnFramePumpFailed`, `OnFramePumpHealthUpdated`, `ApplyHealth`, `ResetHealth`, `BuildEncoderOptions`) into the Operations partial; Streaming.cs keeps state + quality/resolution tiers + session timer + command declarations. Pruned dead usings both files. Pending: Background, SocialsDialogViewModel, LayoutStore.
- [x] **Commit C · `refactor-commit-C`** — **Background** → `MainViewModel.Background.cs` 567 → **418** + new **`MainViewModel.Background.Model.cs`** (163 lines). Moved the static scene-model helpers (`LoadBackgroundImage`, `CreatePlaceholderSnapshot`, `EnsureBackground`, `CreateBackground`, `NormalizeBackgrounds`, `AllBackgrounds`, `ResolveAutoCaptureKey`, `MonitorKeyPrefix`) into the Model partial; Background.cs keeps capture/live state + operations (`UpdateActiveBackground`, `UpdateBackgroundImage`, `RefreshSnapshotsAsync`, `ReacquireScreenCaptures`, `OnScreenPreviewBitmapChanged`, `OnFullscreenMonitorChanged`, `RefreshBackgroundAutoCapture`, `BrowseBackground`, `ClearBackgroundCaptureAsync`, `RedesignateBackgroundAsync`, `ChangeBackgroundCaptureAsync`, `SetBackgroundCapture`, `DefaultBackgroundAsset`, `HealBackgrounds`, `StampDefaultBackgroundAsset`). Pending: SocialsDialogViewModel, LayoutStore.
+- [x] **Commit D · `refactor-commit-D`** — **SocialsDialog** → `SocialsDialogViewModel.cs` 515 → **334** + new **`SocialSlotViewModel.cs`** (183 lines). Moved the `DialogEntry` record + the row-level `SocialSlotViewModel` (six-fixed-slot row VM: service/logo/lock/edit/validate state + computed `Show*` flags) into its own file; `SocialsDialogViewModel.cs` keeps the dialog VM (sign-in gate, validation orchestration, commit). `DialogEntry` stays `public` in the same namespace so both files see it. Pending: LayoutStore.
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.
\ No newline at end of file