TASK 47 take four: alert ticker visible in the preview + 3 display methods
The creator confirmed the clip fix ("the video plays now"), then asked where the
scrolling text was — it was invisible, for a structural reason. AlertTickerFrame
existed only as a frame-pump callback blitted into the OUTPUT; PreviewPane.xaml had
no element for it, because the strip is master-width and global, not a Source, so it
cannot ride a per-element Image. Nothing was wrong in the renderer: there was no
consumer in the preview. Same class of defect as the missing IsAlertBox trigger, one
layer up (MyMistakes RULE 5/6).
- tickerPreviewSink on AlertOverlayLayer, published from RefreshAlertPreviews() so it
is always the UI thread; MainViewModel.AlertTicker writes it into one reused
WriteableBitmap bound to a new global AlertTickerElement, mirroring SocialBarElement.
- Source.AlertDisplayMethod + panel "Display" selector: TickerScroll / Flash / Solid.
Flash pulses 0.5s on / 0.5s off for the whole alert; Solid is centred and still.
- The marquee was also unreadable: a fixed 140px/s took ~17s per pass, so a 10s alert
showed the text once, entering from the right and never crossing. Paced in reads per
alert instead (TickerReadsPerAlert = 3 inside the alert's own length, speed derived
from it) — never px/s. Research (websearch: how do OBS/Streamlabs/StreamElements
alert boxes present announcement timing?) settled the unit: Streamlabs exposes "Alert
Duration: choose how long your alert stays on your stream" and "Text Delay", never a
scroll-speed slider (https://support.streamlabs.com/hc/en-us/articles/52499995174299-Setting-up-Your-Streamlabs-Alerts).
Run is phase-started half a frame in so the first frame isn't blank.
- Persistence: AlertDisplayMethod INTEGER NOT NULL DEFAULT 0 via the idempotent
table_info migration, appended LAST in the SELECT because the Source reader is
positional (GetInt32(32..34)) — a mid-list insert would silently shift a neighbour.
Tests: 15 new facts (suite 339/339). RealApp STA host: the pane draws the strip and
collapses at alert end; the layer publishes a real 1920x48 frame for all three methods
and nothing when the ticker is off; three passes counted in 10s by the pill's leading
edge resetting (a seamless marquee never blanks, so an empty frame cannot count a pass);
Solid byte-identical at every moment; Flash on for half of each second; the panel shows
and writes back the choice; the DB round-trips all three alert fields together.
Incidental finding: a bound ItemsSource ComboBox in LeftPanel.xaml broke
LayerReorderPersistenceTests.RealMouseDrag (that test injects PHYSICAL mouse input, so a
load-time re-measure moves the rows out from under the cursor). Rewritten as inline
ComboBoxItems, the shape the chat Font selector already uses in that panel. Recorded as
MyMistakes RULE (8).
This commit is contained in:
+42
-5
@@ -1,4 +1,4 @@
|
||||
# HANDOFF — 2026-09-26, end of session
|
||||
# HANDOFF — 2026-09-26, end of session (take four: ticker display methods)
|
||||
|
||||
## Where we are
|
||||
`main`, **5 local commits ahead of `origin/main` (= `ecb329e`), NOTHING PUSHED.**
|
||||
@@ -10,7 +10,23 @@ Last pushed commit is still `ecb329e`. Do not push without the creator saying so
|
||||
| `7b940b6` | poller-thread marshal crash fix |
|
||||
| `80038ff` | silent-decoder fallback + diagnostics |
|
||||
| `aea0670` | video pacing via `FfmpegFrameRateProbe` |
|
||||
| (last, unpushed) | **the real alert fix: `PreviewPane.xaml` `IsAlertBox` trigger** + 2 Good Dog tests + resolver/preview diagnostics |
|
||||
| `e2ecc24` | **the real alert fix: `PreviewPane.xaml` `IsAlertBox` trigger** + 2 Good Dog tests + resolver/preview diagnostics |
|
||||
| (last, unpushed) | **alert announcement strip: visible in the preview + 3 display methods** (Scroll / Flash / Solid), paced in reads-per-alert |
|
||||
|
||||
## THE ANSWER, take two (do not re-derive this)
|
||||
The **ticker was output-only**. `AlertTickerFrame` existed solely as a frame-pump
|
||||
callback (blitted at 0,0 into the OUTPUT). `PreviewPane.xaml` had no element for it
|
||||
because the strip is master-width and GLOBAL, not a `Source`, so it cannot ride a
|
||||
per-element `Image`. Nothing was broken in the renderer — there was simply no consumer
|
||||
in the preview. Fixed with a `tickerPreviewSink` on `AlertOverlayLayer` (published from
|
||||
`RefreshAlertPreviews()`, UI thread only) + `MainViewModel.AlertTickerImageSource` +
|
||||
a global `AlertTickerElement` in `PreviewPane.xaml` mirroring `SocialBarElement`.
|
||||
|
||||
The marquee was also **unreadably paced**: a fixed `140px/s` needed ~17s per pass, so a
|
||||
10s alert showed the message barely once, entering from the right and never crossing. It
|
||||
is now paced in **reads per alert** (`TickerReadsPerAlert = 3` inside the alert's own
|
||||
length), which is the incumbent's own unit (Streamlabs: "Alert Duration" + "Text Delay",
|
||||
never a scroll-speed slider).
|
||||
|
||||
## THE ANSWER (do not re-derive this)
|
||||
The alert video was **never drawn**, never mis-decoded. Build 90's diagnostics showed
|
||||
@@ -36,7 +52,7 @@ MSB3027 — the creator must close ytLive first. That is normal, not a broken bu
|
||||
"/mnt/c/Program Files/dotnet/dotnet.exe" build "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.csproj" --no-restore
|
||||
"/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"
|
||||
```
|
||||
Last full suite: **324/324 pass**. Run it with ytLive CLOSED or the RealMouseDrag env
|
||||
Last full suite: **339/339 pass** (15 new facts for the ticker feature). Run it with ytLive CLOSED or the RealMouseDrag env
|
||||
flake fires.
|
||||
|
||||
## Queued next (creator's words, 2026-09-26) — the Good Dog queue
|
||||
@@ -46,11 +62,32 @@ flake fires.
|
||||
2. **Test pull-out chat input: 20px right padding** — the text box is clipped at the right
|
||||
edge of the div; needs 20px padding against the div's right side.
|
||||
3. **Test pull-out chat input: Enter inserts a newline**, it must not send.
|
||||
4. Verify the alert video live on the next build (the fix is in; nobody has SEEN it play
|
||||
yet). This is the confirmation step for the commit above.
|
||||
4. ~~Verify the alert video live~~ — **DONE, the creator confirmed it plays.**
|
||||
5. **Verify the three ticker display methods live** (Scroll / Flash / Solid) and pick a
|
||||
default. Flash is 0.5s on / 0.5s off; Solid is centred; Scroll does 3 passes per 10s
|
||||
alert. Nothing here has been SEEN on screen yet — the tests prove pixels and bindings,
|
||||
not the look.
|
||||
6. **Decide whether the once-per-second alert diagnostics stay.** `ViewModels/MainViewModel.cs`
|
||||
(`ResolveOutputFrame`) and `Services/AlertOverlayLayer.cs` (`UpdatePreview`) still log
|
||||
`Alert preview:` / `Output resolver:` once a second while an alert plays. They earned
|
||||
their keep; they are noise in normal use. Creator's call.
|
||||
|
||||
## Landmines / facts worth keeping
|
||||
- The pinned BtbN ffmpeg has **no libx264** — generate test clips with `-c:v mpeg4`.
|
||||
- **A bound `ItemsSource` ComboBox in `LeftPanel.xaml` breaks
|
||||
`LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder`** (it
|
||||
injects PHYSICAL mouse input, so a load-time re-measure moves the rows out from under
|
||||
the cursor). Use inline `<ComboBoxItem>`s like the chat Font selector does. If a UI-only
|
||||
change breaks a real-input test, suspect the control's binding STYLE first, and bisect
|
||||
with `git checkout <the one xaml file>`.
|
||||
- The `Source` row is read back by **POSITIONAL** index in `LayoutStore.Load.cs`
|
||||
(`GetInt32(32..34)`). Append new Source columns to the END of the SELECT, or a
|
||||
mid-list insert silently shifts a neighbour instead of failing to load. New columns use
|
||||
the idempotent `PRAGMA table_info` guard in `LayoutStore.Migrations.cs` (no
|
||||
`user_version` bump needed).
|
||||
- **A seamless marquee never goes blank** (the wrapped copy enters from the right as the
|
||||
pill clears the left), so an "empty frame" cannot count a pass — count the pill's
|
||||
leading edge resetting to the right edge.
|
||||
- `SceneCompositor.Render` fills its base with **opaque black**, so "any non-zero byte"
|
||||
is not proof a layer painted. Compare against an idle render, or count COLOURFUL px.
|
||||
- `sqlite3` on WSL: `/home/gramps/android-sdk/platform-tools/sqlite3`. Extract a BLOB with
|
||||
|
||||
Reference in New Issue
Block a user