59 lines
5.8 KiB
Markdown
59 lines
5.8 KiB
Markdown
# HANDOFF — Session State
|
||
|
||
## Branch
|
||
**`main`** @ HEAD — committed and pushed, working tree clean. **No feature branches pre-1.0
|
||
(decided 2026-08-24): all work lands on `main` per work unit; every `taskNN-*` branch deleted.**
|
||
|
||
## Shipped (this session, all on main)
|
||
- TASK 26 — webcam (+) menu gates on the app-wide identity (test `WebcamMenuGateTests`).
|
||
- TASK 27 — YouTube Chat one-per-layout gate + legacy label heal (test `ChatLayerGateTests`).
|
||
- TASK 19/23 status flip — Control Surface UX verified shipped; map corrected.
|
||
- TASK 20 step 1 — global hotkeys F1-F9 (`Services/GlobalHotkeys.cs`, test `GlobalHotkeyTests`). Validated by creator.
|
||
- ON-AIR sign right of REC — gray offline / red public / green private (`OnAirBrush`), shared `StatusSignText` style so REC/ON-AIR can't drift (creator dinged inconsistency when labels took different colors). Test `OnAirSignTests`.
|
||
- **Broadcast metadata pull-out + launch geometry** — white "Text" tab on preview's right edge (Live screen only) slides out the broadcast form: Title, Description, Tags csv, Visibility, Made-for-Kids, read-only Scheduled Start. Edits persist to Settings `Broadcast.*` immediately; Update Broadcast PUTs `liveBroadcasts?part=snippet,status` via new `YouTubeStreamService.UpdateBroadcast`. Go Live prefills from the form + captures back what was inserted. Old Default Stream Title/Description removed from App Settings (properties deleted). Window: default 1920×1040, MinWidth 1366, MinHeight 768, Manual startup, size+position persisted on close (`RestoreBounds`) and restored clamped to minimums + primary work area. Test `BroadcastPullOutTests.Metadata_Persists_WindowRestores_Clamped_And_UpdatePatchesRemote`.
|
||
- Suite: 228 total, 227 pass — only failure is the known pre-existing `AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback`. Build 0 warnings. Docs same-commit throughout.
|
||
|
||
## ⚠️ Landmines
|
||
|
||
- **User verdict (2026-08-23):** "The AI wrote fucked up code after repeatedly and intentionally misunderstanding instructions." Trust nothing from those sessions without manual verification.
|
||
- **Pre-existing test failure is ONE** (`AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback`) — unrelated to this session.
|
||
- **Real-MainWindow tests MUST set `LayoutPathOverride`** (ai.md) — DB pollution wiped real data once.
|
||
- **WPF handle forcing:** `WindowInteropHelper.Handle` pre-show returns Zero and does NOT raise `SourceInitialized`; use `EnsureHandle()` (does both).
|
||
- **Two MainWindows alive at once double-register hotkeys** — second window's RegisterHotKey fails silently-by-log. Tests close windows before opening the next.
|
||
- **Closing a MainWindow OVERWRITES saved window geometry** (Closing → SaveWindowGeometry). In tests that assert restore-from-DB, save state AFTER closing prior windows.
|
||
- **liveBroadcasts.update replaces the whole snippet part** — `UpdateBroadcast` echoes scheduledStartTime or YouTube clears the schedule.
|
||
- **contentDetails lock in created/ready** — latency/DVR/embed etc. cannot be edited while live; deliberately absent from the pull-out form.
|
||
- **`Source.Type` setter derives `IsBackground`** (Models/Source.cs:40) — any Type change must re-set IsBackground after.
|
||
- **vstest quirk:** only one `--tests:` param allowed; plain substrings work, alternation regexes silently match nothing.
|
||
- Notification.Wpf gotchas (ai.md/TASKS.md): area routing by XAML `Name`; unknown AreaName silently drops; `NeverExpires()` = `TimeSpan.MaxValue`; `ToHex()` = `#AARRGGBB`.
|
||
- **Text-tab crash fix (2026-08-24):** clicking any text `Run` (e.g. the LlamaCasty logo) crashed the app — `Window_PreviewMouseLeftButtonDown → IsDescendantOf` used only `VisualTreeHelper.GetParent`, which throws on non-Visuals; now falls back to `LogicalTreeHelper.GetParent`. Landmine for ANY new hit-testing code: text Runs are not Visuals.
|
||
- **Avatar display:** connected-but-image-missing now shows the channel initial in the circle + logs to startup.log (`Avatar load failed` / `connected but no profile image URL`). If the creator reports "no avatar", first check whether the app is actually CONNECTED (green status light vs Log In button) — ending a stream clears the OAuth session by design.
|
||
- **Dead code flagged, not touched:** `ShowWebcamCommand` / `CanShowWebcamInStagedScene` wired but unbound — audit item.
|
||
|
||
## Next steps
|
||
1. Creator visual pass: Text tab appears only while streaming; drawer slides over preview; fields pre-load across restarts; Update button greys until Go Live succeeds (needs real OAuth round-trip to verify remotely).
|
||
2. First-launch check: window opens 1920×1040 centered; resize/move/close/reopen restores geometry; can't shrink below 1366×768.
|
||
3. Continue queue: TASK 20 step 2 (hotkey config UI + persistence), TASK 17 web source, TASK 18 local recording, TASK 21 media source.
|
||
4. Loose ends: dead `ShowWebcamCommand`, AudioPipelineTests failure, Velopack update URL.
|
||
|
||
## Build
|
||
```bash
|
||
taskkill.exe /F /IM ytLive.exe # if running
|
||
"/mnt/c/Program Files/dotnet/dotnet.exe" build "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.csproj"
|
||
"/mnt/c/Program Files/dotnet/dotnet.exe" vstest "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.Tests\bin\Debug\net8.0-windows10.0.19041.0\ytLive.Tests.dll"
|
||
```
|
||
|
||
## Polar.sh resources
|
||
| Resource | ID |
|
||
|---|---|
|
||
| Org ID | `c05fb364-b967-4f6c-adf2-8a144e46d085` |
|
||
| Product | `d105dfa1-497e-423b-8cd4-e0ee2e3abbc0` |
|
||
| Price | `7c1b6f70-cf8c-46df-9bc8-6703f9fa64d0` |
|
||
| Benefit | `80f66bc1-d5a3-44a1-b9ea-1709ae508535` |
|
||
| Checkout URL | `https://buy.polar.sh/polar_cl_ueMy8AyAClO0o0yo19IroZwKTbD587gzUsjEW3bFapw` |
|
||
| Validate endpoint | `POST https://api.polar.sh/v1/customer-portal/license-keys/validate` |
|
||
| Key prefix | `LCYT-` |
|
||
|
||
## v1 vision
|
||
> A streaming tool so intuitive it needs no manual — go live or record in minutes, OBS-free.
|