62400e4aa0
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
96113dc267 |
a11y: streamer-profile header — avatar wrap + live card keyboard-activatable
Two click-only divs in the new profile header (4.6.17+) had no keyboard equivalent: - .streamer-profile-avatar-wrap (clicking the avatar opens the channel on twitch.tv) — the only way to trigger that action besides the "Open on Twitch" button in the action column, so keyboard users were missing a primary affordance - .streamer-profile-live-card (clicking anywhere on the live preview card starts a live recording) — the embedded Record-now button inside the card already covered keyboard activation, so this one is more about completeness than necessity Both got: - role="button" + tabindex="0" - aria-label = the existing tooltip locale string (so AT reads the same text shown to sighted users on hover) - An inline onkeydown that re-fires the same onclick handler on Enter / Space. The live-card additionally checks event.target === event.currentTarget so a focused inner button pressing Enter doesn't double-fire the wrapper handler CSS adds focus-visible rings: - Purple ring on the avatar wrap (matching the existing avatar's purple border) - Red ring + glow on the live card (matching the existing card border colour) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
30776c02b9 |
fix: sticky header opaque + banner visible + missing button styles
Three things from screenshot feedback against 4.6.20: 1) VODs visible through/above the sticky profile header. Root cause was a stack: the 0.10/0.04 alpha gradient over var(--bg-card) pushed the resulting background just barely under "opaque" in some renderers AND .content has padding-top: 25px which let VODs scroll through the area above the sticky element when top: 0 was used. Fix: drop the gradient (banner-bg + ::before pseudo handle the visual interest now), use straight var(--bg-card), set top: -25px to negate .contents padding so the header pins flush with the visible top edge, bump z-index to 100, add isolation:isolate to force a new stacking context so VODs cannot escape upward through the header. 2) Banner not visible. Was being suppressed by a 0.78-0.92 alpha dimming gradient applied via background-image alongside the banner URL — readable for text but visually killed the banner. Moved the gradient into a ::before pseudo at z-index 1 with gentler 0.55-0.78 alpha, dropped banner-bg blur from 18px to 10px, took opacity from 0.55 back up to 1.0. Banner now actually shows behind the content the way twitch.tv does. 3) Stray un-styled buttons. Scan turned up a handful of action buttons rolling their own inline styles (.vodBulkAddBtn / MarkBtn / UnmarkBtn / ClearBtn, .vodFilterClearBtn, .btnStreamerBulkRemove, .clipDialogConfirmBtn) plus a missing .queue-detail-btn rule that was leaving every "View chat", "View events", "Open file", "Show in folder" button defaulting to the browsers gray fallback. Added three reusable classes (.btn-pill default/primary/success/danger, .btn-icon, plus the missing .queue-detail-btn) and swapped the inline styles for the classes. Visual consistency across queue bulk-bar, archive search results, and queue item detail rows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3c73efbad7 |
feat: banner background + live preview card + VOD hover storyboard + sticky header
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>
|
||
|
|
9239eebf34 |
feat: streamer profile header — modern channel-page card above VOD grid
When you pick a streamer in the sidebar, the VODs panel now leads with
a polished channel-style header instead of just the bare page title.
This is the "personal" feel — you are looking at a creator, not a folder.
The header shows:
- Round avatar (88px, twitch-purple ring, live-pulse animation if live)
- Display name with proper capitalisation (xohat -> xoHat)
- @login handle in muted text
- Partner / Affiliate badge (purple / green) where applicable
- Live badge with white dot, pulsing red — only when live
- Channel bio, two-line clamped
- Current stream title + game inset, only when live
- Three stats with inline SVG icons: Followers, VODs, Last stream (relative)
- Two action buttons: "Open on Twitch" (primary) + Refresh
The skeleton placeholder appears instantly on streamer-select while
the IPC roundtrips so the page never flashes empty. Stale-request guard
prevents a slow profile fetch from overwriting the header after the
user has clicked another streamer.
Backend (main.ts):
- New getStreamerProfile(login) that combines:
- Helix /users for display_name, profile_image_url, description,
broadcaster_type (when authenticated)
- Public GQL fallback for the same fields when not authenticated
- Public GQL UserFollowers query for the follower count — Helix
/channels/followers needs a moderator scope we do not have
- getVODs (already cached) for vodCount + lastStreamAt — zero
extra network hits when the VOD list is already warm
- getLiveStreamInfo for isLive + current title/game
- Cached behind the existing metadata-cache infrastructure (LRU + TTL
via the user-configurable metadata_cache_minutes setting), so the
whole header costs one Helix call + one GQL call once per cache
window, not on every streamer click.
Frontend:
- New renderer-profile.ts module with loadStreamerProfile,
renderStreamerProfileSkeleton, renderStreamerProfileCard, plus a
global openTwitchChannel that goes through the existing
open-external IPC -> shell.openExternal pipeline.
- Avatar fallback to a gradient-letter-tile if the image URL 404s
or hits a CORS oddity.
- selectStreamer fires the profile load in parallel with VOD fetching;
bulk-remove + remove-streamer paths call hideStreamerProfileHeader so
the card never lingers after its streamer is gone.
CSS adds the .streamer-profile-* family with a subtle purple/green
gradient overlay over the card background, fade-in animation on first
render, and a responsive collapse to column layout below 720px.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|