vv webcam Unit 2: the Chat scene in the vertical tier - rect only, tile or one whole 607x540 area
The creator's locked table (2026-10-17) gives the Chat scene its own rules in the 9:16 tier: RECT ONLY - the circle option is not offered - and a size that is either the normal small tile or ENLARGED to fill one whole 607x540 area (top or bottom). Unit 1 shipped the per-scene placement; this unit is the Chat scene's own variant of it. The rule lives on the MODEL, not on the button: - Models/SceneElement.cs: ClipShape and ToggleClipShape() are now virtual. - Models/WebcamSceneConfig.cs: VerticalShapeLocked (set per scene by the VM) OVERRIDES the ClipShape property, so no writer can slip a circle in - the toggle, the Shape button and the placement swap all go through it. The lock is cleared BEFORE the 16:9 layout comes back, so the desktop shape returns. - ViewModels/MainViewModel.Webcam.cs: CanToggleClipShape (the circle is NOT offered in the vertical Chat layout - the Shape button disappears, it is not merely greyed out), IsChatSceneCamera, ResizeWebcamInVerticalTier. - Models/VerticalCameraLayout.cs: FillsArea (the "enlarged" state, derived - no new column) and SizeFor - the Chat camera's TWO sizes, the tile or the whole area, whichever is nearer the requested width, so the resize handle has two detents instead of any size in between. - An enlarged camera FILLS the area its centre is in (top or bottom), so dragging it up/down swaps the areas. - The Chat camera is auto-tiled like the Live one when the vertical tier is picked - otherwise the vertical chat stream has no camera at all. - Controls/PreviewPane.xaml.cs: the resize routes through the VM; the drag assigns, lets the model clamp into the band, then snaps (so the enlarged camera can be dragged from the top area to the bottom one). Test: ytLive.Tests/VerticalChatCameraLayoutTests.cs - the ONE Good Dog test for the unit: the vertical tier is picked -> the Chat camera comes into the band as a small RECT tile -> the circle option is not offered and cannot be set even on the model -> dragged up and enlarged it fills the TOP area exactly -> back to 16:9 the desktop layout is restored exactly and the circle option is offered again. 430/430 tests pass, 0 warnings. Research (the creator's table is the established practice, not our invention): - Streamlabs' vertical guide: "splitting the screen in half horizontally, placing the camera feed in the top half and the main content on the bottom (or vice versa) may be the bet", and a just-chatting vertical wants the camera "to take up most of the screen" (streamlabs.com/content-hub/post/overlays-vertical-and-horizontal-streaming) - Bloopbot's vertical overlay docs describe the same per-layer model we use at scene granularity: "move or resize a layer on Vertical and it stops being Auto-placed. It keeps the place you gave it, and the horizontal layout is not touched" (bloopbot.com/docs/vertical-overlays-twitch-dual-format) - our swap-at-tier-change, stored per scene. - Landmine recorded: "about the bottom 560 px of the vertical video can be covered by Twitch's chat and player controls on phones" (Bloopbot) - the Chat camera's enlarged BOTTOM area may be half-hidden by the platform chrome; TASK 53's safe-zone work decides that. Memory in the same commit: ai.md (the Chat rules, the shape lock, the tests), TASKS.md + TASKS/task-57-vertical-camera-layout.md (TASK 57 complete), HANDOFF.md, MyMistakes.md (3 recipes: lock the model property not the UI button; two detents for a discrete size set; the edit tool cannot match an oldString with an em dash), Models/ViewModels/Controls index rows.
This commit is contained in:
@@ -2021,3 +2021,60 @@ the password works, use HTTPS".
|
||||
**Also:** `ssh_askpass` is not installed in this WSL — `SSH_ASKPASS`/`GIT_ASKPASS` helpers must be
|
||||
scripts you write yourself (`chmod 700`), and they should read the secret from `CREDENTIALS.md`
|
||||
rather than embed it.
|
||||
|
||||
## 2026-10-21 — A RULE THAT APPLIES TO SOME ELEMENTS ONLY: LOCK THE MODEL PROPERTY, NOT THE UI BUTTON (RECIPE — TASK 57)
|
||||
|
||||
The creator's table says the **Chat** scene's vertical camera is **rect only — the circle option is
|
||||
not offered**, while the Live scene's camera can be a circle. The obvious implementation hides the
|
||||
Shape button for that camera. That implementation is wrong: the button is only ONE writer. The
|
||||
model, the placement swap and the load path are the others, and a rule that lives only in the UI is
|
||||
a rule that will be broken by the next writer.
|
||||
|
||||
**The recipe — lock the property, and let the UI follow:**
|
||||
1. Make the property **`virtual`** in the base class (`SceneElement.ClipShape`) and **override** it
|
||||
in the derived class (`WebcamSceneConfig`): the setter coerces the value when the lock is on.
|
||||
Make the *action* virtual too (`ToggleClipShape()`) so the toggle can no-op **without** touching
|
||||
the round-trip fields (`RectWidth/RectHeight`) — a half-applied toggle is worse than none.
|
||||
2. The lock flag lives on the model (`VerticalShapeLocked`), is set **per scene** by the view model
|
||||
(`ResolveVerticalCameraLayout`) and is **cleared BEFORE the other layout is restored** — clear it
|
||||
after and the restore is silently coerced (the desktop circle comes back as a rectangle).
|
||||
3. The UI gate is then a *read-out* of the model rule, not the rule itself: a view-model property
|
||||
(`CanToggleClipShape`) that the button binds its `Visibility` to. "Not offered" = the button is
|
||||
**gone**, not merely greyed out.
|
||||
4. Test the model path as well as the UI path: call the toggle directly and assert the shape did not
|
||||
change — the UI's absence is not the rule.
|
||||
|
||||
**The general shape:** a per-element variant of a layout rule → one `virtual` property override +
|
||||
one lock flag, with the UI's visibility derived from it.
|
||||
|
||||
## 2026-10-21 — "SIZE A OR SIZE B": GIVE THE RESIZE HANDLE TWO DETENTS, NOT A FREE SIZE (RECIPE — TASK 57)
|
||||
|
||||
The creator's table says the Chat camera in the vertical tier is either the small tile (270×152) or
|
||||
**enlarged to fill one whole 607×540 area**. The obvious implementation lets the resize handle take
|
||||
any width in between — and then the camera is neither of the two things the creator asked for, and
|
||||
the "enlarged" state has to be *guessed* from the width.
|
||||
|
||||
**The recipe:**
|
||||
- Put the allowed sizes in one pure function next to the other band math
|
||||
(`VerticalCameraLayout.SizeFor(band, requestedWidth)`) and return **the size nearest the
|
||||
requested one** — the handle then has exactly two detents and no in-between.
|
||||
- The "enlarged" state is **derived**, not stored: `FillsArea(band, width)` (the camera is wide
|
||||
enough to fill an area). No new DB column, no state to keep in sync.
|
||||
- The position of an enlarged camera is **not free**: it fills the area its **centre** is in (top or
|
||||
bottom) — `AreaFor(band, isBottom)`. Drag it up/down and it swaps areas; that IS the interaction.
|
||||
- Watch the order of the writes: set the **size first**, then the position, because a position clamp
|
||||
that knows the size (the band's edge minus the width) will otherwise fight the new size.
|
||||
|
||||
## 2026-10-21 — THE EDIT TOOL CANNOT MATCH AN oldString CONTAINING AN EM DASH (RECIPE)
|
||||
|
||||
Three `edit` calls in a row failed with "Could not find oldString" on text I had just read in the
|
||||
file. The common factor: the matched block contained the **em dash** `—` (and in one case the
|
||||
`×` multiplication sign) — the same non-ASCII typography the repo's comments are full of. The tool
|
||||
documents a retry that ignores "typographic quote, dash, and space variants", but that retry did not
|
||||
save the match.
|
||||
|
||||
**The workaround that works:** keep the `oldString` **ASCII-only**. Match the smallest unique slice
|
||||
that does not contain a dash (e.g. the `if (...) return false;` line instead of the comment above
|
||||
it), and put the new prose in the `newString` where any character is fine. When a whole stale block
|
||||
must go (e.g. half-overwritten comments), a three-line `python3` script that deletes the exact line
|
||||
range is faster and safer than fighting the matcher.
|
||||
|
||||
Reference in New Issue
Block a user