TASK 18: local (record-only) recording pipeline + top-bar pills/light/Start-End button UX
Encoder dual-output: StreamEnabled/RecordEnabled/RecordPath options; per-output ffmpeg block (stream ->flv, record ->mp4); throw unless at least one output. RecordingFile: auto-name ty-<yyyymmdd>-<start hhmm>-0000.mp4, rename-on-stop to ty-...-<len hhmmss>.mp4 with numeric-suffix on collision. LayoutStore persists record folder. MainViewModel: REC/ON-AIR pills, status light, single Start<->End button (replaces Start/Stop variants), StopStream no longer signs out, explicit session timer. About overlay widened. Sign In label + spacing before Start. Tests: 244 passed / 246 (2 pre-existing: AudioPipeline gains-mute, RoundClip). Build 0 warnings. Scope check clean. Memory-map correction (derived-solution rule, 2026-08-29): image-shrink recipe was never recorded when first done, so it was re-derived. Added Derived-solution/recipes rule to AGENTS.md; MyMistakes.md now a permanent recipes registry (records the verified WPF-imaging shrink recipe); schema.md rows MyMistakes as registry; ai.md points to the rule. README hero image replaced: 3.2MB screenshot -> 1400x794 -> 188KB docs/ytLlive-preview.jpg (JPEG q82).
This commit is contained in:
+57
-49
@@ -2,66 +2,74 @@
|
||||
|
||||
## Branch / Commit State
|
||||
|
||||
**`main`**, working tree clean. Web-source bounding work is **DONE, verified, committed, and
|
||||
PUSHED (2026-08-28)** — the first push in a while; the web feature is complete and on origin.
|
||||
No feature branches pre-1.0: all work lands on `main` per work unit.
|
||||
**`main`**, working tree has an **uncommitted TASK 18 work unit** (record-only pipeline + top-bar
|
||||
pills/light/button UX + About resize + now button-label Start/End simplification) **plus a docs/image
|
||||
memory-map fix** (derived-solution rule + README hero image). `origin/main` and head are both at
|
||||
**`1e633f9`** (spin-guard rule), already pushed. This session's changes are **NOT committed** yet — see
|
||||
"Next steps".
|
||||
|
||||
## The web-source rendering work (2026-08-28) — DONE: canvas-size viewport + ALPHA-BBOX crop
|
||||
No feature branches pre-1.0: all work lands on `main` per work unit (pending the creator's go-ahead to
|
||||
commit + push).
|
||||
|
||||
**VERDICT (creator, 2026-08-28):** bounding is PERFECT. Verified with a second widget resource that
|
||||
has no glow effect. The earlier "remaining defect / dead-space gap" was the WIDGET'S OWN GLOW
|
||||
effect pushing out its perceived borders — NOT a code bug. The alpha-bbox was already hugging the
|
||||
glow halo; with a plain widget the dashed selection border sits exactly on the widget edge.
|
||||
## Memory-map corrections (2026-08-29 — "don't re-derive" fix)
|
||||
|
||||
**History of failures (log wiped from MyMistakes.md per task-complete rule; see git history):**
|
||||
box-size rendering clipped full-bleed widgets to their upper-left chunk; `JSON.stringify` return
|
||||
double-encoded under `ExecuteScriptAsync` (crop silently disabled); `scrollWidth/scrollHeight` at
|
||||
canvas size = whole viewport for full-canvas wrappers (no trim); DOM-element-union measured an
|
||||
unsettled layout on NavigationCompleted (BROKE the image); sizing the container to the crop
|
||||
(`ab29ec8`) made things worse (reverted).
|
||||
- **Incident:** the image-shrink recipe (for `docs/ytLlive-preview.jpg`) was never recorded when first
|
||||
done, so it was re-derived from scratch. Fixed by:
|
||||
- **`AGENTS.md`** → Working rules: new **🔬 Derived-solution rule** (record a one-off solution in
|
||||
`MyMistakes.md` the same session; grep it first on any "did this before?" wall).
|
||||
- **`MyMistakes.md`** → contract expanded: per-task failure log **+ RECIPES REGISTRY** (permanent, NOT
|
||||
truncated); recorded the verified image-shrink recipe.
|
||||
- **`schema.md`** → file inventory now rows `MyMistakes.md` as the recipes registry.
|
||||
- **`ai.md`** → Derivative-work section notes the derived-solution rule (registry reachable from map home).
|
||||
- **`README.md`** + `docs/ytLlive-preview.jpg` → README hero image replaced (screenshot downscaled to
|
||||
1400×794, 188KB JPEG).
|
||||
|
||||
**The model that shipped:**
|
||||
- `Services/WebView2Manager.cs` — page renders at 1920×1080 (stable, never tracked); `overflow:hidden`
|
||||
+ transparent-bg injection. `FindContentBounds` scans the Bgra32 capture per frame and crops to the
|
||||
bounding box of NON-TRANSPARENT pixels — the widget's true rendered extent, measured from the frame
|
||||
itself. No DOM query, no layout timing: it can never truncate visible content (worst case = full
|
||||
frame = the confirmed-good image). `Stretch="Fill"` maps the cropped frame flush under the selection
|
||||
box → the box hugs the widget on all four sides. `QueryContentBoundsAsync` + the JS script are
|
||||
DELETED.
|
||||
- `MainWindow.xaml` — web `<Image>` Stretch set to Fill via the IsWebSource DataTrigger (unchanged).
|
||||
- Invariant: **render at canvas size; crop to the alpha extent; Fill to the box.**
|
||||
- Unit tests: 7/7 (3 `FindContentBounds`, 4 control-size.)
|
||||
|
||||
**Property panel / sliders (2026-08-28):** web URI ✕ clears the textbox; web ✓/✕ icon buttons
|
||||
`IsTabStop="False"` so Tab walks X→Y→W→H→URI; Slider style `IsMoveToPointEnabled="True"`
|
||||
restores click-anywhere-on-bar. All three confirmed fixed by the creator.
|
||||
## TASK 18 — Local recording (in progress, 2026-08-29)
|
||||
|
||||
Build: 0 warnings; tests 7/7 (WebView2Manager). Rejected for the record: box-sized viewport (upper-left
|
||||
truncation), fixed 1920×1080 + `Uniform` (letterbox dead space), CSS margin/100% reset (enlarged gap),
|
||||
viewport==box (reflow/truncation/scrollbars), DOM-union crop (broke the image), container==content
|
||||
auto-fit sized to the crop (`ab29ec8`, reverted).
|
||||
**Shipped in this (uncommitted) change:**
|
||||
- `EncoderOptions` + `FfmpegArgs` + `FfmpegEncoder`: independent `StreamEnabled`/`RecordEnabled`/
|
||||
`RecordPath`; per-output blocks (`-f flv` stream, `-f mp4` record); encoder throws unless ≥1 output.
|
||||
- `MainViewModel`: REC + ON-AIR pill toggles, status lights, dynamic `PrimaryButtonText`
|
||||
(Start Streaming / Start Recording), `ShowPrimaryStartButton`/`ShowLogInButton`/`ShowEndStreamButton`,
|
||||
`CanStartSession`, `AccountStatusLightToolTip`; `IsEditMode` += `!IsRecording`.
|
||||
- `StartSession()` routes: ON-AIR → GoLive dialog then stream (± record); only-REC → `BeginRecordOnly()`
|
||||
(no YouTube dialog). `StopStream()` no longer signs the creator out (explicit logout only) and runs
|
||||
**rename-on-stop** to `ty-<yyyymmdd>-<HHmm start>-<hh2mm2 length>.mp4`.
|
||||
- `Services/RecordingFile` (pure): auto-name `ty-…-0000.mp4` → rename-on-stop with hh2mm2 length.
|
||||
- `LayoutStore.Load/SaveRecordFolder` + `ChooseRecordFolderCommand` (`OpenFolderDialog`), default
|
||||
`%APPDATA%\ytLlive\recordings\`.
|
||||
- `MainWindow.xaml`: REC/ON-AIR pill `ToggleButton`s + status lights, dynamic button, status-light tooltip,
|
||||
About overlay border 520→650 / MaxHeight 560→700 / padding 24→28, About grid background `#171928`.
|
||||
- Tests: `FfmpegEncoderTests` record-only + dual-output arg tests; `RecordingFileTests` (naming rules).
|
||||
|
||||
**Not done / parked:** manual rename dialog for the user (planned as a modal, needs a trigger since the
|
||||
toast service has no actions) — parked; running-app verification of rename + dual output.
|
||||
|
||||
## Key facts / landmines
|
||||
|
||||
- **Why the web-box saga spun (2026-08):** nine commits of in-head theorizing for a problem
|
||||
OBS already solved (browser source = fixed canvas, box = user-sized, content clips at the
|
||||
edge). The apparent gap was the widget's CSS glow, not a bug. → AGENTS.md now mandates
|
||||
external research on the second failed fix and proactive scans for new features.
|
||||
|
||||
- **App running locks `ytLive.exe`** → build fails MSB3027 until creator exits the app.
|
||||
- **Startup diagnostics:** unhandled exceptions + checkpoints go to `%APPDATA%\ytLlive\startup.log`
|
||||
(`Helpers/AppLog.cs`). On a silent startup crash, read that.
|
||||
- **Pre-existing test failures (unrelated to web source, do not chase):**
|
||||
`AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback` +
|
||||
`RoundClipInteractionTests.Round_Clip_Corner_Is_Grabbable_And_Shape_Is_Circle`.
|
||||
- **Pre-existing test failures (do not chase):** `AudioPipelineTests.Mix_HonorsProviderGains…` +
|
||||
`RoundClipInteractionTests.Round_Clip_Corner_Is_Grabbable…` — fail on `main` with this change unapplied.
|
||||
All other tests pass (incl. the 7 new recording tests).
|
||||
- **App running locks `ytLive.exe`** → build fails MSB3027 until the creator exits the app.
|
||||
- **Startup diagnostics:** `%APPDATA%\ytLlive\startup.log` (`Helpers/AppLog.cs`).
|
||||
- **Real-MainWindow tests MUST set `LayoutPathOverride`** — DB pollution wiped real data once.
|
||||
- **Good Dog Rule:** ONE integration test per change; no new feature branches pre-1.0.
|
||||
- **Scope Lock:** declare the file list, run `scripts/scope-check.sh` before every commit.
|
||||
- **Good Dog Rule:** ONE integration test per change; no feature branches pre-1.0.
|
||||
- **Scope Lock:** declared list below; run `scripts/scope-check.sh` before committing.
|
||||
- **Every feature change ships memory updates in the SAME commit** — `TASKS.md` status + this file.
|
||||
- No `CODING.md`; conventions live in `AGENTS.md` + `ai.md`.
|
||||
|
||||
## Commit scope (declared)
|
||||
|
||||
`Services/Encoder/EncoderOptions.cs Services/Encoder/FfmpegArgs.cs Services/Encoder/FfmpegEncoder.cs
|
||||
Services/Encoder/FramePump.cs Services/RecordingFile.cs Services/LayoutStore.cs
|
||||
ViewModels/MainViewModel.cs MainWindow.xaml ytLive.Tests/FfmpegEncoderTests.cs
|
||||
ytLive.Tests/FramePumpTests.cs ytLive.Tests/RecordingFileTests.cs
|
||||
AGENTS.md TASKS.md ai.md HANDOFF.md MyMistakes.md schema.md README.md
|
||||
docs/ytLlive-preview.jpg docs/ytLlive-preview.png(deleted)`
|
||||
|
||||
## Next steps
|
||||
1. Done (2026-08-28): web-source bounding verified PERFECT and pushed. If an animated widget causes
|
||||
visible crop jitter later, add hysteresis as a follow-up — not queued.
|
||||
2. Graceful handling of navigation errors / invalid URIs (TASK 17 ×9) — only if the creator asks.
|
||||
3. Continue queue: TASK 18 local recording, TASK 21 media source, TASK 22 audio sync offset.
|
||||
|
||||
1. Creator to decide: commit + push this TASK 18 work unit (scope list above), or refine first
|
||||
(verify the rename + dual output in the running app before committing).
|
||||
2. Continue queue: TASK 21 media source, TASK 22 audio sync offset.
|
||||
|
||||
Reference in New Issue
Block a user