127 lines
8.3 KiB
Markdown
127 lines
8.3 KiB
Markdown
# TASK 40 — App Settings round: active camera + config dialog, gear move, defaults, accent
|
||
|
||
**Status:** ☐ Queued — **plan saved 2026-09-22** (creator: "save this plan to the memory
|
||
files"; implementation to start on the next task spec). Sub-units A → B → C → D, each a
|
||
separate scope-checked work unit with its own Good Dog integration test.
|
||
|
||
## Creator asks (2026-09-22, three)
|
||
|
||
1. **Settings → App Settings:** add a new section **under "Default Location for Recordings"**
|
||
for the *active web camera* — (a) triggering it queries Windows for the **current** list of
|
||
webcam resources **including vcams**, presented as a **sub-menu list**; (b) selecting a
|
||
camera opens a **dialog with the available configuration params for tweaking**.
|
||
2. The settings **gear** (bottom bar, lower-left, icon-only, context-menu) "looks lost" —
|
||
re-locate and/or label it.
|
||
3. "Surely there's more tweakables?" — add other settings.
|
||
|
||
## Locked decisions (user answers, 2026-09-22)
|
||
|
||
- **Three units A → B → C** (recommended split honored; one Good Dog test + one commit each).
|
||
- Camera tweaks are **live-only** — no persistence, no schema change in Unit A. A
|
||
per-camera "re-apply on preview start" preset stays a follow-up if ever wanted.
|
||
- Gear: **top-left, to the right of the "LlamaCasty" wordmark, gap ≈ 3 wordmark letters**,
|
||
icon-only; **left-click = App Settings** (direct), **right-click = Report Bug / Feature
|
||
Request / About** menu (relocated with the button off BottomBar).
|
||
- Unit C picks: **default output resolution**, **default scene transition + duration**,
|
||
**Confirm before End Stream**.
|
||
- **Accent/theme color** was picked too, but it is heavy (69 hardcoded
|
||
`#e94560`/`#ef4444`/`#22c55e` sites across 15 files + VM brush literals + the _persisted_
|
||
per-source `Source.ChatMemberColor`) → split out as **Unit D** with its own scope + test.
|
||
|
||
## Facts this plan sits on (all verified in code, 2026-09-22)
|
||
|
||
- Webcam identity is **app-level**: `LayoutStore.Webcam` (Webcam table), picked via
|
||
`CameraPickerDialog` on first Add, swapped via "Change Webcam…" context menu
|
||
(`MainViewModel.Webcam.cs` → `ChangeWebcamAsync` / `SwapWebcamIdentityAsync`: release old,
|
||
re-acquire per scene config, persist).
|
||
- Startup resource check auto-locks only when **exactly one** camera is found; **≥2 cameras →
|
||
deliberately no auto-lock, selection left to the App Settings dialog (gear)** (ai.md §Startup
|
||
resource lifecycle) — this plan's Unit A IS that selector.
|
||
- Enumeration is already "current list incl. vcams": `MediaCaptureCameraEnumerator` →
|
||
`DeviceInformation.FindAllAsync(DeviceClass.VideoCapture)` (same source OBS consumes;
|
||
caveat: a vcam must be a *registered capture device* to appear).
|
||
- **Default scene transition type + duration are persisted (`TransitionType` /
|
||
`TransitionDurationMs`) and loaded every start, but editable NOWHERE in the UI** (nothing in
|
||
XAML binds `SelectedTransitionType`/`TransitionDurationMs`) — Unit C surfaces an existing knob.
|
||
- **Output resolution resets every launch** (`_selectedQuality = QualityOptions[0]` in the
|
||
MainViewModel ctor; bottom-bar ComboBox not persisted).
|
||
- Gear today: `Controls/BottomBar.xaml:13` (far-left column 0), icon-only, ContextMenu
|
||
(App Settings / Report Bug / Feature Request / About); click handler `BottomBar.xaml.cs`.
|
||
|
||
## Unit A — App Settings "Active Web Camera" (your ask 1)
|
||
|
||
New group **under** Recordings in the App Settings overlay (`OverlayHost.xaml:39` settings
|
||
StackPanel): current camera display + **Change…** + **Tune…**.
|
||
|
||
- **Change…** → **fresh enumeration per trigger** → ContextMenu **sub-menu** listing cameras
|
||
(current = checkmarked; disabled "No cameras found" item when empty). Selection:
|
||
`_webcam == null` → adopt + base-lock (mirrors the single-camera startup path); else →
|
||
`SwapWebcamIdentityAsync` (release old / re-acquire per scene / Webcam row persisted).
|
||
- **Tune…** (enabled when a camera is active) → **CameraConfigDialog** (new window; mirrors
|
||
`CameraPickerDialog.xaml` owner/geometry/theme): probes the device's
|
||
`VideoDeviceController` **MediaDeviceControl**s — Brightness, Contrast, Hue, Saturation,
|
||
Sharpness, Gamma, WhiteBalance, BacklightCompensation, Exposure, Focus, Pan, Tilt, Zoom —
|
||
showing only the ones the device reports. Each row: **Auto checkbox** (when
|
||
`Capabilities.AutoSupported`) + **Slider Min/Max/Step** applying live via
|
||
`TrySetValue`/`TrySetAuto`; empty state "No adjustable controls on this camera" (common on
|
||
cheap cams and vcams).
|
||
- **⚠ Tech risk, verified during implementation (not assumed):** whether MediaDeviceControl
|
||
writes succeed under our `MediaCaptureSharingMode.SharedReadOnly` init (the MJPG
|
||
stream-property negotiation already works there, but extended controls are a separate
|
||
path). Mitigation ladder: (1) probe-capture `SharedReadOnly` → (2) the running frame
|
||
source's own `VideoDeviceController` → (3) read-only controls + an "applies to a stopped
|
||
preview" note. **Spin-guard:** a second failed attempt = research the established answer
|
||
(OBS does this via DirectShow VideoProcAmp) before writing a third.
|
||
- **Test seam:** `Services/CameraControlProbe.cs` — `ICameraControlProbe` (capability/value/
|
||
auto read + `TrySet*`) with a MediaCapture-backed implementation; the Good Dog test drives a
|
||
fake that records calls. (The WinRT/hardware probe itself is not unit-testable.)
|
||
|
||
| Unit A — declared files | |
|
||
|---|---|
|
||
| touch | `Controls/OverlayHost.xaml`, `ViewModels/MainViewModel.Webcam.cs`, `ViewModels/MainViewModel.cs` |
|
||
| new | `Services/CameraControlProbe.cs`, `CameraConfigDialog.xaml(+.cs)`, `ViewModels/CameraConfigViewModel.cs`, `ytLive.Tests/AppSettingsWebcamTests.cs` |
|
||
| docs | `TASKS.md`, `ai.md`, `TASKS/task-40-app-settings-round.md`, `HANDOFF.md` |
|
||
|
||
**Good Dog test `AppSettingsWebcamTests`** (RealApp + temp DB, reuses existing
|
||
`CameraEnumeratorOverride` / `CameraFrameSourceFactoryOverride` seams):
|
||
(1) settings panel → Change… lists the fake cameras (incl. vcam-styled names); (2) pick B when
|
||
A is current → identity swapped, A released, B acquired, Webcam row persisted, B checkmarked;
|
||
(3) pick when `_webcam == null` → adopt + base-lock acquired; (4) fake `ICameraControlProbe`
|
||
drives `CameraConfigViewModel` — Auto + slider changes forward to the probe.
|
||
|
||
## Unit B — gear to top-left (your ask 2)
|
||
|
||
- `Controls/TopBar.xaml`: `IconButton` gear **right of the brand Button**, gap ≈ 3 wordmark
|
||
letters; left-click `OpenSettingsCommand`; ContextMenu keeps Report Bug / Feature
|
||
Request / About. Remove the gear (+ its click handler) from `Controls/BottomBar.xaml(.cs)`.
|
||
- Bonus: TopBar sits inside the overlay scrim span (`OverlayHost Grid.RowSpan=3` covers rows
|
||
0–2), so the gear is correctly covered while an overlay is open — today it sits below the scrim.
|
||
|
||
## Unit C — the small defaults (your ask 3, rest)
|
||
|
||
- **Default output resolution** — persist `SelectedQuality` (e.g. Settings key
|
||
`Output.Resolution`), restore at startup.
|
||
- **Default scene transition + duration** — surface the persisted-but-uneditable
|
||
`TransitionType`/`TransitionDurationMs` as controls in App Settings.
|
||
- **Confirm before End Stream** — toggle; when on, `EndStreamCommand` confirms before stopping
|
||
live/recording.
|
||
- One Good Dog test for the unit + docs in the same commit.
|
||
|
||
## Unit D — accent / theme color (own unit; not merged into C)
|
||
|
||
- Consolidate the hardcoded accent literals into a `Themes/Controls.xaml` accent brush,
|
||
consumed via `DynamicResource` across the ~15 files (brand "ty" run, YtButton primary,
|
||
selected rings, live toolbar/glow, meters, trax, etc.). `Source.ChatMemberColor` (persisted
|
||
per-source) stays per-source.
|
||
- Picker + persistence via a Settings key (`App.AccentColor`).
|
||
- Own Good Dog test (accent propagates) + docs (`ai.md` gains the hardcoded-accent →
|
||
resource-brush consolidation note).
|
||
- Caveat for the plan file: this is the widest diff of the round; the scope-check file list
|
||
will be long by necessity and every touch is a genuine dependency of the consolidation.
|
||
|
||
## Memory updates (this plan's save, 2026-09-22)
|
||
|
||
- [`TASKS/task-40-app-settings-round.md`](task-40-app-settings-round.md) (new — this file)
|
||
- `TASKS.md` — row 40 + webcam "next slices" bullet (selector → TASK 40) + TASK 40 open item
|
||
- `ai.md` — ≥2-camera bullet points at TASK 40 Unit A
|
||
- `HANDOFF.md` — in-flight plan + next step |