a809676731
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bcc7eea968 |
defensive: type="button" on the 16 renderer-rendered <button> template strings
Continuing the type-attribute pass from 4.6.114 (static HTML). The renderer modules build buttons into their template strings via tagged-template literals; sixteen of these were rendered without an explicit type attribute and would inherit the type="submit" default: - renderer-archive.ts: 4 archive-result buttons (chat / events viewer triggers + open file / show in folder) - renderer-profile.ts: 3 profile action buttons (live record CTA, open on Twitch, refresh) - renderer-queue.ts: 4 queue detail-row buttons (open file, show in folder, view chat, view events) - renderer-streamers.ts: 2 per-VOD-card buttons (trim, queue) - renderer.ts: 3 merge file row buttons (move up, move down, remove) Same defensive reasoning as 4.6.114: no <form> wraps these today, but the moment one does they all silently turn into submit buttons. Explicit type="button" closes that footgun. Every <button> in the codebase — static or dynamic — now declares its type. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
632f348349 |
a11y: aria-hidden on the 6 decorative SVG icons rendered from TS
Continuing the SVG aria-hidden pass from 4.6.110 (which covered index.html). The renderer modules build six more decorative SVG icons into their template strings: - renderer-profile.ts: followers icon, vods icon, last-stream clock, live-viewers eye - renderer-queue.ts: merge-group icon next to merge-bundled queue rows - renderer-streamers.ts: empty-state VOD icon shown when a streamer has no VODs All 6 are pure decoration — each sits next to a textual label or value (e.g. "1.2K followers", "5 VODs", a viewer count, etc.). Screen readers were either announcing them as "image/graphic" or silently traversing — adding aria-hidden="true" cleanly skips them so the assistive-tech read-out is just the meaningful text. The data-URL SVG fallback in renderer-streamers.ts:257 (used as an onerror src for VOD thumbnails) is intentionally not touched — it's an image fallback inside a URL-encoded string, not an actual SVG element in the DOM. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
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>
|