Commit Graph

410 Commits

Author SHA1 Message Date
xRangerDE
7cb2358a54 release: 4.6.123 streamer-profile-skel-block variants
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:17:36 +02:00
xRangerDE
3fa49a5283 cleanup: profile-skeleton block variants — 5 inline width/height styles gone
The streamer profile loading skeleton (8 inline-styled div placeholders that preview the final card layout while data fetches) carried width/height/border-radius properties inline on every block. Four of those are pre-shaped slots that match a real layout element (avatar, name line, badge, subtitle), and one is the stats container margin — extracted to CSS classes:

- .streamer-profile-skel-block.avatar (88x88 round, flex-shrink:0)
- .streamer-profile-skel-block.name (180x24)
- .streamer-profile-skel-block.badge (90x18, 10px radius)
- .streamer-profile-skel-block.subtitle (60% x 14, margin-top:6px)
- .streamer-profile-skel-stats (the container's margin-top:8px)

The three stat-line placeholders (widths 100/80/120) keep their inline width: the slight variation is intentional visual texture so the skeleton doesn't look like three identical rectangles, and it's the only place where the inline value actually carries meaning.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:17:23 +02:00
xRangerDE
a809676731 release: 4.6.122 .queue-output-row + .queue-output-label
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:13:12 +02:00
xRangerDE
26d737b3fc cleanup: .queue-output-row + .queue-output-label CSS — 2 inline styles gone
The queue-item detail panel's output-files row (rendered after a job completes — Open file / Show in folder / View chat / View events buttons + a tiny file-label span) had two inline-styled elements:

- The container div with display:flex; gap:6px; margin-top:6px; flex-wrap:wrap; align-items:center
- The span with color:var(--text-secondary,#888); font-size:11px; word-break:break-all

The span's inline fallback color (#888 after the comma) was a leftover defensive value from before --text-secondary was guaranteed to be defined globally; it's never actually needed today.

Extracted to .queue-output-row + .queue-output-label classes. Class definitions live next to the .context-menu* family they belong to (other queue-detail-panel internals).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:13:00 +02:00
xRangerDE
84abfb7cf7 release: 4.6.121 role=menu/menuitem on context menus
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:07:52 +02:00
xRangerDE
8d95a4a6a5 a11y: role=menu / menuitem / separator on the two right-click context menus
Both context menus (queue row + VOD card) were built as plain <div> trees with no ARIA roles — screen readers couldn't tell they were menus, and the individual rows weren't exposed as menu items. Users on assistive tech got a generic "group of nested divs" with no menu semantics, despite the menus being visually and functionally menus.

Added the WAI-ARIA menu pattern roles:
- role="menu" on the container
- role="menuitem" on each clickable row
- aria-disabled="true" on disabled menu items
- role="separator" on the horizontal divider lines

Both renderer-queue.ts (queue right-click context menu) and renderer-streamers.ts (VOD card right-click context menu) get the same treatment so the two share both visual style (.context-menu — 4.6.120) and accessibility semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:07:35 +02:00
xRangerDE
7de560f44c release: 4.6.120 .context-menu shared by queue + VOD context menus
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:03:12 +02:00
xRangerDE
a1ea920003 cleanup: VOD context menu reuses .context-menu — renamed queue-context-menu to shared
renderer-streamers.ts had its own copy of the same right-click menu boilerplate that 4.6.119 just consolidated for renderer-queue.ts — the VOD card context menu (Open on Twitch / Copy URL / Trim / Add to Queue / Mark as downloaded) was building ~14 inline-styled properties on its container and ~6 per item, with the same mouseenter/mouseleave hover fake.

Renamed the freshly-extracted classes from .queue-context-menu* to .context-menu* (more accurate since they're generic right-click-menu styles, not queue-specific) and pointed both renderer-queue.ts and renderer-streamers.ts at the new shared class set. The VOD menu drops its entire inline-style block + two hover handlers per item.

Net: ~17 more inline style assignments + 5 hover handlers gone, two context menus now share a single visual definition.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 09:02:59 +02:00
xRangerDE
0132c96a7f release: 4.6.119 queue context menu CSS class extraction
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:59:46 +02:00
xRangerDE
9a4fbb8af4 cleanup: queue context menu styled via CSS classes — kills ~16 inline styles
showQueueContextMenu in renderer-queue.ts was building the right-click menu entirely with .style.X = '...' assignments — the menu container had 8 inline declarations (position/z-index/bg/border/border-radius/box-shadow/padding/min-width), each menu item had 6 (padding/cursor/font-size/color/border-radius/opacity) plus two mouseenter/mouseleave handlers to fake :hover, and the separator added 3 more.

Extracted everything except the dynamic positioning into four CSS classes:
- .queue-context-menu (the container; left/top stay inline since they're click-position-derived)
- .queue-context-menu-item (default state)
- .queue-context-menu-item:hover:not(.disabled) (replaces the JS mouseenter/mouseleave dance with a real :hover rule)
- .queue-context-menu-item.disabled (greys + cursor)
- .queue-context-menu-separator

Net: ~17 inline style assignments + 2 hover handlers gone, menu styling lives in styles.css next to other context-card patterns. The mouseenter/mouseleave -> :hover conversion also picks up reduced-motion suppression that the prefers-reduced-motion rule applies to transitions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:59:33 +02:00
xRangerDE
ca74a865f8 release: 4.6.118 localize 3 stuck-in-German placeholders
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:54:22 +02:00
xRangerDE
7a6654097f i18n: localize 3 placeholders that stayed in German under English
Three input placeholders in the HTML had hardcoded German strings that were never routed through the locale system:

- clipStartPart (Clip cutter modal — "Start Part-Nummer") said "z.B. 42" regardless of language
- clipUrl (Clips tab) read "...oder https://www.twitch.tv/..." with a mid-string German conjunction
- cutterFilePath (Video cutter "Datei ausgewahlt" field) read "Keine Datei ausgewahlt..." even under EN

Added three locale keys (clips.urlPlaceholder, clips.startPartPlaceholder, cutter.filePathPlaceholder) with DE+EN translations, plus the three setPlaceholder() wire-up calls in renderer-texts.ts. The HTML defaults stay German (consistent with the other placeholders) but the JS now overrides them when the user picks English (or re-renders when language changes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:54:08 +02:00
xRangerDE
97ea32a08b release: 4.6.117 chat viewer filter localized + status-dot aria-hidden
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:49:31 +02:00
xRangerDE
773addb279 a11y + i18n: chat viewer filter placeholder + aria-label, status dot aria-hidden
Two unrelated small fixes bundled:

1. The chat viewer modal's filter input (chatViewerFilter) had a hardcoded "Filter..." placeholder that was never localized — every other filter input in the app routes its placeholder through UI_TEXT. Added queue.chatViewerFilterPlaceholder + queue.chatViewerFilterAria locale keys (DE: "Chat filtern..." / "Chatnachrichten filtern"; EN: "Filter chat..." / "Filter chat messages") and wired them through renderer-texts so the placeholder now matches the active language and screen readers get a proper accessible name on the input.

2. The status-bar's coloured dot (statusDot) had no aria-hidden — screen readers would read it as a generic element with no meaning. The status text right next to it already carries the same information ("Verbunden" / "Nicht verbunden"), so the dot is purely decorative. Added aria-hidden="true".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:49:17 +02:00
xRangerDE
3603caed21 release: 4.6.116 merge file-row buttons — aria-label + focus-visible
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:42:26 +02:00
xRangerDE
f6f266e3d4 a11y: localized aria-label + focus-visible on the 3 merge file-row buttons
The merge tab's per-file action buttons (▲ ▼ x — move up, move down, remove from list) were three icon-only buttons whose only visible content was the unicode glyph. No aria-label, no title, no focus-visible ring:

- Screen readers had nothing to announce — a keyboard user navigating the merge file list would tab through three unnamed buttons in a row.
- Sighted keyboard users had no visible focus indicator on .file-btn.

Three new locale keys (DE+EN) — merge.moveUpAria / moveDownAria / removeAria — give each button a translated aria-label and matching title tooltip. CSS adds .file-btn:focus-visible with the purple ring and a red variant for .file-btn.remove to match its red-on-hover colour family.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:42:10 +02:00
xRangerDE
85cc4957d8 release: 4.6.115 type=button on 16 renderer-rendered <button> elements
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:36:43 +02:00
xRangerDE
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>
2026-05-11 08:36:31 +02:00
xRangerDE
82df586c9e release: 4.6.114 type=button on all 43 unmarked <button> elements
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:32:04 +02:00
xRangerDE
5ea763d79b defensive: type="button" on all 43 <button> elements in static HTML
The HTML had 59 <button> elements; 16 already declared type="button" but the remaining 43 relied on the browser default. The default for <button> without a type attribute (and outside a <form>) is type="submit" — harmless today because index.html has no <form> elements, but the moment a future refactor wraps any section in a form, every unmarked button suddenly becomes a submit button. That's a latent footgun.

Added type="button" to all 43 unmarked buttons via a regex replacement. Now any <button> in the markup is explicitly non-submit — no behaviour change today, but the markup no longer depends on the absence of a form element to behave correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:31:53 +02:00
xRangerDE
b251d795dc release: 4.6.113 streamer-profile-btn focus-visible + storage open type=button
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:27:50 +02:00
xRangerDE
85128086b4 a11y: focus-visible on .streamer-profile-btn + type=button on storage open btn
Two small targeted fixes:

- .streamer-profile-btn (the action buttons in the streamer profile header: Record now, Open on Twitch, Refresh) had :hover but no :focus-visible. Keyboard users tabbing through the profile header buttons couldn't tell which one was focused. Added a purple ring for the default variant and the inner-white + outer-purple double-ring for the .primary variant (matches the convention used everywhere else for purple-background buttons).

- The dynamically-built Storage table "Open" button (renderer-settings.ts:397) was created via document.createElement('button') without setting type. Browsers default to type="submit" for <button> elements, which is fine outside a form but defensive coding to mark it explicitly. Set openBtn.type = 'button'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:27:38 +02:00
xRangerDE
71fedcb34c release: 4.6.112 aria-label on queue retry button + streamer live-dot
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:23:34 +02:00
xRangerDE
1da5589b1a a11y: aria-label on queue retry button + streamer live-dot
Two icon-only elements that had a title tooltip but no programmatic accessible-name source for screen readers:

- .queue-retry-btn: a small button on failed queue items, its only visible content is the unicode glyph U+21BB (↻). Screen readers would read it as "clockwise open circle arrow" or skip it entirely. Added aria-label using the already-translated UI_TEXT.queue.retryItem string (same value as the title). Also added type="button" so it doesn't default to submit semantics if the queue is ever wrapped in a form.

- .streamer-live-dot: the small red dot that appears next to a streamer name when they're currently live. It's pure CSS styling on an empty span, so screen readers had nothing to read — losing the live state for assistive tech. Added role="img" + aria-label using the existing UI_TEXT.streamers.liveNowTooltip string, so screen readers announce "Live now" alongside the streamer name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:23:21 +02:00
xRangerDE
c7b2ef0d24 release: 4.6.111 aria-hidden on renderer-rendered SVG icons
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:17:17 +02:00
xRangerDE
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>
2026-05-11 08:17:05 +02:00
xRangerDE
aed770a56c release: 4.6.110 aria-hidden on 12 decorative SVG icons
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:12:30 +02:00
xRangerDE
7b6ced7818 a11y: aria-hidden on all 12 decorative SVG icons
Every <svg> in index.html is a decorative graphic that visually reinforces an adjacent text label — the logo SVG sits next to "Twitch VOD Manager", the 7 nav-item SVGs each pair with their tab name, the refresh button SVG sits next to "Aktualisieren", and the 3 empty-state SVGs sit above a heading + paragraph. The graphics carry no information that isn't already in the surrounding text.

Without aria-hidden, screen readers either announce these as "image" / "graphic" (depending on implementation) or silently traverse them — neither adds value, and the former adds noise to nav navigation in particular.

Added aria-hidden="true" to all 12 SVG elements in static HTML. Screen readers now skip them and read just the labelled text, which is the desired UX for decorative icons.

(The two existing aria-hidden="true" attributes on the flag-icon spans in the language picker were already correct and are not affected.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:12:12 +02:00
xRangerDE
fc631c2403 release: 4.6.109 aria-hidden on decorative middle-dot separators
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:08:19 +02:00
xRangerDE
43924fd51f a11y: aria-hidden on three decorative middle-dot separators
Three places in the renderer use a literal middle-dot character (· / &middot;) as a visual separator between two label components:

- Sidebar streamer-section counter: "12 · 3 live"
- Stats top-streamers row sub-label: "streamerName · 5 files"
- Stats size-bucket row sub-label: "count · size"

Screen readers either announce "middle dot" verbatim or skip it depending on the implementation — neither is useful information. Wrapped each dot in <span aria-hidden="true"> so the assistive-tech read-out becomes "12 3 live" / "streamerName 5 files" / "count size" — the meaningful parts only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:08:08 +02:00
xRangerDE
1fb33aa6cc release: 4.6.108 template-guide table scope=col + aria-labelledby
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:01:37 +02:00
xRangerDE
42c7831c96 a11y: scope=col + aria-labelledby on the template-guide variable table
The "Verfugbare Variablen / Available Variables" reference table inside the Template Guide modal (a 3-column data table mapping placeholders to descriptions and example output) was missing two standard data-table a11y hooks:

- The three <th> cells had no scope attribute. The implicit <th>-inside-<thead> column scope works in modern screen readers but explicit scope="col" is the recommended pattern and what a11y audit tooling looks for.

- The <table> had no programmatic relationship to its own heading (the <h3 id="templateGuideVarsTitle"> directly above). Added aria-labelledby="templateGuideVarsTitle" so screen readers announce the table's purpose ("Verfugbare Variablen, Tabelle") when the user enters it.

Pairs nicely with the storage-stats table fix in 4.6.107 — both data tables in the app now have explicit column scopes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:01:26 +02:00
xRangerDE
63b04b6469 release: 4.6.107 storage stats table scope=col + aria-label
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:57:44 +02:00
xRangerDE
03b575cd1d a11y: scope=col + aria-label on storage stats table headers
The Storage card's per-streamer stats table was built without scope attributes on its <th> cells and the last column (the "Open" action column) had an empty header — screen readers couldn't tell which column a data cell belonged to once you tabbed into the rows, and the actions column had no announced name at all.

Fixes:
- All 6 column headers get scope="col" — explicit signal to screen readers that each <th> is a column header (the implicit <th>-inside-<thead> semantics work, but explicit scope is best practice for data tables and the de-facto standard for accessibility tooling validation)
- The empty actions header gets aria-label set from a new storageColumnActionsAria locale key (DE: "Aktionen", EN: "Actions") so screen readers announce "Aktionen, Spaltenkopf" for that column instead of skipping over an unnamed header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:57:33 +02:00
xRangerDE
3748e25d1d release: 4.6.106 prefers-reduced-motion support
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:51:35 +02:00
xRangerDE
7dd6755392 a11y: respect prefers-reduced-motion — suppress all animations/transitions
The app had no @media (prefers-reduced-motion: reduce) rule, meaning users with the OS-level "Reduce motion" setting enabled still got the full animation set: the empty-state floating SVG (4s infinite), the btn-icon-spin on Refresh hover, the vod-bulk-bar slide-in, the storyboard fade-in, and the ~6 transition: all declarations scattered across button hover states.

For users with vestibular disorders this is real discomfort, not aesthetic preference. Windows 11 and macOS both expose the setting via Settings > Accessibility, and the media query is the standard way to honour it from CSS.

Added the conventional reduce-motion block at the bottom of styles.css:
- animation-duration: 0.01ms (effectively instant)
- animation-iteration-count: 1 (kills infinite loops)
- transition-duration: 0.01ms (state changes are immediate)
- scroll-behavior: auto (kills smooth-scroll)

All hover/state changes still happen — they just snap rather than animate. No feature is lost.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:51:23 +02:00
xRangerDE
02b61c7ea4 release: 4.6.105 .vod-btn focus-visible rings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:46:42 +02:00
xRangerDE
5a4b054d9d a11y: focus-visible rings on .vod-btn (per-card action buttons)
Each VOD card renders 2-3 action buttons in its footer (.vod-btn — Trim, Queue, etc.). They had :hover states but no :focus-visible, so keyboard users tabbing through the VOD grid would land on each button without any visual focus indicator.

Added:
- .vod-btn:focus-visible — purple ring (covers the secondary variant which has a translucent grey bg)
- .vod-btn.primary:focus-visible — inner-white + outer-purple double-ring so the indicator stays visible against the button's own purple background (same pattern used for .btn-pill.primary and .btn-primary in 4.6.81/82)

Continues the per-area focus-visible pass — VOD-grid keyboard nav (already covered: card itself, checkbox; now: action buttons inside the card) is the final big interactive surface to get coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:46:32 +02:00
xRangerDE
e73db55e29 release: 4.6.104 .modal-close + .queue-retry-btn focus-visible rings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:42:32 +02:00
xRangerDE
9be864e614 a11y: focus-visible on .modal-close + .queue-retry-btn
Two more interactive buttons that had hover + active states but no keyboard focus indicator:

- .modal-close (the X-close button used by all 5 modals — update, clip-cutter, events viewer, chat viewer, template guide). Red-toned ring matching the hover colour family.

- .queue-retry-btn (per-item retry button on failed queue items). Purple ring matching the hover state's accent-purple feedback.

Continues the focus-visible pass started in 4.6.81/82/103. Closes the remaining shell-and-modal buttons that keyboard users could tab to without seeing where they were.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:42:21 +02:00
xRangerDE
03f3756523 release: 4.6.103 .lang-option focus-visible ring
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:37:49 +02:00
xRangerDE
91e4e65fa6 a11y: focus-visible ring on .lang-option (language picker)
The two language-picker buttons (Deutsch / English) had :hover and an
.active state but no :focus-visible — keyboard users tabbing into the
group couldn't see which button was focused unless it also happened to
be the active one. And even then, the active state uses a 1px soft
shadow which is easy to mistake for the hover border tweak.

Added two rules:
- .lang-option:focus-visible — purple-accent ring matching the rest of
  the app's focus-visible convention
- .lang-option.active:focus-visible — combines the pressed-state border
  with the thicker 2px focus halo so focus and pressed state are both
  visible when they coincide

Continues the focus-visible pass started in 4.6.81 (btn-primary/secondary/
pill/close) and 4.6.82 (queue + top-bar + add-streamer buttons).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:37:37 +02:00
xRangerDE
9046344375 release: 4.6.102 centralize localStorage try/catch helpers
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:33:33 +02:00
xRangerDE
752a5b2556 cleanup: centralise localStorage try/catch in renderer-shared — safeLocalStorageGet/Set/Remove
Every renderer module that persists state was wrapping its localStorage.getItem/setItem/removeItem call in the same try/catch idiom — handling private-browsing quirks and other sandbox contexts where storage isn't writable. Three identical patterns repeated nine times across renderer-streamers (filter / sort / hide-downloaded state), renderer-updates (skipped-update version), and renderer.ts (active-tab persistence).

Introduced three helpers in renderer-shared.ts:
- safeLocalStorageGet(key, fallback = '') — wraps getItem with the try/catch + fallback
- safeLocalStorageSet(key, value) — wraps setItem
- safeLocalStorageRemove(key) — wraps removeItem (needed for clearSkippedUpdateVersion which actually deletes the entry rather than blanking it)

Refactored 9 callsites. Reduces the noise:before:
    try { return localStorage.getItem(KEY) ?? ''; } catch { return ''; }
    try { localStorage.setItem(KEY, value); } catch { /* localStorage may be unavailable */ }
after:
    return safeLocalStorageGet(KEY);
    safeLocalStorageSet(KEY, value);

Left the VOD scroll-positions persistence in renderer-streamers untouched — its surrounding try/catch wraps JSON.parse/stringify logic that doesn't fit the simple helper signature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:33:23 +02:00
xRangerDE
cf76e37c22 release: 4.6.101 language-picker labelled button group
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:28:23 +02:00
xRangerDE
a7fbdd2fbf a11y: language-picker is now a labelled button group for screen readers
The language picker is a pair of <button aria-pressed=...> toggles (Deutsch / English) sitting inside a div, with a separate <label id="languageLabel">Sprache</label> directly above. Two issues:

- The <label> has no for= attribute because the actual control is a custom button-pair, not a single input. So the label was just floating text — present visually but not programmatically connected to the buttons it describes.

- The two buttons were a flat list with no group container, so a screen reader navigating button-by-button announced "Deutsch, pressed" / "English, not pressed" without any context that these are language alternatives.

Added role="group" and aria-labelledby="languageLabel" to the language-picker div. Screen readers now announce "Sprache, group, Deutsch button pressed" — the floating label becomes the group's accessible name and the relationship is exposed via the accessibility tree.

Kept the aria-pressed toggle pattern (rather than rewiring to role="radiogroup"+role="radio") because the JS state plumbing already aligns with the toggle semantics and the group label is enough to clarify the picker's intent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:28:13 +02:00
xRangerDE
d0de52de95 release: 4.6.100 remove dead .clip-template-lint + clip-template-wrap button margin
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:22:47 +02:00
xRangerDE
3e5bdb73d4 dead-code: remove .clip-template-lint orphan + extract clipTemplateGuideBtn margin
The .clip-template-lint CSS rule was documented as a no-op alias kept in case any external reference still used it (deprecated when the shared .template-lint class with .ok/.warn modifiers took over). Grep confirms zero external references — both .ts files and index.html only mention the new .template-lint class. Deleted the rule + its 5-line comment.

Same edit moves the clipTemplateGuideBtn's margin-top:8px from inline to a .clip-template-wrap .btn-secondary descendant rule. One inline style attribute gone, and the spacing now lives next to the .clip-template-wrap definition where future readers will look for it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:22:36 +02:00
xRangerDE
f3e7225011 release: 4.6.99 .section-header-actions + drop redundant inline style
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:17:18 +02:00
xRangerDE
8f6d7b2d9a cleanup: .section-header-actions class + drop redundant margin-top:0 on archiveTitle
The stats card header (Archiv-Statistik) had an inline-styled inner div grouping the "last scanned" label with the Aktualisieren button — display:flex; gap:8px; align-items:center;. Extracted that pattern to .section-header-actions, which has a real semantic role: it's the right-side action cluster inside a section-header that needs to stay together as a single flex item so the parent's justify-content:space-between can pin it to the right.

Same edit drops the inline style="margin-top:0;" from <h3 id="archiveTitle"> — the global * { margin: 0 } reset already zeroes it, so the inline declaration was a literal no-op that just added noise to the markup. Worth removing as it might mislead future readers into thinking the override does something.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:17:09 +02:00