Commit Graph

55 Commits

Author SHA1 Message Date
gramps 9b56e88800 Revert ab29ec8: container==content auto-fit made it worse — back to alpha-crop with the container at element size
The container tracks the element's own Width/Height again; the crop stays in the bitmap.
2026-08-28 12:09:13 -07:00
gramps ab29ec801d Web source: container == content — element grid + selection box sized to the cropped frame's canvas extent
- FindContentBounds already trimmed the bitmap; the grid/box still rendered at the uncropped user
  size, so Fill stretched the tight widget into a bigger frame = dead-between-content-and-border.
- ContentCanvasSize maps the crop rect to canvas units (1920/pixW per axis, DPI-correct) and the
  preview event now carries (bitmap, canvasW, canvasH).
- OnWebView2PreviewBitmapChanged sets the element Width/Height to that size; the grid and the
  SelectionOverlay (both bound to the element's Width/Height) now equal the widget exactly.
- IsPreviewDragging (set in MainWindow drag/resize) suppresses auto-fit mid-gesture.
- Tests 10/10 (alpha bounds, canvas-size mapping incl. DPI, control size); 0 warnings.
2026-08-28 12:08:04 -07:00
gramps bccdb4892c Web source: crop to the frame's ALPHA bounding box — the widget's true rendered extent, no DOM timing, can't break the render
- FindContentBounds scans the Bgra32 capture for the bounding box of non-transparent pixels
  (background is injected transparent, so alpha = widget extent). Crop to that rect + Stretch=Fill
  maps the widget flush under the selection box: full-canvas widgets fall through to the full frame
  (identical to the confirmed-good image); floating widgets get tight-fitted with no dead space.
- Removes QueryContentBoundsAsync + the JS script entirely — the DOM-union attempt (a5b9952) broke
  rendering by measuring an unsettled layout on NavigationCompleted; alpha measurement is immune.
- 3 new unit tests: tight box, no-alpha no-crop, full-alpha no-crop. 7/7 pass, 0 warnings.
2026-08-28 11:53:36 -07:00
gramps 70fe2b80b2 Web source: render at canvas size 1920x1080 — full-bleed widgets no longer truncated to upper-left 2026-08-28 11:16:46 -07:00
gramps 23874c2dd6 Web source: stable viewport (snapshot at register) + Stretch=Fill + overflow:hidden — no reflow, no scrollbars, no dead space 2026-08-28 10:56:23 -07:00
gramps 8d793af91a Web source: geometry-agnostic viewport==box model — viewport locked to element box, Stretch=Fill, no dead space 2026-08-28 10:48:53 -07:00
gramps e917c41a48 Web source: fix rendering — fixed 1920×1080 viewport (OBS pattern), Stretch=Uniform, startup registration 2026-08-28 10:23:59 -07:00
gramps fd6e02b687 Web source: off-screen WebView2 capture + layout persistence + transparent bg
Bundle exception (2026-08-27): this lands prior-session work plus today's
transparency fix as one commit per creator instruction.

WebView2Manager (new): off-screen WebView2 control parked at -5000/-5000 in
the window's visual tree, captures via CapturePreviewAsync every 100 ms into
a BGRA8 VideoFrame. Source Width/Height sync to the control via
PropertyChanged. JS transparent-background injection runs on every
NavigationStarting/NavigationCompleted so the page's own CSS does not paint
white over the capture.

Transparency fix: DefaultBackgroundColor = System.Drawing.Color.Transparent
set on the WPF control before EnsureCoreWebView2Async. The WPF SDK doc says
the value is forwarded to the controller on init; the IDL (ICoreWebView2-
Controller2) confirms alpha=0 makes the capture alpha-preserving. The
prior-session 'CoreWebView2.BackgroundColor' path was the wrong property
name and has been replaced.

LayoutStore: WebUri column added (idempotent ALTER TABLE) and round-tripped
on save/load (schema v11).

MainViewModel: WebView2Manager constructed on Loaded; loaded web sources
re-registered; Source.WebUri PropertyChanged forwards to manager.

MainWindow: off-screen host Canvas added to visual tree.

Tests: 3 scale-sync + WebView2_DefaultBackgroundColor_Is_Transparent +
LayoutStorePersistenceTests.WebSource_WebUri_Persists. 234/236 pass
(2 pre-existing failures unchanged).

Scale bug (DPI/physical-pixel mismatch) still open.
2026-08-27 08:52:39 -07:00
gramps 3a1484b3c5 TASK 20 step 2: hotkey config UI + modifier chord persistence + conflict detection 2026-08-26 06:48:14 -07:00
gramps 89ab83be2e Update Broadcast button: diff before push, offline=local only, live=API; Go Live empty-field prompt 2026-08-25 17:25:25 -07:00
gramps d9e352f996 Text tab toggles the drawer; click-away collapses it 2026-08-24 14:49:15 -07:00
gramps e6baa55867 Fix logo-click crash (Run is not a Visual); Text tab always visible; avatar initial fallback 2026-08-24 14:44:16 -07:00
gramps b988e6d72e Broadcast Text pull-out (Live screen) + launch geometry 1920x1040/1366x768 with restore 2026-08-24 11:04:41 -07:00
gramps 7357836f9b ON-AIR sign right of REC: gray offline, red public, green private 2026-08-24 10:22:18 -07:00
gramps b0bace7432 TASK 20 step 1: global hotkeys F1-F9 (scenes, stream, mutes, TRAX) 2026-08-24 09:32:54 -07:00
gramps 708b336da7 YouTube Chat layer: one-per-layout gate + legacy label heal (TASK 27) 2026-08-24 09:08:03 -07:00
gramps ef710c5537 Webcam menu gates on the app-wide identity (TASK 26) 2026-08-24 08:56:17 -07:00
gramps 7dd140a4c0 Backgrounds: one locked Background per screen + mini rule (TASK 25) 2026-08-23 13:33:33 -07:00
gramps b400ab59c5 Toasts: non-blocking notifications replace MessageBoxes + log-only failures (TASK 24) 2026-08-23 12:25:04 -07:00
gramps 809c066184 Fix: derive IsBackground from Type on layout load (legacy rows healed) 2026-08-23 07:25:54 -07:00
gramps d2114c7e1c the ai is an overthinking fucking moron who needs it's plug pulled until such time it can repeat a similar action with its head and ass 2026-08-23 06:25:38 -07:00
gramps b74f15ff14 Fix: IsBackground derived from SourceType + per-scene background property tests
- Source.Type setter auto-sets IsBackground = true when Type is Background
- SceneElement.IsBackground: expression-bodied → virtual auto-property
- Source.IsBackground: new → override (WPF binding fix)
- Three new tests: IsBackground per-scene, derived from type, UseDefaultBackground default
2026-08-22 18:16:11 -07:00
gramps 4b9a8e515f Control Surface UX: pill toggle, background rename, per-scene properties
- Sliding pill toggle (green ON / red OFF) replaces checkbox
- Geometry mismatch error for custom backgrounds
- Backdrop -> Background: C# rename, SQL column rename with migration
- Source.IsBackground: new -> override (fixes WPF binding)
- Legacy 'Backdrop' display name migrated to 'Background'
- Per-scene UseDefaultBackground + CustomBackgroundPath
- Asset files renamed backdrop -> background
- StagedScene/LiveScene split, thumbnail strip, edit gating
- TransitionService (Cut/Fade/Move)
- Context menu: Capture Desktop, Refresh Desktop
- Resolution badge removed, ON-AIR status light added
2026-08-22 18:10:59 -07:00
gramps 0bd17ab428 feat(ui): Show Desktop toggle, avatar login, chat preview, backdrop seeding, border animations
- Source.ShowDesktop bool persisted in DB (schema migration + LayoutStore)
- ClearBackdropCaptureAsync clears CaptureKey + VideoImageSource for static fallback
- Top bar: red/green/pulsing status light, avatar via BitmapImage, Log In button
- ShowStartStream now requires IsConnected
- RunMockChatPreviewAsync: simple async loop, ChatPreviewEnabled property on Source
- EnsureDefaultBackdrop called on every scene switch, seeds all 5 scenes
- SeedEndingBackdrop replaces existing, SeedChatBackdrop added
- SceneCatalog: Chat scene display name corrected (layer = 'YouTube Chat')
- Left panel spacing consistency, context menu reordered
- Border animation rendering via code-behind Storyboard approach
- 3 new backdrop assets (ending-backdrop.jpg, chat-backdrop.jpg, live-backdrop.jpg)
- Build 0 warnings, 178 tests passing
2026-08-20 18:02:41 -07:00
gramps e97406d649 v1 task queue: add TASK 19-23 (transitions, hotkeys, media source, audio sync, studio mode)
- TASKS.md: added 5 new tasks for v1 feature completeness, execution order, updated backlog
- MONETIZATION.md: added v1 feature completeness section (competitive gap analysis)
- ai.md: added v1 task queue note in Current limitations/TODOs
- HANDOFF.md: updated session state and next steps
- ViewModels/SocialsDialogViewModel.cs: removed isPremium parameter (social slot locks)
- ViewModels/MainViewModel.cs: removed isPremium argument from SocialsDialogViewModel constructor
- ytLive.Tests/SocialsDialogViewModelTests.cs: updated 5 tests for unlocked slots
2026-08-19 11:51:32 -07:00
gramps a3dd0791db fix: creator feedback batch Branch 1 — TRAX volume, tooltip, meter boost, slider click, backdrop
- TRAX: LocalGain now applies 0.2× factor so music is quieter relative to desktop audio slider
- TRAX tooltip: switched to ToolTipService with Placement=Top to fix z-order in Viewbox overlay
- Mic meter: MeterLevel multiplied by 1.2× before clamping for ~20% higher visual readout
- Volume sliders: click anywhere on track to jump thumb to that position before dragging
- Backdrop: Image Visibility now binds to BackdropVisible; compositor checks IsVisible before blit
- Renamed 'Backdrop' to 'Game Capture' in EnsureBackdrop + test assertions
2026-08-17 07:45:29 -07:00
gramps 0fac1e6a5a TASK 5 items 1-3: reusable stream, health banner, docs refresh
- Reusable stream: GetOrCreateReusableStreamAsync, cached via LayoutStore, bound at broadcast insert
- Health monitoring: GetStreamHealthAsync polls liveStreams.status, report-by-exception banner
- Go-live order: ensure stream → create broadcast → then start pump (URL must exist first)
- Full broadcast form scoped (TASK 5 items 6-7) in TASKS.md + ai.md
- TASK 23 trimmed to technical scope, TASK 26 added (marcom/launch kit)
- Confidential files: MONETIZATION.md + MARCOM.md (gitignored, not committed)
- .gitignore updated for confidential business files
- HANDOFF.md updated with session state and next steps
2026-08-16 17:48:37 -07:00
gramps 92f1471ab4 TASK 25: master limiter on the live mix (2026-08-15, queued from the TRAX discussion) — the review confirmed two of the creator's three instincts were already satisfied (MediaFoundationReader streams from disk so file size needs no guard; the MusicPlayer 0.20 bed rides the same loopback gain as the game, so music is always exactly 20% of the desktop volume by construction) and found one real gap: AudioMixer.FillAndMix summed mic + loopback with no output ceiling, so hot gains could pass 0 dBFS and clip the AAC encode. New pure Services/Audio/MasterLimiter.cs: -1 dBFS ceiling (Ceiling = 0.891), instant attack per frame (a hot frame is scaled exactly to the ceiling — no overshoot), smoothed release toward unity so loud passages don't pump, gain never exceeds 1. Applied at the end of FillAndMix right before the pipe write. 3 unit tests (trim-to-ceiling, never-boost, recovery) + ONE integration test (MasterLimiter_CapsTheLiveMix_OnThePipe: a 0.95 loopback bed capped to exactly 0.891 on the wire while staying audible). Docs in the same commit: ai.md go-live audio section, Services/index.md new row, TASKS.md TASK 25 record, HANDOFF.md shipped state. Build 0 warnings, 193 tests passing (189 + 4 new) 2026-08-15 16:10:16 -07:00
gramps 24f16b2cd9 Game audio bar is now ALWAYS visible (TASK 4 gating removed, 2026-08-15): the show/hide machinery kept hiding the desktop/game meter whenever no full-screen game with sound was up or no TRAX music played, which the creator read as the meter being lost. Deleted IGameAudioDetector/GameAudioDetector/GameAudioHysteresis (Services/), the VM's 250ms DispatcherTimer poll + OnGameAudioActiveChanged/OnGameAudioPollTick + IsGameAudioBarVisible + _gameAudioBarActive/_gameAudioTimer/_gameAudioDetector, and the two detector test files (GameAudioDetectorTests, GameAudioHysteresisTests); MainWindow.xaml drops the Visibility binding so the bar renders unconditionally. Desktop audio is just automatic WASAPI loopback now. Docs in the same commit: ai.md (audio section + Services inventory), Services/index.md, ViewModels/index.md, TASKS.md TASK 9 item 7 follow-up, HANDOFF.md shipped state + stale poll-timer landmine removed. Build 0 warnings, 189 tests passing (8 detector tests removed) 2026-08-15 15:53:51 -07:00
gramps a0a8331569 TASK 24 post-pause polish batch (creator's 8 review issues, 2026-08-15): (1) desktop/game audio volume slider + (2) mute were no-ops — the AudioMixer's gain Func seams defaulted to unity because the VM never passed them; new AudioGainProvider (Services/Audio/) wires MicMuted/MicVolume/GameMuted/GameAudioVolume into the mixer at construction, read live each mix tick, so sliders and mutes are stream-honest; the game bar also scales TRAX locally via new MusicPlayer.LocalGain (UpdateTraxLocalGain on every game volume/mute change + on load) so the creator HEARS the controls work on the music in the headphones. (3) TRAX played once then stopped — OnPlaybackStopped only looped on Position>=Length (unreliable for MediaFoundationReader); now any clean stop rewinds + replays, errors/explicit stops surface via PlaybackEnded; unused using dropped. (4+6) TRAX moved right of Socials in the footer's left cluster (centered under scenes/sources); the mic cluster is now MIC + meter + mute + volume. (5) mic source persists — LayoutStore gains a Settings key/value table (SaveMicSourceName/LoadMicSourceName); the VM saves on pick and restores before the mixer's first Start, so a restart reconnects the same vetted device (green) or reports it missing (yellow). (7) Backdrop's missing trashcan shifted the edit/eye icons right — new HiddenBoolToVisibilityConverter keeps the trash column reserved (Hidden, not Collapsed) so every source row's icons stay in fixed columns. (8) a 1px hairline with top/bottom padding separates scenes from sources in the left panel. Docs in the same commit: ai.md (gains honest + drive TRAX locally, TRAX loops, mic persistence, footer layout), TASKS.md TASK 24, HANDOFF.md shipped state, ViewModels/index.md. ONE integration test: AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback (real mixer + provider gains through the pipe harness: scaled loopback audible, silence after mute). Build 0 warnings, 197 tests passing 2026-08-15 10:23:49 -07:00
gramps 86fcd868a8 Monetization policy locked + dev pause recorded (docs changeset, 2026-08-14): the one paid line becomes an annual subscription — early access $49.99/yr → $99/yr list at GA, grandfather-while-subscribed with lapse → list on renewal (no escalation matrix); free tier unchanged (branding flash is the free tier's billboard + no Alerts, which is the one paid feature); billing NOT locked to itch.io (hunted fact: itch.io has no native subscription billing — candidates Gumroad, whose native affiliate program is the tiebreaker, and Lemon Squeezy); support = in-app bug-report → git issues with a when-I-get-around-to-it cadence + emergency patches. ai.md Monetization section rewritten + stale paid-unlock seams fixed (L115 subscription-entitlement wording, the About hub's tabled billing PremiumUrl seam); TASKS.md gains TASK 23 (Monetization — billing provider decision, unlock mechanism with lapse→flash-returns enforcement, in-app bug-report loop, Unlock Premium seam lighting, Alerts gating, and line-item pointers for the early-access strategy, affiliate/ads phase 2, and the channel revival); HANDOFF.md records the decisions + the development pause (resume streaming = pair-programming dev streams via OBS + The Division 2 under the gramps/GOAT persona, 67 and gaming since 1981, 3D-printing content retired; dogfood switch to ytLlive once TASK 5 lands; resume point = TASK 5). Stale itch.io assertions corrected across comments and indexes (MainViewModel.cs PremiumUrl/IsPremium seams, MainWindow.xaml tooltip, AboutHubTests comment, ViewModels/index.md, TASK 9 out-of-scope note). Build 0 warnings, 196 tests passing 2026-08-14 18:48:42 -07:00
gramps 6d71acef8c TASK 9 audio milestone: real stream audio + voice filters + auto-duck + free TRAX music — the mixer now feeds the encoder real audio (ffmpeg reads a Windows named pipe, -f f32le -ar 48000 -ac 2 -i \.\pipe\ytllive_audio, replacing anullsrc silence; explicit -map 0:v -map 1:a via EncoderOptions.AudioPipeName), with honest gains reaching the stream (MicVolume scales mic, GameAudioVolume + mute scale loopback, Func<double> seams on AudioMixer), an always-on pure-C# voice chain on the mic BEFORE the meter and mix (LowShelfFilter 120Hz +4dB → HighShelfFilter 8kHz +3dB → NoiseGate 0.005/hysteresis 0.5 → Compressor 0.5 4:1, all TDF2), AutoDucker (mic RMS>0.02 → loopback ×0.25, attack 0.05/release 0.005), and TRAX free background music (MusicPlayer = MediaFoundationReader → VolumeWaveProvider16 at fixed 0.20 → WaveOutEvent via the new sibling NAudio.WinMM 2.2.1 package; plays to the default device so the existing loopback carries it, ducked with game; footer TRAX button with red/yellow/green status dot, left-click toggles/picks, right-click opens the OpenFileDialog picker, tooltip shows the track name; persisted via schema v9 single-row Music). Build-time deviations from the plan (recorded in ai.md + TASKS.md): WasapiCapture in NAudio 2.2.1 exposes no overridable GetDefaultMixFormat so sources run device mix format and the mixer's TinyResampler normalizes any rate to 48kHz (resampler IS the design); FfmpegEncoder.cs untouched — MainViewModel.StopStream calls _audioMixer.StopLive() (pipe EOF) before the frame pump stops; sound-bar relabelled 'Desktop Audio', IsGameAudioBarVisible = game sound OR music playing. Tests: new AudioPipelineTests (DSP/ring-buffer/ducker/resampler units + the ONE integration test Mix_WithFiltersDuckAndGain_Lands_On_AudioPipe reading real pipe bytes; ring-buffer overwrite bug found + fixed), FfmpegEncoderTests/LayoutStorePersistenceTests updated — 196 tests passing, 0 warnings 2026-08-14 18:09:26 -07:00
gramps 3d92bd0225 TASK 21: real logo + creator-hub About overlay — the About overlay is now the in-app creator hub: real logo (the creator's 'llama fortnite superman logo' copied to Assets/llama-logo.png, creator-owned art), hub links — llama chile shop on YouTube (MainViewModel.ChannelUrl, now public), Mastodon (https://mastodon.llamachile.tube/@gramps), Buy me a coffee (https://buymeacoffee.com/llamachiley, live), Unlock Premium (greyed 'coming soon'; IsPremiumAvailable flips on once the tabled itch.io PremiumUrl lands); a Licenses & legal sub-panel loads the full shipped THIRD-PARTY-NOTICES.txt in-app (ShowLicenses reads from the executable dir on first open, 'not found' fallback — never the OS viewer) with '← Back to About' (BackToAbout_Click) — the AboutHubTests integration test drives the real window + VM asserting the hub opens, link URLs are real, licensing loads the shipped notices text, and back returns to the hub — 174 tests passing, 0 warnings 2026-08-14 12:39:05 -07:00
gramps 801219ec46 TASK 4 ship step 7: one-click go live + private-only enforcement (req 8) — the Go Live dialog is locked to Private (GoLiveViewModel.Visibility is a get-only string, no dropdown; settings' dead Default Visibility dropdown + Visibilities/DefaultStreamVisibility removed); YouTubeStreamService.CreateBroadcast always sends privacyStatus='private' and gained an injectable HttpClient? http = null seam; BeginGoLive fires CreateBroadcastAsync which stashes _currentBroadcastId for TASK 5's bind/transition (failure → StreamStatus.Error via AppLog, never a crash) alongside the frame pump, and StopStream clears it; the REC sign shows a dark-red PRIVATE badge next to REC whenever the live stream is private (IsLivePrivate) — the broadcast-insert integration test asserts the request body carries privacyStatus private (RecordingHandler seam) + a no-session guard test — 173 tests passing, 0 warnings 2026-08-14 09:20:03 -07:00
gramps 0b71b030e4 TASK 8 meter scaling: +10 dB input amplification in AudioLevelMeter.ToDisplay so the meters use the full bar — speech peaks (~0.2 RMS, -14 dBFS) read ~0.93 (red zone) and normal speech (~0.05, -26 dBFS) ~0.73 (yellow) at maxed volume, instead of hovering at the red edge; ≤0.001 linear still reads zero so the meter never idles on background noise; one knob shared by the mic bar and the game bar (× MicVolume gain-on-noise untouched); tests updated + new ToDisplay_Pushes_Speech_Peaks_Into_Red_At_Maxed_Volume — 171 tests passing, 0 warnings 2026-08-14 08:43:06 -07:00
gramps d90b5ded0d TASK 7 UI polish batch: scene/source row cleanup, dedup naming, full social handles — scenes are now pure selection rows (edit/trash/visibility icons and inline rename removed with EditSceneCommand/RemoveSceneCommand/ToggleSceneVisibilityCommand + Scene.IsEditing); source rows gained the trio (new EditElementCommand → inline rename via SceneElement.IsEditing, ToggleElementVisibilityCommand → eye flips IsVisible, open/slashed style rebound, hidden rows dim to 45%); duplicate resource names get a no-space incrementing suffix via shared NextSourceName (Image, Image2, Image3…) derived from actual names so deletions never collide (AddSource + AddReusedImage); social bar renders the full validated handle (MaxWidth=200 + TextTrimming removed from SocialBarRenderer and the preview template); side panels stay fixed 220/300; focus-loss capture lag documented in ai.md as a known OS limit (deferred) — the two real-App tests now share RealAppHost, a dedicated STA thread owning the single WPF App, instead of each calling new App(); new SourceNamingTests integration test (Text/Text2/Text3, delete-middle re-add no-collision) — 170 tests passing, 0 warnings 2026-08-13 18:50:56 -07:00
gramps 18abe4210f TASK 4 audio fixes round 2: dB-scaled meters, game bar moved into the preview, Notepad About button replaced by the logo → in-app About — AudioLevelMeter.ToDisplay maps -60..0 dBFS onto 0..1 (real speech/game RMS ~0.01..0.1 linear left the flat scale dead; both meters now scale through it before volume); the game audio bar moved OUT of the footer (back to two rows) and is now a bottom-center dark chip overlaid on the preview window — monitoring UI only, never on the live output; the top-bar About button (opened THIRD-PARTY-NOTICES.txt in the OS viewer) removed, the ytLlive wordmark now opens the in-app About overlay, and task 21 queued (real logo + creator-hub About with premium/coffee/socials links + in-app scrolling licensing panel) — 169 tests passing, 0 warnings 2026-08-13 11:56:22 -07:00
gramps 9f9ed34627 TASK 4 audio follow-up: game audio bar + mic status dot + always-on capture — the footer's second audio control (desktop/game, a mirror of the mic bar: meter + mute + volume) appears only while a full-screen game is producing sound (IGameAudioDetector seam + GameAudioHysteresis: show ~500ms of fullscreen+sound, hide ~1s after leaving fullscreen, silence never hides an active bar; VM polls on a 250ms timer); capture now runs for the app's lifetime so both meters preview live (started at startup via StartMicCaptureAsync, disposed in Shutdown — no longer go-live driven); MIC label is a button with a status dot (Models/MicStatus: green via the source Started event, yellow = mic problem, red = no device); PickMicrophone swaps the live device immediately via AudioMixer.RestartMic; fixed a latent ?.Invoke(meter.Push(...)) short-circuit that skipped the meter update when nothing subscribed — 167 tests passing, 0 warnings 2026-08-13 11:29:44 -07:00
gramps ea250c02a6 Social bar position is a click-toggle, not a drag — the drag-snap misbehaved for shaky hands (snapped up but wouldn't come back down; jitter around the deadzone). Clicking the bar now flips it top/bottom via ToggleSocialBarPosition; SocialBarSnap and its units removed — 153 tests passing, 0 warnings 2026-08-13 11:04:32 -07:00
gramps ac60a26d82 TASK 4 ship step 5.5: social bar bug fixes + bar on the live output — direction-snap drag (SocialBarSnap.Decide + ClearValue, local Canvas.Top was overriding the binding), fediverse nodeinfo subdomain probing + load-time heal (settable FediverseSoftware), SocialBarRenderer strip blitted above the flash via a re-read FramePump socialBar seam — 155 tests passing, 0 warnings 2026-08-13 09:29:58 -07:00
gramps e72ba71165 TASK 4 ship step 5: live frame pipeline — FramePump paces the active scene's composite into the encoder, ScreenCaptureManager.GetLatestFrame, MainViewModel resolver/option-builder/pump wiring, FramePumpTests (7) + GetLatestFrame test — 147 tests passing, 0 warnings 2026-08-13 08:57:56 -07:00
gramps 58c0f8e8c4 TASK 4 ship step 4: WASAPI audio capture — NAudio loopback + mic behind an IAudioSource seam, AudioMixer driving AudioLevel while live, pure level meter + WaveToFloat, NAudio.Wasapi 2.2.1 (MIT, notices item 9) — 139 tests passing, 0 warnings 2026-08-12 21:18:07 -07:00
gramps ba427e85e1 TASK 4 ship step 3: encoder + RTMP push — FFmpeg subprocess with probed H.264 picker, BGRA stdin feed, stderr health parsing, graceful stop — 122 tests passing, 0 warnings 2026-08-12 20:32:24 -07:00
gramps 9873eeda7d Social bar v2: fediverse icons resolved via nodeinfo (with redirect-following), persisted software column, icon colors — 112 tests passing, 0 warnings 2026-08-12 18:28:59 -07:00
gramps 5e8b8e5835 Social bar (Branch B): global resource with per-scene presence — Models/Socials.cs (19 services, icons, canonical URLs), Services/SocialValidator.cs (HTTP lookup validation seam), LayoutStore schema v7 (Socials + SocialEntry tables + Scene.HasSocialBar), MainViewModel (socials collection, AddSocial/RemoveSocial/ToggleSceneSocialBar commands, freemium cap YT+1, premium seam), MainWindow.xaml (footer Social button + [+/-] toggle on meter line, preview bar rendering top/bottom + LCR), 4 integration tests (roundtrip, empty-not-persisted, canonical URLs, icons), 85 tests passing, 0 warnings 2026-08-12 07:33:03 -07:00
gramps 60259c08c2 Fix CameraStreamState.Failed compile error (19041 projection omits enum member names — compare by int); fix first-frame proof test (no dispatcher = no bitmap, assert LatestFrame instead) 2026-08-12 07:11:54 -07:00
gramps 8c7938aca0 FFmpeg locator (TASK 4 ship step 2) + licensing: notices, About, LGPL-shared pin
IFfmpegLocator seam that resolves ffmpeg.exe (PATH -> cache -> pinned download),
plus the licensing compliance that makes a paid GA product defensible.

NOTE FOR USERS: this change shows NO difference in the app's behavior except a
new top-bar "About" button (opens THIRD-PARTY-NOTICES.txt). It is scaffolding
for the encoder/streaming work.

- Services/Encoder/: IFfmpegLocator + FfmpegLocator. Pin is BtbN lgpl-shared
  autobuild-2026-08-09-13-03 (NOT gyan.dev/GPL or static: LGPLv2.1 §6 static
  relink material avoided by dynamic linking); extracts ffmpeg.exe + libav*.dll
  via a staging dir so a crash never leaves a partial cache
- THIRD-PARTY-NOTICES.txt: LGPL/BSD/MIT notices + source offer, copied to the
  build output, surfaced by the About button; v1 gate = bundle full license
  texts (TASK 4 requirement 9)
- ai.md "Licensing - do not violate" guardrails (never GPL/nonfree/static/latest-
  tag, never link FFmpeg in, never drop notices); TASKS.md + Services/index.md
  updated
- Tests: FfmpegLocatorTests - hermetic decision-ladder integration test + shared-
  build DLL extraction + edge cases; docs updated (78 tests passing, 0 warnings)
2026-08-10 11:02:26 -07:00
gramps 18a21010bb Scene compositor (TASK 4 ship step 1): encoder master-frame scaffolding
Software compositor that renders a scene into the encoder's master VideoFrame,
mirroring the XAML preview minus editing chrome: backdrop -> background ->
elements (UniformToFill cover-crop, round clip, mirror, opacity, border) ->
branding flash.

NOTE FOR USERS: this change shows NO difference in the app's UI — it is pure
backend scaffolding laying the groundwork for live video capture/streaming.
The preview you see is unchanged.

- Services/Compositor/: SceneCompositor (Render(scene, frameFor resolver,
  flashFrame, CompositorOptions)), CompositorOptions (source rect + output
  size; 16:9 full master, vertical 607x1080 -> 1080x1920), StretchMath (pure
  UniformToFill + bilinear), StaticPixelCache (asset bytes -> BGRA8 frame)
- Frame sources injected via Func<SceneElement, VideoFrame?> resolver, so the
  compositor is pure, WPF-free, and hermetic to test (D3D11 upgrade behind the
  same seam later)
- SceneElement.TryGetBorderColor public (shared hex parse), stale
  MainViewModel comment fixed, pre-existing CS1998 in YouTubeAuthServiceTests
  cleaned up
- Tests: SceneCompositorTests integration (full scene + vertical tier + flash)
  + StretchMath units, docs updated (72 tests passing, 0 warnings)
2026-08-10 10:15:58 -07:00
gramps ad9b3e1e48 Screen backdrop (schema v5/v6) + five-scene catalog + webcam polish: live desktop/game capture as a permanent non-deletable bottom layer (Source.IsBackdrop), auto full-screen game detection (Win32FullScreenDetector) else primary display, GraphicsCapturePicker re-designation, refcounted shared ScreenCaptureManager, Live-only backdrop by policy (HasBackdrop + v5-v6 backfill + EnforceBackdropPolicy, checkbox gone), SceneCatalog (Starting/Live/BRB/Chat/Ending) with + button re-adding missing scenes, webcam mid-session transparent-container fix (GetPreviewBitmap propagation), Chat half-screen-area cap, 'Add Webcam' always opens the picker (SwapWebcamIdentityAsync, no silent resurrect), WindowsRuntimeMarshal frame-read + 5s-throttled errors, docs updated, tests (65 passing) 2026-08-07 14:15:50 -07:00
gramps e037ba027b Multi-scene webcam (schema v3/v4): singleton Webcam + per-scene WebcamSceneConfig, right-click OBS-style border/context menu, persisted round-to-rect restore + legacy-square 16:9 heal, dark MenuItem template, tests (25 passing) 2026-08-07 08:24:18 -07:00