docs: Spec-Korrekturen nach Review
- Jinja2-Variante entfernt (nur JS-Lösung) - i18n: bestehenden Key status.mark_series verwenden - Responsive Breakpoint 1200px berücksichtigt - opacity-Fix nur für TV (hover:none Media-Query) - Änderung 2/2b als atomar markiert - focusin-Handler Sidebar-Ausschluss dokumentiert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
205830f474
commit
efbda20e42
1 changed files with 38 additions and 40 deletions
|
|
@ -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
|
||||
<!-- Verfügbare Buchstaben sammeln -->
|
||||
{% 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 %}
|
||||
|
||||
<nav class="tv-alpha-sidebar" id="alpha-sidebar" ...>
|
||||
{% for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#' %}
|
||||
{% if letter in available_letters %}
|
||||
<span class="tv-alpha-letter" data-letter="{{ letter }}"
|
||||
onclick="filterByLetter('{{ letter }}')" data-focusable>{{ letter }}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</nav>
|
||||
```
|
||||
|
||||
**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)">
|
||||
<span class="mark-series-icon">✓</span>
|
||||
<span class="mark-series-text">{{ t('series.mark_all_watched') }}</span>
|
||||
<span class="mark-series-text">{{ t('status.mark_series') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue