Probleme der 5.0.3:
- CSS .vod-storyboard-preview hatte noch aspect-ratio:16/9, top:0, left:0, right:0
- JS hat dann inset:0 + aspect-ratio:auto inline gesetzt
- Im Electron-28-Chromium kam ein Layout-Konflikt raus -> Overlay-Box-
Dimensions wichen von Host ab -> backgroundSize-Skalierung passte nicht
zu visible-area -> mehrere Cells gleichzeitig sichtbar (Sprite-Sheet-
Look statt Single-Cell-Preview)
Fix 5.0.4:
- CSS-Klasse hat NUR noch Visual+Stacking (opacity, transition, border-
radius, overflow:hidden, z-index, position:absolute, pointer-events)
- KEIN top/left/right/bottom/width/height/aspect-ratio im CSS
- JS setzt alles inline und voll explicit: top=0, left=0, width=Xpx,
height=Ypx aus hostRect (.vod-thumb-wrap)
- Sanity-Guards fuer width/height<=0 und cellWidth/cellHeight<=0
- scaleX und scaleY weiter unabhaengig fuer korrekte Cell-Box-Fuellung
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wechsel von explicit width/height auf inset:0 (top/right/bottom/left:0,
width/height:auto, aspect-ratio:auto override des CSS-Legacy 16/9). Damit
fuellt das Overlay garantiert das gesamte .vod-thumb-wrap — kein leerer
Streifen mehr am oberen oder unteren Rand der Hover-Vorschau, egal ob
Twitch Storyboard-Cell-Aspect oder Subpixel-Rendering minimal abweicht.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fix 1: scaleX und scaleY werden jetzt unabhaengig berechnet. Twitch
Storyboard-Cells haben nicht zwingend 16:9; eine einheitliche scale-Variable
fuehrte zu Subpixel-Leakage am oberen oder unteren Rand mit Inhalt aus der
Nachbarzelle. Mit getrennter Achsen-Skalierung fuellt eine Cell die Overlay-
Box exakt.
Fix 2: Bulk-Select-Checkbox und Downloaded-Badge werden waehrend des Hover-
Previews ausgeblendet (vorher nur die Duration-Badge). Mein vorheriger Move
des Overlays in .vod-thumb-wrap hatte die Z-Order so geaendert, dass diese
Elemente jetzt sichtbar drueber lagen.
219 unit tests + e2e gruen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug 1 — VOD-Hover Storyboard zeigte am unteren Rand einen statischen Streifen vom Original-Thumbnail (Subpixel-Mismatch + Aspect-Ratio-Konflikt). Fix: Overlay haengt jetzt an .vod-thumb-wrap statt .vod-card, mit explizitem width+height aus dem Thumbnail-BoundingRect — keine CSS-aspect-ratio-Interferenz mehr.
Bug 2 — Merge-Group Download zeigte einen eingefrorenen Progress-Bar bei Multi-Part-VODs (Part X/Y). Root Cause: der weighted-progress Wrapper clamped progress=-1 (HLS unknown-total 1s-Tick) auf 0, was overallProgress auf priorWeight fix-nagelte. Bar oszillierte zwischen indeterminate-animation und einem fixen ~10% Wert. Fix: lastVodProgress persistiert zwischen Path-A-Ticks und Path-B-Streamlink-%-Lines, sodass der Bar smooth waehrend einer Part hochzaehlt.
210 unit tests + e2e:release gruen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vodStoryboardClientCache was a plain Map<vodId, VodStoryboard | null>
with no eviction. Every VOD ever hovered cached its first sprite
data URL — about 50-200 KB each. Browsing a long-running streamer's
2000-VOD archive could leave the renderer holding 100-400 MB of
hover-only sprite data permanently, with no signal to the user
that it was happening.
Wrapped writes in a rememberStoryboard helper that caps the cache
at 100 entries with FIFO eviction (Map iterator is insertion-ordered
so .keys().next().value is always the oldest). Cache hit / miss
semantics unchanged for the live set — only the dropped-off-the-
back entries get re-fetched if the user scrolls back to a VOD they
hovered hundreds of cards ago, and that re-fetch is fast because
the main-process side has its own metadata cache that survives the
renderer-side eviction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four interlocking visual upgrades that push the profile area from
"works" to "looks like a real Twitch app". Single release because
all four share data plumbing and need to land coherently.
1) Banner background — getStreamerProfile now also pulls
bannerImageURL via public GQL, fetches the bytes server-side as a
data URL (same path as the avatar fix in 4.6.18-4.6.19), and the
renderer puts it behind the header content with blur(18px) +
saturate(1.2) + a 0.55 opacity overlay. Result: per-streamer
colour identity at a glance, like twitch.tv's channel page.
2) Live preview card — when isLive, the public-GQL stream block also
carries previewImageURL(640x360), viewersCount, title, game{name}.
A second card slides in below the main profile row showing the
current frame at 240×135, eye-icon viewer count, big bold title,
game, and a red "Jetzt aufnehmen" CTA. Click anywhere on the card
OR on the button triggers triggerLiveRecording — same path as
the sidebar REC dot, so the recording reaches the queue with
identical settings.
3) VOD hover storyboard — Twitch ships a seekPreviewsURL per VOD
pointing at a JSON manifest of sprite-sheet images, each a grid
of preview thumbnails spanning the recording. New IPC
get-vod-storyboard fetches the manifest, picks the high-quality
first sprite, fetches its bytes as a data URL, and returns the
grid metadata. Renderer (new renderer-vod-hover.ts) hooks
delegated mouseover on #vodGrid: 220ms debounce, then on
activation overlays a div positioned over the thumbnail with
background-image=sprite + a setInterval cycling
background-position through 4 evenly-spaced cells at 600ms each.
Per-VOD result cached client-side so repeated hovers don't
re-fetch. Negative results (private VODs, expired) are also
cached so we don't re-query a known-empty manifest.
4) Sticky header — position:sticky;top:0;z-index:20 plus a
backdrop-filter:blur(6px) so the VOD grid scrolling underneath
reads through the banner subtly. Header stays anchored to the top
of .content as the user scrolls hundreds of VODs.
GQL refresher: the public schema rejects `broadcasterType` but
accepts `roles{isPartner isAffiliate}`, plus the same query now
includes bannerImageURL and stream{previewImageURL viewersCount
title game{name}}. One single roundtrip pulls everything we need
for the header AND the live card. The old separate-follower-count
roundtrip (fetchOnlyFollowerCount) is now redundant but kept around
for back-compat in case other call sites grow into it.
Also: profile layout switched from one big flex row to a relative
container with two children (.streamer-profile-row for the meta,
.streamer-profile-live-card for the live block). The .live-card
only renders when isLive — offline streamers get the same compact
header they had before.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>