Files
LlamaCasty/ViewModels/index.md
T
gramps e1006a04ab refactor: split Streaming out of MainViewModel — Commit 9/12
MainViewModel.Streaming.cs (787 lines) takes the full go-live/record flow
(StartSession/BeginRecordOnly/BeginGoLive/PrepareAndStartLiveAsync/StopStream,
record-file naming + rename-on-stop), health pipeline (FramePump.Handle +
poll, report-by-exception banner), session timer, REC/ON-AIR start/end UI,
the resolution quality dropdown over the 1920x1080 master, live window
visual tokens (UpdateLiveVisuals), record-folder commands, broadcast
pull-out (CurrentBroadcastId/BroadcastForm) and the stream-state setters
(StreamStatus/StreamVisibility) + their fields. Core drops 1890 -> 1119
lines. Cross-partial references (IsConnected, IsEditMode, BrandFlash*,
_audioMixer, ctor wiring) are safe on one partial class.

Zero behavior change; build 0 warnings; 246 pass, only the 2 known
pre-existing failures (audio-mix gain + round-clip grab). Docs: index.md
Streaming row added + core row trimmed in the same commit.
2026-08-30 08:45:36 -07:00

12 KiB
Raw Permalink Blame History

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. 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/Socials partials), OpenUrl/ComposeEmail/SubmitBug/SubmitFeature, window-geometry pass-through, SelectedElement/OnSelectedElementPropertyChanged, ShowPreviewPlaceholder/ShowChatInactiveMessage, BrandFlash*, WebView2 hosting (InitWebView2/RegisterLoadedWebSources/OnWebView2PreviewBitmapChanged), frame-pump wiring (construction + _rtmpUrlProvider seam + Dispose) — the pump's stream-state consumers live in the Streaming partial.
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).
MainViewModel.Streaming.cs Go-live/record + health + quality tiers + session UI. Stream state: StreamStatus/StreamVisibility setters + _streamStatus field, IsOffline/IsLive/IsLivePrivate. Go-live flow: StartSession (routes by pill state) → BeginRecordOnly (signed-out local recording) or BeginGoLive (GoLive dialog → captures title/description/visibility + BroadcastForm.CaptureGoLive → PrepareAndStartLiveAsync: reusable stream → CreateBroadcast (stores _currentBroadcastId) → empty-metadata warn → chat poll start (liveChatId) → _framePump.StartAsync() + health poll; failures → StreamStatus.Error, never a crash); StartRecordFile (auto-named ty-…-0000.mp4) + FinalizeRecordingAsync (rename modal, UniquePath); StopStream (posts Offline, stops chat/audio pipe/pump, clears _currentBroadcastId; session/token deliberately NOT cleared — sign-out stays explicit). Health: CurrentHealth/ResetHealth/ApplyHealth/OnFramePumpHealthUpdated (marshal FramePump.HealthUpdated off the stderr thread), PollHealthAsync/OnHealthPollTick/ApplyHealthIssue (report-by-exception banner HealthIssueBanner/HealthIssueBackground via StreamHealthReporter). Session timer: _liveTimer (ctor-wired) UpdateSessionTimer/OnLiveTimerTick/LiveElapsedText/_recDotPulse. Start/End UI: REC/ON-AIR pills (RecordPillOn/OnAirPillOn/CanToggleOnAir), ShowPrimaryStartButton/CanStartSession/ShowEndStreamButton/EndButtonText/ShowLogInButton, REC sign RecDotBrush/RecTextBrush/RecDotOpacity/OnAirBrush, live window tokens WindowTitle/TopBarBackground/PreviewGlow* via UpdateLiveVisuals; ChooseRecordFolder/ResetRecordFolder/_recordFolder/RecordFolderDisplay/DefaultRecordFolder. Resolution dropdown: QualityOptions/SelectedQuality/ResolutionHelp/ApplyStreamQuality + OutputRect*/DimRects/IsOutputCropped/ResolutionBadgeText over the 1920×1080 master. Pipeline bits: _framePump/_liveTimer/_healthPollTimer fields, BuildEncoderOptions. Commands StartStreamCommand/EndStreamCommand/ChooseRecordFolderCommand/ResetRecordFolderCommand. Broadcast pull-out: CurrentBroadcastId/BroadcastForm.
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 · refactor-commit-9 — Streaming → MainViewModel.Streaming.cs
  • 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.