Die Oberfläche nach Plex' Vorbild — Hintergrund, Schriftzug, Besetzung
Nach den Bildern deiner Plex-Installation umgebaut. Übernommen ist der Aufbau, nicht die Farbe: Plex ist bernsteingelb, Flaro bleibt Orchidee. **Kopfleiste oben** mit Marke, Suche und Konto. Die Suche gehört dorthin und nicht in jede Mediathek: wer einen Titel sucht, weiss oft gar nicht, in welcher er liegt. Sie geht quer durch alles. **Seitenleiste mit Icons** — echte SVG, keine Zeichen aus einer Schriftart. Ein ▶ aus Unicode sieht auf jedem System anders aus, lässt sich nicht einfärben und springt in der Grundlinie. Die aktive Zeile bekommt einen schmalen Balken links statt eines leuchtenden Blocks; das liest sich in einer langen Liste ruhiger. **Die Kachel steht jetzt an einer Stelle** statt an fünfen. Sie trägt den Haken für Gesehenes in der Ecke — wie bei Plex, ein Blick über das Raster genügt —, die Zahl offener Folgen an derselben Stelle, den Fortschrittsbalken unten und beim Überfahren einen Abspielknopf. Folgen sind dreizeilig: Serie, Folgentitel, „S14 · F22". **Reihen haben Pfeile.** Zwanzig Kacheln passen nicht in den Bildschirm, und quer zu scrollen geht mit dem Rad nur mit Umschalttaste. Die Überschrift nennt die Mediathek beim Namen: „Zuletzt in Dokumentationen hinzugefügt". **Mediatheken haben drei Reiter** — Empfehlungen, Mediathek, Kategorien. Darunter eine Filterzeile mit Genre, Sortierung und der Anzahl. Die Kategorien sind farbige Kacheln; die Farbe kommt aus dem Namen, nicht aus einer festen Liste, die jedes Genre kennen müsste, das TMDB je erfindet. **Die Detailseite** hat jetzt den unscharfen Hintergrund über die ganze Seite, den **Schriftzug des Films** statt der Systemschrift, eine Zeile mit Jahr, Genres, Laufzeit, Altersfreigabe und Bewertung, runde Knöpfe für gesehen/Favorit/weglegen, die Medienangaben als Tabelle — Datei, Video, Ton, Untertitel — und zuletzt Besetzung & Stab mit runden Porträts. **Dazu am Server:** ein Name. Ohne ihn zeigten die Clients den Hostnamen, und die App schrieb „localhost" über die Anmeldung. Vorgabe ist der Rechnername des Containers. --- **Der Fehler, den das erste Bild zeigte:** die Lupe in der Suchleiste war dreihundert Bildpunkte gross und lag über der halben Seite. Ein rohes `<svg>` ohne Breite füllt seinen Container; die Regel galt nur für `.icon`. Jetzt für jedes. 368 Tests grün. Am Container nachgesehen: „Der Untergang" mit eigenem Schriftzug, FSK 12, Bewertung 7,9, Genres Drama/Historie/Kriegsfilm, beide Tonspuren in der Tabelle, 22 Porträts darunter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ede4069cd4
commit
8c395ac051
@@ -0,0 +1,59 @@
|
||||
import { filme, serien } from '../api';
|
||||
import { esc, leer, fehlerzeile } from '../util';
|
||||
import { kachel, alsKachel } from '../card';
|
||||
|
||||
/** Suchergebnisse quer durch alle Mediatheken.
|
||||
*
|
||||
* **Nicht je Mediathek.** Wer einen Titel sucht, weiss oft gar nicht, in
|
||||
* welcher er liegt — genau deshalb steht die Suche oben in der Kopfleiste
|
||||
* und nicht in jeder Mediathek einzeln. */
|
||||
export function suchseite(
|
||||
text: string,
|
||||
oeffnen: (art: 'filme' | 'serien', id: string) => void,
|
||||
): HTMLElement {
|
||||
const wurzel = document.createElement('div');
|
||||
|
||||
const kopf = document.createElement('div');
|
||||
kopf.className = 'seitenkopf';
|
||||
kopf.innerHTML = `<div class="wer"><h1>${esc(text)}</h1>`
|
||||
+ '<div class="quelle">Suchergebnisse</div></div>';
|
||||
wurzel.appendChild(kopf);
|
||||
|
||||
const buehne = document.createElement('div');
|
||||
buehne.innerHTML = '<div class="spinner"></div>';
|
||||
wurzel.appendChild(buehne);
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
const [f, s] = await Promise.all([
|
||||
filme({ search: text, take: 100 }),
|
||||
serien({ search: text, take: 100 }),
|
||||
]);
|
||||
|
||||
buehne.innerHTML = '';
|
||||
if (f.total === 0 && s.total === 0) {
|
||||
buehne.appendChild(leer(`Nichts gefunden für „${text}".`));
|
||||
return;
|
||||
}
|
||||
|
||||
if (f.total > 0) buehne.appendChild(abschnitt(`Filme · ${f.total}`, f.items));
|
||||
if (s.total > 0) buehne.appendChild(abschnitt(`Serien · ${s.total}`, s.items));
|
||||
} catch (fehler) {
|
||||
buehne.innerHTML = '';
|
||||
buehne.appendChild(fehlerzeile((fehler as Error).message));
|
||||
}
|
||||
})();
|
||||
|
||||
function abschnitt(titel: string, eintraege: Parameters<typeof alsKachel>[0][]): HTMLElement {
|
||||
const bereich = document.createElement('section');
|
||||
const h = document.createElement('h2');
|
||||
h.textContent = titel;
|
||||
const gitter = document.createElement('div');
|
||||
gitter.className = 'raster';
|
||||
eintraege.forEach(e => gitter.appendChild(kachel(alsKachel(e), oeffnen)));
|
||||
bereich.append(h, gitter);
|
||||
return bereich;
|
||||
}
|
||||
|
||||
return wurzel;
|
||||
}
|
||||
Reference in New Issue
Block a user