diff --git a/docs/superpowers/specs/2026-03-16-tv-dpad-fixes-design.md b/docs/superpowers/specs/2026-03-16-tv-dpad-fixes-design.md index 3ac3129..e8c87b2 100644 --- a/docs/superpowers/specs/2026-03-16-tv-dpad-fixes-design.md +++ b/docs/superpowers/specs/2026-03-16-tv-dpad-fixes-design.md @@ -30,30 +30,7 @@ Die Sidebar rendert alle 26 Buchstaben + `#` (27 Einträge × 36px = 972px). Buc ### Lösung Im Template `series.html` (Zeile 161-167): Nur Buchstaben rendern, die tatsächlich Serien haben. Das Backend liefert bereits die Serien mit `data-letter` Attribut — wir sammeln die verfügbaren Buchstaben serverseitig oder per Jinja2-Filter. -**Ansatz:** Die verfügbaren Buchstaben werden im Template aus den Serien-Daten extrahiert: - -```html - -{% set available_letters = [] %} -{% for s in series %} - {% set letter = s.sort_title[0:1]|upper if s.sort_title else '#' %} - {% set letter = letter if letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' else '#' %} - {% if letter not in available_letters %} - {% do available_letters.append(letter) %} - {% endif %} -{% endfor %} - - -``` - -**Fallback:** Falls Jinja2 `{% do %}` nicht unterstützt wird, die Filterung per JavaScript im bestehenden IIFE durchführen: +**Ansatz:** Per JavaScript im bestehenden IIFE — Buchstaben ohne Treffer komplett entfernen statt dimmen. Das bestehende Script (series.html, Zeile 244-253) wird angepasst: ```javascript // Buchstaben ohne Treffer komplett entfernen (statt nur dimmen) @@ -69,7 +46,7 @@ Im Template `series.html` (Zeile 161-167): Nur Buchstaben rendern, die tatsächl })(); ``` -**Bevorzugter Ansatz:** JavaScript-Variante — einfacher, kein Backend-Change nötig. +Kein Backend-Change nötig, kein Jinja2-Template-Change. ## Änderung 2: Alphabet-Sidebar per D-Pad erreichbar machen @@ -153,6 +130,8 @@ if (!inNav && !inSidebar && direction === "ArrowRight") { } ``` +**WICHTIG: Beide folgenden Änderungen sind atomar — sie müssen zusammen eingefügt werden!** + Zusätzlich: Die Sidebar-Elemente aus der normalen Nearest-Neighbor-Suche ausschließen (Zeile 240-243 erweitern): ```javascript @@ -161,20 +140,37 @@ const searchEls = inNav : focusables.filter(el => !el.closest("#tv-nav") && !el.closest(".tv-alpha-sidebar")); ``` +Außerdem: Im `focusin`-Handler (tv.js) die Sidebar aus dem `_lastContentFocus`-Tracking ausschließen, damit der ArrowLeft-Rücksprung zuverlässig funktioniert: + +```javascript +// Im focusin-Handler: Sidebar-Elemente nicht als Content-Focus speichern +if (!el.closest("#tv-nav") && !el.closest(".tv-alpha-sidebar")) { + this._lastContentFocus = el; +} +``` + +**Enter-Handling:** Wenn der Nutzer in der Sidebar Enter drückt, simuliert der FocusManager einen Click auf das `data-focusable`-Element, was `onclick="filterByLetter()"` auslöst. Kein zusätzlicher Code nötig. + ## Änderung 3: Episoden-Cards vergrößern ### Problem Das Episode-Grid nutzt `minmax(180px, 1fr)` (tv.css, Zeile 1821). Auf einem 1080p-TV sind die Cards zu klein und die Laufzeit-Badge wird abgeschnitten. ### Lösung -Grid-Spalten vergrößern und Laufzeit-Badge anpassen: +Grid-Spalten vergrößern und Laufzeit-Badge anpassen. **Achtung:** Es gibt einen `@media (min-width: 1200px)` Breakpoint (tv.css, Zeile 2019-2020) der auf `minmax(220px, 1fr)` setzt — dieser muss ebenfalls angepasst werden. ```css +/* Basis (tv.css, Zeile 1821) */ .tv-episode-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); /* war 180px */ gap: 1rem; /* war 0.8rem */ } +/* Responsive: 1200px+ Breakpoint (tv.css, Zeile 2019-2020) — anpassen */ +@media (min-width: 1200px) { + .tv-episode-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); } /* war 220px */ +} + .tv-ep-duration { font-size: 0.8rem; /* war 0.7rem */ padding: 3px 8px; /* war 2px 6px */ @@ -202,11 +198,14 @@ Der Mark-Button (series_detail.html, Zeile 129-132) hat `tabindex="-1"` und kein - `tabindex="-1"` entfernen - `data-focusable` hinzufügen -**CSS** (tv.css): Mark-Button auf TV immer sichtbar machen: +**CSS** (tv.css): Mark-Button auf TV immer sichtbar machen. Der Base-Wert `opacity: 0` bleibt für Desktop/Mobile erhalten — nur auf TV (kein Hover-Support) wird der Button dauerhaft sichtbar: + ```css -/* Mark-Button auf TV immer sichtbar (halbtransparent) */ -.tv-ep-tile-mark { - opacity: 0.5; /* war 0 — immer leicht sichtbar */ +/* TV-Geräte (kein Hover): Mark-Button immer leicht sichtbar */ +@media (hover: none) { + .tv-ep-tile-mark { + opacity: 0.5; + } } .tv-episode-tile:hover .tv-ep-tile-mark, @@ -242,7 +241,7 @@ Es gibt nur `markSeasonWatched()` pro Staffel (series_detail.html, Zeile 96-99). data-series-id="{{ series.id }}" onclick="markSeriesWatched(this)"> - {{ t('series.mark_all_watched') }} + {{ t('status.mark_series') }} ``` @@ -328,25 +327,24 @@ function markSeriesWatched(btn) { } ``` -**i18n** — Neuer Übersetzungsschlüssel: -``` -series.mark_all_watched = "Serie als gesehen" (de_DE) -series.mark_all_watched = "Mark series watched" (en_US) -``` +**i18n** — Bestehender Schlüssel `status.mark_series` wird verwendet: +- `de.json`: `"mark_series": "Serie als gesehen"` (bereits vorhanden) +- `en.json`: `"mark_series": "Mark series as watched"` (bereits vorhanden) + +Kein neuer i18n-Key nötig. ## Zusammenfassung der Änderungen | # | Datei | Art | Beschreibung | |---|-------|-----|-------------| | 1 | `series.html` | JS ändern | Buchstaben ohne Serien per JS entfernen statt dimmen | -| 2 | `tv.js` | Code einfügen | Explizite Sidebar-Navigation (ArrowRight→Sidebar, ArrowLeft→Content, sequentiell Up/Down) | -| 2b | `tv.js` | Code ändern | Sidebar aus Nearest-Neighbor-Suche ausschließen | +| 2 | `tv.js` | Code einfügen | **ATOMAR:** Explizite Sidebar-Navigation + Sidebar aus Nearest-Neighbor ausschließen + focusin-Handler anpassen | | 3 | `tv.css` | CSS ändern | Episode-Grid: `minmax(240px, 1fr)`, Laufzeit-Badge größer | | 4 | `series_detail.html` | HTML ändern | Mark-Button: `tabindex="-1"` → `data-focusable` | -| 4b | `tv.css` | CSS ändern | Mark-Button: `opacity: 0.5` statt `0`, Focus-Ring | +| 4b | `tv.css` | CSS ändern | Mark-Button: `opacity: 0.5` nur auf TV (hover:none), Focus-Ring | | 5 | `series_detail.html` | HTML+JS einfügen | "Serie als gesehen"-Button + `markSeriesWatched()` | | 5b | `tv.css` | CSS einfügen | Styling für `.tv-mark-series-btn` | -| 5c | i18n | Key einfügen | `series.mark_all_watched` | +| 5c | i18n | — | Bestehender Key `status.mark_series` wird verwendet (kein Change) | ## Risiken & Hinweise