MainViewModel.Socials.cs: social-bar state (_socials/_socialBarFrame/ _socialValidator), bar props (HasSocials/SocialBarVisible/SocialBarDotBrush/ SocialBarGlowBrush/Socials/SocialBarTop), OpenSocialDialog + NotifySocialsChanged + RenderSocialBarFrame (SocialBarRenderer), fediverse heal (HealFediverseSoftwareAsync/HealFediverseSoftwareInBackground), SetSocialBarPosition/ToggleSocialBarPosition, OpenSocialDialogCommand. BarOnBrush/BarOffBrush/CreateBrush stay in core (shared tokens used by Audio/Trax partials); core keeps ctor command assignment, LoadLayout, SaveLayoutNow, and the compositor socialBar seam. Docs: index.md restructured — each landed partial (Account..Trax) now owns its own table row, the core row shrinks to only what remains in MainViewModel.cs, and the split tracker is a compact code<->commit map. Verify: 0 warnings, 246 passed, 2 known failures only.
11 KiB
ViewModels — index
MVVM layer. See schema.md for the memory-map conventions.
Each landed MainViewModel partial owns a row here describing its area; the
MainViewModel.cs row describes only what still lives in core. Docking most
areas' docs in their own rows keeps this index small to read.
| File | Purpose |
|---|---|
MainViewModel.cs |
Core. public partial class MainViewModel : ViewModelBase. Broadcast/stream state: go-live (BeginGoLive accepts the GoLive dialog → StreamVisibility always "Private" → CreateBroadcastAsync storing _currentBroadcastId for the bind/transition; failure → StreamStatus.Error + _framePump.StartAsync()), End (StopStream — signs out, clears session/token/_currentBroadcastId, crash-safe), StreamStatus/StreamVisibility setters, IsOffline/IsLive/IsLivePrivate. Health stats: CurrentHealth/ResetHealth/OnFramePumpHealthUpdated marshal FramePump.HealthUpdated (encoder bitrate/FPS/dropped/duration) off the stderr thread onto the UI thread. Start/End UI: REC/ON-AIR pills (RecordPillOn/OnAirPillOn, CanStartSession, ShowPrimaryStartButton/ShowEndStreamButton/EndButtonText), REC sign (RecDotBrush/RecTextBrush/RecDotOpacity, pulsing live via _recDotPulse), ShowLogInButton, ChooseRecordFolder/ResetRecordFolder/_recordFolder. Resolution dropdown: QualityOptions/SelectedQuality/ResolutionHelp/ApplyStreamQuality computes the output rect over the 1920×1080 master (OutputRectX/Y/W/H, DimRects, IsOutputCropped, ResolutionBadgeText). Source commands + element rows: scenes are pure selection rows; sources carry edit/visibility/trash (EditElementCommand sets element.IsEditing, ToggleElementVisibilityCommand flips IsVisible, RemoveSourceCommand; Backdrop trash slot reserved via HiddenBoolToVisibilityConverter — TASK 13); shared NextSourceName (Image, Image2, Image3… no-space increment from actual names — used by AddSource + AddReusedImage), AddImageCommand. Overlays/lifecycle: fields + ctor wiring (all command assignments, mixer/camera/chat/hotkey/subscription hookups), Shutdown/SaveLayoutNow/ScheduleSave/LoadLayout, SetPanel/ShowOverlay/ShowLicenses, CreateBrush + shared BarOnBrush/BarOffBrush (referenced by Audio/Trax partials), OpenUrl/ComposeEmail/SubmitBug/SubmitFeature, window-geometry pass-through, SelectedElement/OnSelectedElementPropertyChanged, ShowPreviewPlaceholder/ShowChatInactiveMessage, BrandFlash*, WebView2 hosting (InitWebView2/RegisterLoadedWebSources/OnWebView2PreviewBitmapChanged). |
MainViewModel.Account.cs |
Session + connected account: LoadSavedSessionAsync (DPAPI restore at startup), SignInAsync/ChangeAccountAsync/SignOutYouTubeAsync, IsConnected/AccountAvatarUrl/AccountDisplayName/AccountStatusLightToolTip via SyncConnectedAccount (cleared on End). |
MainViewModel.License.cs |
Polar license (freemium): LicenseKey/LicenseMessage/IsLicenseValidating/IsLicenseEntryOpen/IsHubVisible/IsPremium, OfflineGracePeriod, ValidateLicenseAtStartupAsync/ActivateLicenseAsync. |
MainViewModel.Scenes.cs |
Five-scene catalog: Scenes + StagedScene/LiveScene/IsEditMode, AddScene (canonical names only), MissingScenes/ShowAddScene/AddSceneCommand; scene wiring OnScenesChanged/WireScene/UnwireScene; transitions SelectedTransitionType/TransitionDurationMs/TransitionToScene/ApplyTransitionToLive; global hotkeys HandleHotkey/OpenHotkeyConfig/RefreshHotkeyLabels; scene-gating hints (CanChangeBackground). |
MainViewModel.Background.cs |
Screen backdrop (Live-only by policy): ScreenCaptureManager + ScreenCaptureSourceFactory (WinRT GraphicsCapture); background model EnsureBackground/CreateBackground/NormalizeBackgrounds/HealBackgrounds/StampDefaultBackgroundAsset; screen capture ReacquireScreenCaptures/ResolveAutoCaptureKey/RedesignateBackgroundAsync/ClearBackgroundCaptureAsync/SetBackgroundCapture/OnScreenPreviewBitmapChanged/OnFullscreenMonitorChanged/RefreshBackgroundAutoCapture/BackgroundShowDesktop (keys on full-screen-game monitor or primary display via Win32FullScreenDetector); display picker Displays/SetBackgroundDisplayCommand; preview fns ActiveBackgroundImage/BackgroundImage/UpdateActiveBackground/UpdateBackgroundImage/RefreshSnapshotsAsync/LoadBackgroundImage/CreatePlaceholderSnapshot; ChangeCaptureCommand/RefreshCaptureCommand/ToggleShowDesktopCommand/BrowseBackgroundCommand. ShowPreviewPlaceholder stays in core. |
MainViewModel.Webcam.cs |
Webcam identity: _webcam/ReacquireWebcam/SwapWebcamIdentityAsync; per-scene configs AddWebcamToStagedSceneAsync/ShowWebcamInStagedScene/ChangeWebcamAsync (Windows picker always opens); capture OnCameraPreviewBitmapChanged/OnCameraFailed; gating CanAddWebcam/CanChangeWebcam/CanShowWebcamInStagedScene/IsWebcamSelected/WebcamError; size safeguards WebcamMax*/ClampWebcamToBounds/HealLegacySquareRect; AddWebcamCommand/ChangeWebcamCommand/ShowWebcamCommand. |
MainViewModel.Audio.cs |
Mic bar: read-only realtime meter AudioLevel/MeterFillWidth/MeterBrush (fill = Math.Min(1, AudioLevelMeter.ToDisplay((float)AudioLevel) * MicVolume), green→yellow→red at 60%/80% zones), MicVolume (0.8 default; drives MicMuted ⇔ volume 0; unmute flash BeginVolumeFlash/EndVolumeFlash/CancelVolumeFlash), MicSourceName (left-justified inside the bar, persists via LayoutStore.Settings), ToggleMicMute/PickMicrophone (MicPickerDialog; picked device swaps the live source via _audioMixer.RestartMic(), loopback keeps running), status dot MicStatus/MicStatusBrush/MicStatusToolTip (Models.MicStatus: green Connected / yellow Problem / red no-device), capture starts once at startup (StartMicCaptureAsync) — NOT go-live. Game audio bar: overlaid bottom-center, always visible (show/hide gating removed 2026-08-15); GameAudioLevel (loopback via OnLoopbackLevelChanged), GameAudioVolume/GameMuted/GameMuteText/ToggleGameMute/PushSystemVolume, mirror volume flash SetGameVolumeAdjusting/BeginGameVolumeFlash/EndGameVolumeFlash/CancelGameVolumeFlash. Mixer handlers OnMicLevelChanged/OnLoopbackLevelChanged/OnMicConnected/OnMicFailed. |
MainViewModel.Trax.cs |
Background music (TASK 9, rides the loopback into the stream): _musicPlayer (MusicPlayer), IsMusicPlaying/TraxError/TraxDotBrush/TraxToolTip, interaction TraxLeftClick/TraxRightClick/PickTraxTrack, playback LoadTrax/PlayTrax/PauseTrax/OnTraxPlaybackEnded/RefreshTraxUi, UpdateTraxLocalGain (mirrors the game bar's mute onto MusicPlayer.LocalGain so the track ducks/restores with desktop audio). |
MainViewModel.Socials.cs |
Social bar (global resource, overlaid on every scene): _socials/_socialBarFrame/_socialValidator (ISocialValidator/HttpSocialValidator), HasSocials/SocialBarVisible/SocialBarDotBrush/SocialBarGlowBrush/Socials/SocialBarTop, OpenSocialDialogCommand/OpenSocialDialog (spawns SocialsDialogViewModel + window; saved copy replaces _socials), NotifySocialsChanged/RenderSocialBarFrame (SocialBarRenderer → immutable _socialBarFrame the frame pump reads), fediverse heal HealFediverseSoftwareAsync (static, test-facing) + HealFediverseSoftwareInBackground, bar edge toggle SetSocialBarPosition/ToggleSocialBarPosition. BarOnBrush/BarOffBrush/CreateBrush stay in core (shared tokens). |
GoLiveViewModel.cs |
Start Stream dialog: account row (saved channel shown with Change Account, or Sign in to YouTube; Start gated on IsSignedIn/IsBusy) + title/description + locked Visibility (always "Private" — ship step 7, no dropdown), start/cancel requests |
About hub (TASK 11, in MainViewModel) |
the About overlay is the in-app creator hub: ChannelUrl/MastodonUrl/CoffeeUrl (public consts, live) + PremiumUrl (tabled billing seam — TASK 12 → the greyed "Unlock Premium" button lights up when filled); OpenLicensesCommand → ShowLicenses() loads the shipped THIRD-PARTY-NOTICES.txt into LicensesText and sets IsLicensesOpen (in-app scrolling panel, never the OS viewer); BackToAbout_Click in MainWindow.xaml.cs flips it off. |
ReuseImageViewModel.cs |
Add Image dialog: candidate list (ReuseImageCandidate), reuse/new/cancel |
CameraPickerViewModel.cs |
Add Webcam dialog: async camera list (loading / has / none states), UseRequested(CameraDeviceInfo)/CancelRequested |
MicPickerViewModel.cs |
Select Microphone dialog (voice source): async mic list (loading / has / none states), UseRequested(MicrophoneDeviceInfo)/CancelRequested |
SocialsDialogViewModel.cs |
Social Media Site Promotion dialog (six fixed slots, SocialSlotViewModel): row 0 = YouTube sign-in gate (sign-in provider fills the channel, delete = sign-out); rows 1-5 validated entries / empty / freemium locks 2-5 (isPremium seam); DetectService → ISocialValidator on confirm (input snaps to text + real logo on success, red do-not + stays editable on failure); CanSave blocked while any slot edits/validates/errors; BarEnabled switch; commit via CommittedEntries + BuildSocialsConfig() (YouTube first) + SaveRequested/CancelRequested. WPF-free — the window owns the dialogs (delete confirm, sign-in gate) and calls StartEdit/ConfirmEdit/DeleteSlotAsync. |
Related: Models/index.md for the data; Services/index.md
for what the ViewModels drive; base class in Helpers/ViewModelBase.cs.
MainViewModel partial split (in progress)
MainViewModel.cs is being split into one file per functional area, zero behavior
change, one commit per partial (each commit a roll-back point). Commit naming:
titles carry the series id (Commit N/12), tags are refactor-commit-N — e.g.
git show refactor-commit-1. Every partial lands with a ViewModels/index.md
update in the SAME commit. Each partial's docs live in its table row above; this
tracker is just the code↔commit map.
Progress (Commit N/12 · tag):
- Commit 1/12 ·
refactor-commit-1— Account →MainViewModel.Account.cs - Commit 2/12 ·
refactor-commit-2— License →MainViewModel.License.cs - Commit 3/12 ·
refactor-commit-3— Scenes →MainViewModel.Scenes.cs - Commit 4/12 ·
refactor-commit-4— Background →MainViewModel.Background.cs - Commit 5/12 ·
refactor-commit-5— Webcam →MainViewModel.Webcam.cs - Commit 6/12 ·
refactor-commit-6— Audio →MainViewModel.Audio.cs - Commit 7/12 ·
refactor-commit-7— Trax →MainViewModel.Trax.cs - Commit 8/12 ·
refactor-commit-8— Socials →MainViewModel.Socials.cs - Commit 9/12 — Streaming (go-live/record + health + quality tiers + media source pipeline)
- Commit 10/12 — Chat (YouTube chat + mock preview + chatbox render)
- Commit 11/12 — Overlays (settings/bug/feature/about panels + license entry)
The partial .cs files land next to MainViewModel.cs in this folder as the split
proceeds; each partial carries its own usings and re-declares nothing from core.