Flixy heißt ab hier Halo
Reine Umbenennung, kein Verhalten geändert — bewusst als eigener Commit, damit „umbenannt" und „umgebaut" im Nachhinein auseinanderzuhalten sind. 712 mal `Flixy`, 778 mal `flixy`, 7 mal `FLIXY` in 169 Dateien, dazu 25 Pfade. Ziele: `Halo`, `HaloTV`, `HaloTopShelf`, `HaloUITests`, `HaloKit` mit `HaloDesign`, `HaloDownloads`, `HaloPlayback` und `HaloProbe`. `MediaKit`, `JellyfinKit` und `DownloadWidget` tragen den Namen nicht und bleiben. `docs/superpowers/specs/` ist ausgenommen. Datierte Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen; sie nachträglich umzuschreiben hiesse, die Vergangenheit zu glätten. **Die Bundle-ID wandert mit** — ausdrücklich so gewollt, mit bekanntem Preis: `com.scarriffleservices.halo` ist für iOS eine *andere* App. Die installierte Fassung bleibt daneben stehen, mit Downloads und Anmeldung in ihrem Container; Halo startet leer. Einmal neu anmelden, heruntergeladene Folgen einmal neu laden. Dasselbe gilt für die App-Gruppe, die sich TV-App und Top Shelf teilen. **Eine Folge davon, die beim Entscheiden noch nicht auf dem Tisch lag:** der Aufräumschritt aus dem vorigen Commit kommt an die alten Plex-Token nicht mehr heran. Der Schlüsselbund grenzt nach Team und Bundle-Kennung ab — was unter `…flixy` liegt, ist für `…halo` unerreichbar. Erledigt wird es trotzdem, nur anders: **die alte App löschen.** iOS räumt deren Schlüsselbund-Einträge beim Deinstallieren mit ab. Der Code bleibt stehen, weil er für jeden Stand vor dieser Umbenennung richtig ist und im Jahr einen Startvergleich kostet. Die Projektdatei ist aus `project.yml` neu erzeugt, der alte Ordner gelöscht statt umbenannt — sie steht ohnehin nicht im Repo. Das Verzeichnis des Repos selbst heißt weiter `Flixy`; das umzubenennen ist ein Handgriff ausserhalb des Projekts. 368 Tests grün. iOS und tvOS bauen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2fcf6821c8
commit
4b9a1e7b67
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Flixy Web
|
||||
# Halo Web
|
||||
|
||||
Die Weboberfläche für Rechner, auf denen es keine App gibt — Windows vor allem.
|
||||
|
||||
@@ -28,7 +28,7 @@ Nebenbei bleibt der Zugangsschlüssel beim Server, statt im Browser zu liegen.
|
||||
|
||||
```bash
|
||||
cd app && npm install && npm run build
|
||||
cd ../server && FLIXY_SERVER=http://jellyfin:8096 node index.js
|
||||
cd ../server && HALO_SERVER=http://jellyfin:8096 node index.js
|
||||
```
|
||||
|
||||
Dann auf `http://localhost:8080`. Der Port lässt sich über `PORT` ändern.
|
||||
@@ -36,7 +36,7 @@ Dann auf `http://localhost:8080`. Der Port lässt sich über `PORT` ändern.
|
||||
## Entwickeln
|
||||
|
||||
```bash
|
||||
cd app && FLIXY_SERVER=http://jellyfin:8096 npm run dev
|
||||
cd app && HALO_SERVER=http://jellyfin:8096 npm run dev
|
||||
```
|
||||
|
||||
Vite übernimmt dabei das Weiterreichen, der eigene Server wird nicht gebraucht.
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Flixy</title>
|
||||
<title>Halo</title>
|
||||
</head>
|
||||
<body>
|
||||
<section id="login"></section>
|
||||
|
||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "flixy-web",
|
||||
"name": "halo-web",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "flixy-web",
|
||||
"name": "halo-web",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"hls.js": "^1.5.18"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "flixy-web",
|
||||
"name": "halo-web",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
|
||||
+10
-10
@@ -14,13 +14,13 @@ import type { Wiedergabeauskunft } from './types';
|
||||
const BASIS = '/api';
|
||||
|
||||
export const sitzung = {
|
||||
token: localStorage.getItem('flixy.token') ?? '',
|
||||
userId: localStorage.getItem('flixy.user') ?? '',
|
||||
token: localStorage.getItem('halo.token') ?? '',
|
||||
userId: localStorage.getItem('halo.user') ?? '',
|
||||
geraet:
|
||||
localStorage.getItem('flixy.device') ??
|
||||
localStorage.getItem('halo.device') ??
|
||||
(() => {
|
||||
const id = 'flixy-web-' + Math.random().toString(36).slice(2, 12);
|
||||
localStorage.setItem('flixy.device', id);
|
||||
const id = 'halo-web-' + Math.random().toString(36).slice(2, 12);
|
||||
localStorage.setItem('halo.device', id);
|
||||
return id;
|
||||
})(),
|
||||
};
|
||||
@@ -32,7 +32,7 @@ export const sitzung = {
|
||||
* angemeldet, obwohl der Schlüssel stimmt. */
|
||||
function kopfzeile(): string {
|
||||
const teile = [
|
||||
'Client="Flixy Web"',
|
||||
'Client="Halo Web"',
|
||||
'Device="Browser"',
|
||||
`DeviceId="${sitzung.geraet}"`,
|
||||
'Version="0.1"',
|
||||
@@ -90,13 +90,13 @@ export async function anmelden(benutzer: string, passwort: string): Promise<void
|
||||
);
|
||||
sitzung.token = r.AccessToken;
|
||||
sitzung.userId = r.User.Id;
|
||||
localStorage.setItem('flixy.token', sitzung.token);
|
||||
localStorage.setItem('flixy.user', sitzung.userId);
|
||||
localStorage.setItem('halo.token', sitzung.token);
|
||||
localStorage.setItem('halo.user', sitzung.userId);
|
||||
}
|
||||
|
||||
export function abmelden(): void {
|
||||
localStorage.removeItem('flixy.token');
|
||||
localStorage.removeItem('flixy.user');
|
||||
localStorage.removeItem('halo.token');
|
||||
localStorage.removeItem('halo.user');
|
||||
location.reload();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Kontrastrechnung nach WCAG 2.1 — dieselbe wie in der App.
|
||||
----------------------------------------------------------------------------
|
||||
Flixy lässt jede Farbe zu, aber ein dunkles Marineblau auf fast schwarzem
|
||||
Halo lässt jede Farbe zu, aber ein dunkles Marineblau auf fast schwarzem
|
||||
Grund ist als Schrift unlesbar. Solche Farben werden angehoben, bis sie den
|
||||
geforderten Kontrast erreichen — und es wird gesagt, statt die Farbe
|
||||
stillschweigend zu verändern.
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ function inselBauen(): void {
|
||||
insel.appendChild(b);
|
||||
};
|
||||
|
||||
stueck('brand', 'Flixy');
|
||||
stueck('brand', 'Halo');
|
||||
eintrag('Start', 'home');
|
||||
eintrag('Einstellungen', 'settings');
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ const vorgabe: Einstellungen = {
|
||||
};
|
||||
|
||||
function lesen<K extends keyof Einstellungen>(schluessel: K): Einstellungen[K] {
|
||||
const roh = localStorage.getItem('flixy.' + schluessel);
|
||||
const roh = localStorage.getItem('halo.' + schluessel);
|
||||
if (roh == null) return vorgabe[schluessel];
|
||||
const art = typeof vorgabe[schluessel];
|
||||
if (Array.isArray(vorgabe[schluessel])) {
|
||||
@@ -92,7 +92,7 @@ export function setzen<K extends keyof Einstellungen>(schluessel: K, wert: Einst
|
||||
// Listen müssen als JSON abgelegt werden — ein Array landete sonst als
|
||||
// "a,b,c" im Speicher und käme als Zeichenkette zurück.
|
||||
localStorage.setItem(
|
||||
'flixy.' + schluessel,
|
||||
'halo.' + schluessel,
|
||||
Array.isArray(wert) ? JSON.stringify(wert) : String(wert),
|
||||
);
|
||||
anwenden();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* ============================================================================
|
||||
Flixy Web — ein Frontend für Jellyfin
|
||||
Halo Web — ein Frontend für Jellyfin
|
||||
----------------------------------------------------------------------------
|
||||
Die Werte stammen aus FlixyDesign, nicht aus dem Augenmaß: dieselben
|
||||
Die Werte stammen aus HaloDesign, nicht aus dem Augenmaß: dieselben
|
||||
Flächen, dieselben Radien, dieselben Abstände wie in der App.
|
||||
========================================================================= */
|
||||
:root {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { esc } from '../util';
|
||||
export function anmeldung(wurzel: HTMLElement, fertig: () => void): void {
|
||||
wurzel.innerHTML = `
|
||||
<form class="login-card" id="login-form">
|
||||
<h1>Flixy</h1>
|
||||
<h1>Halo</h1>
|
||||
<p class="sub">Deine Mediathek, schöner.</p>
|
||||
<div class="field">
|
||||
<label for="user">Benutzer</label>
|
||||
|
||||
@@ -474,8 +474,8 @@ function fortschritt(
|
||||
...extra,
|
||||
}),
|
||||
})
|
||||
.then(() => console.debug(`[Flixy] ${weg} → ${(wo / 60).toFixed(1)} min angenommen`))
|
||||
.catch((e: Error) => console.warn(`[Flixy] ${weg} abgelehnt: ${e.message}`));
|
||||
.then(() => console.debug(`[Halo] ${weg} → ${(wo / 60).toFixed(1)} min angenommen`))
|
||||
.catch((e: Error) => console.warn(`[Halo] ${weg} abgelehnt: ${e.message}`));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export default defineConfig({
|
||||
// Herkunft dieselbe wie die der Seite — und CORS nie wieder ein Thema.
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: process.env.FLIXY_SERVER ?? 'http://localhost:8096',
|
||||
target: process.env.HALO_SERVER ?? 'http://localhost:8096',
|
||||
changeOrigin: true,
|
||||
rewrite: (pfad) => pfad.replace(/^\/api/, ''),
|
||||
},
|
||||
|
||||
+37
-37
@@ -3,12 +3,12 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Flixy</title>
|
||||
<title>Halo</title>
|
||||
<style>
|
||||
/* ============================================================================
|
||||
Flixy Web — ein Frontend für Jellyfin
|
||||
Halo Web — ein Frontend für Jellyfin
|
||||
----------------------------------------------------------------------------
|
||||
Die Werte stammen aus FlixyDesign, nicht aus dem Augenmaß: dieselben
|
||||
Die Werte stammen aus HaloDesign, nicht aus dem Augenmaß: dieselben
|
||||
Flächen, dieselben Radien, dieselben Abstände wie in der App.
|
||||
========================================================================= */
|
||||
:root {
|
||||
@@ -568,7 +568,7 @@ input[type=range] { width: 100%; accent-color: var(--accent); }
|
||||
<!-- ---------- Anmeldung ---------- -->
|
||||
<section id="login">
|
||||
<form class="login-card" id="login-form">
|
||||
<h1>Flixy</h1>
|
||||
<h1>Halo</h1>
|
||||
<p class="sub">Deine Mediathek, schöner.</p>
|
||||
<div class="field">
|
||||
<label for="server">Serveradresse</label>
|
||||
@@ -607,18 +607,18 @@ input[type=range] { width: 100%; accent-color: var(--accent); }
|
||||
Jellyfin die Zeile falsch und meldet, man sei nicht angemeldet.
|
||||
========================================================================= */
|
||||
const S = {
|
||||
base: localStorage.getItem('flixy.server') || '',
|
||||
token: localStorage.getItem('flixy.token') || '',
|
||||
userId: localStorage.getItem('flixy.user') || '',
|
||||
deviceId: localStorage.getItem('flixy.device') || (() => {
|
||||
const id = 'flixy-web-' + Math.random().toString(36).slice(2, 12);
|
||||
localStorage.setItem('flixy.device', id); return id;
|
||||
base: localStorage.getItem('halo.server') || '',
|
||||
token: localStorage.getItem('halo.token') || '',
|
||||
userId: localStorage.getItem('halo.user') || '',
|
||||
deviceId: localStorage.getItem('halo.device') || (() => {
|
||||
const id = 'halo-web-' + Math.random().toString(36).slice(2, 12);
|
||||
localStorage.setItem('halo.device', id); return id;
|
||||
})(),
|
||||
};
|
||||
|
||||
function authHeader() {
|
||||
const f = [
|
||||
'Client="Flixy Web"', 'Device="Browser"',
|
||||
'Client="Halo Web"', 'Device="Browser"',
|
||||
`DeviceId="${S.deviceId}"`, 'Version="0.1"',
|
||||
];
|
||||
if (S.token) f.push(`Token="${S.token}"`);
|
||||
@@ -675,9 +675,9 @@ el('login-form').addEventListener('submit', async (e) => {
|
||||
body: JSON.stringify({ Username: el('user').value, Pw: el('pass').value }),
|
||||
});
|
||||
S.token = r.AccessToken; S.userId = r.User.Id;
|
||||
localStorage.setItem('flixy.server', S.base);
|
||||
localStorage.setItem('flixy.token', S.token);
|
||||
localStorage.setItem('flixy.user', S.userId);
|
||||
localStorage.setItem('halo.server', S.base);
|
||||
localStorage.setItem('halo.token', S.token);
|
||||
localStorage.setItem('halo.user', S.userId);
|
||||
start();
|
||||
} catch (e2) {
|
||||
err.textContent = /Failed to fetch/.test(e2.message)
|
||||
@@ -689,7 +689,7 @@ el('login-form').addEventListener('submit', async (e) => {
|
||||
});
|
||||
|
||||
function signOut() {
|
||||
['flixy.server', 'flixy.token', 'flixy.user'].forEach((k) => localStorage.removeItem(k));
|
||||
['halo.server', 'halo.token', 'halo.user'].forEach((k) => localStorage.removeItem(k));
|
||||
location.reload();
|
||||
}
|
||||
|
||||
@@ -707,28 +707,28 @@ const PALETTE = [
|
||||
];
|
||||
|
||||
const P = {
|
||||
accent: localStorage.getItem('flixy.accent') || '#C04AC9',
|
||||
nav: localStorage.getItem('flixy.nav') || 'top', // 'top' | 'side'
|
||||
cardW: +(localStorage.getItem('flixy.cardW') || 150),
|
||||
accent: localStorage.getItem('halo.accent') || '#C04AC9',
|
||||
nav: localStorage.getItem('halo.nav') || 'top', // 'top' | 'side'
|
||||
cardW: +(localStorage.getItem('halo.cardW') || 150),
|
||||
// Startseite — dieselben Stellschrauben wie in der App.
|
||||
heroSource: localStorage.getItem('flixy.heroSource') || 'continueFirst',
|
||||
heroCount: +(localStorage.getItem('flixy.heroCount') || 5),
|
||||
heroHeight: +(localStorage.getItem('flixy.heroHeight') || 82),
|
||||
combineContinue: localStorage.getItem('flixy.combineContinue') !== 'false',
|
||||
hideWatched: localStorage.getItem('flixy.hideWatched') === 'true',
|
||||
heroSource: localStorage.getItem('halo.heroSource') || 'continueFirst',
|
||||
heroCount: +(localStorage.getItem('halo.heroCount') || 5),
|
||||
heroHeight: +(localStorage.getItem('halo.heroHeight') || 82),
|
||||
combineContinue: localStorage.getItem('halo.combineContinue') !== 'false',
|
||||
hideWatched: localStorage.getItem('halo.hideWatched') === 'true',
|
||||
// Welche Reihen ausgeblendet sind und in welcher Reihenfolge der Rest
|
||||
// steht — dieselbe Aufteilung wie `Arrangement` in der App.
|
||||
hiddenRows: JSON.parse(localStorage.getItem('flixy.hiddenRows') || '[]'),
|
||||
rowOrder: JSON.parse(localStorage.getItem('flixy.rowOrder') || '[]'),
|
||||
hiddenRows: JSON.parse(localStorage.getItem('halo.hiddenRows') || '[]'),
|
||||
rowOrder: JSON.parse(localStorage.getItem('halo.rowOrder') || '[]'),
|
||||
// Wiedergabe — dieselben Stufen wie in der App, damit derselbe Begriff
|
||||
// überall dasselbe heißt.
|
||||
rate: +(localStorage.getItem('flixy.rate') || 1.0),
|
||||
skipForward: +(localStorage.getItem('flixy.skipForward') || 30),
|
||||
skipBackward: +(localStorage.getItem('flixy.skipBackward') || 10),
|
||||
quality: +(localStorage.getItem('flixy.quality') || 0), // 0 = Original
|
||||
rate: +(localStorage.getItem('halo.rate') || 1.0),
|
||||
skipForward: +(localStorage.getItem('halo.skipForward') || 30),
|
||||
skipBackward: +(localStorage.getItem('halo.skipBackward') || 10),
|
||||
quality: +(localStorage.getItem('halo.quality') || 0), // 0 = Original
|
||||
// Sprachen. Leer heißt: was der Server vorgibt.
|
||||
audioLanguage: localStorage.getItem('flixy.audioLanguage') || '',
|
||||
subtitleLanguage: localStorage.getItem('flixy.subtitleLanguage') || '',
|
||||
audioLanguage: localStorage.getItem('halo.audioLanguage') || '',
|
||||
subtitleLanguage: localStorage.getItem('halo.subtitleLanguage') || '',
|
||||
};
|
||||
|
||||
/// Die Stufen der App, damit die Begriffe sich decken.
|
||||
@@ -759,7 +759,7 @@ function setPref(key, value) {
|
||||
P[key] = value;
|
||||
// Listen müssen als JSON abgelegt werden — ein Array landete sonst als
|
||||
// "a,b,c" im Speicher und käme als Zeichenkette zurück.
|
||||
localStorage.setItem('flixy.' + key, Array.isArray(value) ? JSON.stringify(value) : value);
|
||||
localStorage.setItem('halo.' + key, Array.isArray(value) ? JSON.stringify(value) : value);
|
||||
applyPrefs();
|
||||
}
|
||||
|
||||
@@ -833,7 +833,7 @@ function buildIsland() {
|
||||
|
||||
// Die Marke steht nur in der Spalte — oben nähme sie Platz weg, den die
|
||||
// Einträge brauchen.
|
||||
bit('brand', 'Flixy');
|
||||
bit('brand', 'Halo');
|
||||
add('Start', 'home');
|
||||
add('Einstellungen', 'settings');
|
||||
|
||||
@@ -929,7 +929,7 @@ function go(key) {
|
||||
/* ---------- Einstellungen ---------- */
|
||||
/* Kontrastrechnung nach WCAG 2.1 — dieselbe wie in der App.
|
||||
----------------------------------------------------------------------------
|
||||
Flixy lässt jede Farbe zu, aber ein dunkles Marineblau auf fast schwarzem
|
||||
Halo lässt jede Farbe zu, aber ein dunkles Marineblau auf fast schwarzem
|
||||
Grund ist als Schrift unlesbar. Solche Farben werden angehoben, bis sie den
|
||||
geforderten Kontrast erreichen — und es wird gesagt, statt die Farbe
|
||||
stillschweigend zu verändern.
|
||||
@@ -1809,7 +1809,7 @@ function openPlayer(item) {
|
||||
const anteil = lauf.sitzung?.stelle && v.duration
|
||||
? lauf.sitzung.stelle / v.duration : 0;
|
||||
if (anteil > 0 && anteil < 0.05) {
|
||||
console.info(`[Flixy] Bei ${(anteil * 100).toFixed(1)} % beendet — `
|
||||
console.info(`[Halo] Bei ${(anteil * 100).toFixed(1)} % beendet — `
|
||||
+ 'Jellyfin merkt sich das erst ab 5 %. Einstellbar im Dashboard unter '
|
||||
+ 'Wiedergabe → Fortsetzen.');
|
||||
}
|
||||
@@ -2114,9 +2114,9 @@ function wireProgress(video, session) {
|
||||
...extra,
|
||||
}),
|
||||
}).then(() => {
|
||||
console.debug(`[Flixy] ${path} → ${(stelle / 60).toFixed(1)} min angenommen`);
|
||||
console.debug(`[Halo] ${path} → ${(stelle / 60).toFixed(1)} min angenommen`);
|
||||
}).catch((e) => {
|
||||
console.warn(`[Flixy] ${path} abgelehnt: ${e.message}`);
|
||||
console.warn(`[Halo] ${path} abgelehnt: ${e.message}`);
|
||||
});
|
||||
};
|
||||
session.melden = send;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* ============================================================================
|
||||
Flixy für Jellyfin Web
|
||||
Halo für Jellyfin Web
|
||||
----------------------------------------------------------------------------
|
||||
Bringt Jellyfins Weboberfläche auf die Farben, Schrift und Formen der
|
||||
Flixy-App. Es ist ein Kleid, kein Umbau: die Oberfläche bleibt Jellyfins,
|
||||
Halo-App. Es ist ein Kleid, kein Umbau: die Oberfläche bleibt Jellyfins,
|
||||
sie sieht nur anders aus.
|
||||
|
||||
EINBAUEN
|
||||
@@ -26,32 +26,32 @@
|
||||
Orchid #C04AC9 · Rose #F76190
|
||||
Voreingestellt ist Orchid — die Farbe, die auf dem iPad eingestellt
|
||||
ist. Die App selbst kommt ab Werk mit Mint. */
|
||||
--flixy-accent: #C04AC9;
|
||||
--halo-accent: #C04AC9;
|
||||
|
||||
/* ---- Die Flächen der App, von hinten nach vorn ---------------------- */
|
||||
--flixy-canvas: #0B0B0D; /* Seitenhintergrund */
|
||||
--flixy-surface: #141417; /* Karten, Kopfbereiche */
|
||||
--flixy-elevated: #1D1D21; /* Blätter, Menüs */
|
||||
--flixy-placeholder: #26262B; /* Platzhalter, gedrückte Zustände */
|
||||
--halo-canvas: #0B0B0D; /* Seitenhintergrund */
|
||||
--halo-surface: #141417; /* Karten, Kopfbereiche */
|
||||
--halo-elevated: #1D1D21; /* Blätter, Menüs */
|
||||
--halo-placeholder: #26262B; /* Platzhalter, gedrückte Zustände */
|
||||
|
||||
--flixy-text: #F5F5F7;
|
||||
--flixy-text-secondary: #A0A0A8;
|
||||
--flixy-text-muted: #6E6E76;
|
||||
--flixy-destructive: #E5484D;
|
||||
--flixy-separator: rgba(255, 255, 255, 0.10);
|
||||
--halo-text: #F5F5F7;
|
||||
--halo-text-secondary: #A0A0A8;
|
||||
--halo-text-muted: #6E6E76;
|
||||
--halo-destructive: #E5484D;
|
||||
--halo-separator: rgba(255, 255, 255, 0.10);
|
||||
|
||||
/* ---- Formen --------------------------------------------------------- */
|
||||
--flixy-radius-card: 8px;
|
||||
--flixy-radius-control: 10px;
|
||||
--flixy-radius-sheet: 16px;
|
||||
--flixy-radius-panel: 22px; /* die schwebende Leiste */
|
||||
--halo-radius-card: 8px;
|
||||
--halo-radius-control: 10px;
|
||||
--halo-radius-sheet: 16px;
|
||||
--halo-radius-panel: 22px; /* die schwebende Leiste */
|
||||
|
||||
/* ---- Schrift --------------------------------------------------------
|
||||
Der größte einzelne Hebel. Jellyfin liefert Noto Sans aus; die
|
||||
Systemschrift macht aus derselben Seite sofort eine, die auf den
|
||||
Rechner gehört — SF auf Mac und iPhone, Segoe UI Variable auf
|
||||
Windows 11. */
|
||||
--flixy-font: -apple-system, BlinkMacSystemFont, "SF Pro Text",
|
||||
--halo-font: -apple-system, BlinkMacSystemFont, "SF Pro Text",
|
||||
"Segoe UI Variable Text", "Segoe UI", system-ui,
|
||||
Roboto, "Helvetica Neue", sans-serif;
|
||||
|
||||
@@ -59,52 +59,52 @@
|
||||
Hier entsteht der Großteil der Wirkung: Jellyfin baut auf einer
|
||||
MUI-Palette auf, und wer die verstellt, verstellt alles, was darauf
|
||||
aufsetzt — inklusive Bauteilen, die dieses CSS gar nicht kennt. */
|
||||
--jf-palette-background-default: var(--flixy-canvas);
|
||||
--jf-palette-background-default: var(--halo-canvas);
|
||||
--jf-palette-background-defaultChannel: 11 11 13;
|
||||
--jf-palette-background-paper: var(--flixy-surface);
|
||||
--jf-palette-background-paper: var(--halo-surface);
|
||||
--jf-palette-background-paperChannel: 20 20 23;
|
||||
|
||||
--jf-palette-primary-main: var(--flixy-accent);
|
||||
--jf-palette-primary-dark: var(--flixy-accent);
|
||||
--jf-palette-primary-light: var(--flixy-accent);
|
||||
--jf-palette-secondary-main: var(--flixy-accent);
|
||||
--jf-palette-secondary-dark: var(--flixy-accent);
|
||||
--jf-palette-secondary-light: var(--flixy-accent);
|
||||
--jf-palette-primary-main: var(--halo-accent);
|
||||
--jf-palette-primary-dark: var(--halo-accent);
|
||||
--jf-palette-primary-light: var(--halo-accent);
|
||||
--jf-palette-secondary-main: var(--halo-accent);
|
||||
--jf-palette-secondary-dark: var(--halo-accent);
|
||||
--jf-palette-secondary-light: var(--halo-accent);
|
||||
/* Schrift auf dem Akzent: hell, weil die Palette der App durchweg
|
||||
kräftige Farben hat. Bei einem hellen Akzent (Amber, Lime) hier auf
|
||||
#101013 stellen, sonst verschwindet die Beschriftung. */
|
||||
--jf-palette-primary-contrastText: #FFFFFF;
|
||||
--jf-palette-secondary-contrastText: #FFFFFF;
|
||||
|
||||
--jf-palette-text-primary: var(--flixy-text);
|
||||
--jf-palette-text-secondary: var(--flixy-text-secondary);
|
||||
--jf-palette-text-disabled: var(--flixy-text-muted);
|
||||
--jf-palette-text-icon: var(--flixy-text-secondary);
|
||||
--jf-palette-divider: var(--flixy-separator);
|
||||
--jf-palette-text-primary: var(--halo-text);
|
||||
--jf-palette-text-secondary: var(--halo-text-secondary);
|
||||
--jf-palette-text-disabled: var(--halo-text-muted);
|
||||
--jf-palette-text-icon: var(--halo-text-secondary);
|
||||
--jf-palette-divider: var(--halo-separator);
|
||||
|
||||
--jf-palette-error-main: var(--flixy-destructive);
|
||||
--jf-palette-error-main: var(--halo-destructive);
|
||||
--jf-palette-error-dark: #B4373B;
|
||||
--jf-palette-error-light: #EC6E72;
|
||||
|
||||
--jf-palette-action-hover: rgba(255, 255, 255, 0.06);
|
||||
--jf-palette-action-selected: rgba(255, 255, 255, 0.12);
|
||||
--jf-palette-action-disabled: var(--flixy-text-muted);
|
||||
--jf-palette-action-disabled: var(--halo-text-muted);
|
||||
|
||||
/* Die Kopfleiste trägt die Insel — sie selbst bleibt durchsichtig. */
|
||||
--jf-palette-AppBar-defaultBg: transparent;
|
||||
--jf-palette-AppBar-darkBg: transparent;
|
||||
|
||||
--jf-card-borderRadius: var(--flixy-radius-card);
|
||||
--jf-shape-borderRadius: var(--flixy-radius-control);
|
||||
--jf-card-borderRadius: var(--halo-radius-card);
|
||||
--jf-shape-borderRadius: var(--halo-radius-control);
|
||||
}
|
||||
|
||||
/* ---- Grundfläche und Schrift --------------------------------------------
|
||||
`html` bekommt seinen Hintergrund bei Jellyfin unmittelbar gesetzt und
|
||||
nicht über die Palette — deshalb steht er hier noch einmal ausdrücklich. */
|
||||
html {
|
||||
background-color: var(--flixy-canvas) !important;
|
||||
font-family: var(--flixy-font) !important;
|
||||
color: var(--flixy-text);
|
||||
background-color: var(--halo-canvas) !important;
|
||||
font-family: var(--halo-font) !important;
|
||||
color: var(--halo-text);
|
||||
}
|
||||
|
||||
body,
|
||||
@@ -117,12 +117,12 @@ textarea,
|
||||
.emby-select,
|
||||
.cardText,
|
||||
.sectionTitle {
|
||||
font-family: var(--flixy-font) !important;
|
||||
font-family: var(--halo-font) !important;
|
||||
}
|
||||
|
||||
.backgroundContainer,
|
||||
.mainAnimatedPage {
|
||||
background-color: var(--flixy-canvas) !important;
|
||||
background-color: var(--halo-canvas) !important;
|
||||
}
|
||||
|
||||
/* ---- Die Kopfleiste als schwebende Insel --------------------------------
|
||||
@@ -136,8 +136,8 @@ textarea,
|
||||
background: rgba(20, 20, 23, 0.72) !important;
|
||||
backdrop-filter: saturate(180%) blur(20px);
|
||||
-webkit-backdrop-filter: saturate(180%) blur(20px);
|
||||
border: 1px solid var(--flixy-separator);
|
||||
border-radius: var(--flixy-radius-panel);
|
||||
border: 1px solid var(--halo-separator);
|
||||
border-radius: var(--halo-radius-panel);
|
||||
margin: 10px 12px 0;
|
||||
width: calc(100% - 24px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
|
||||
@@ -149,18 +149,18 @@ textarea,
|
||||
|
||||
/* ---- Die Seitenleiste ---------------------------------------------------- */
|
||||
.mainDrawer {
|
||||
background-color: var(--flixy-surface) !important;
|
||||
border-right: 1px solid var(--flixy-separator);
|
||||
background-color: var(--halo-surface) !important;
|
||||
border-right: 1px solid var(--halo-separator);
|
||||
}
|
||||
|
||||
.navMenuOption {
|
||||
border-radius: var(--flixy-radius-control);
|
||||
border-radius: var(--halo-radius-control);
|
||||
margin: 2px 8px;
|
||||
}
|
||||
|
||||
.navMenuOption-selected {
|
||||
background: var(--flixy-elevated) !important;
|
||||
color: var(--flixy-accent) !important;
|
||||
background: var(--halo-elevated) !important;
|
||||
color: var(--halo-accent) !important;
|
||||
}
|
||||
|
||||
/* ---- Karten -------------------------------------------------------------
|
||||
@@ -171,7 +171,7 @@ textarea,
|
||||
.cardContent,
|
||||
.cardScalable,
|
||||
.cardOverlayContainer {
|
||||
border-radius: var(--flixy-radius-card) !important;
|
||||
border-radius: var(--halo-radius-card) !important;
|
||||
}
|
||||
|
||||
.cardBox {
|
||||
@@ -179,11 +179,11 @@ textarea,
|
||||
}
|
||||
|
||||
.cardText {
|
||||
color: var(--flixy-text);
|
||||
color: var(--halo-text);
|
||||
}
|
||||
|
||||
.cardText-secondary {
|
||||
color: var(--flixy-text-secondary);
|
||||
color: var(--halo-text-secondary);
|
||||
}
|
||||
|
||||
.defaultCardBackground,
|
||||
@@ -192,13 +192,13 @@ textarea,
|
||||
.defaultCardBackground3,
|
||||
.defaultCardBackground4,
|
||||
.defaultCardBackground5 {
|
||||
background-color: var(--flixy-placeholder) !important;
|
||||
background-color: var(--halo-placeholder) !important;
|
||||
}
|
||||
|
||||
/* Der Fortschrittsbalken auf angefangenen Titeln — in der App trägt er den
|
||||
Akzent, und er ist dünner als Jellyfins Voreinstellung. */
|
||||
.itemProgressBarForeground {
|
||||
background-color: var(--flixy-accent) !important;
|
||||
background-color: var(--halo-accent) !important;
|
||||
}
|
||||
|
||||
.itemProgressBar {
|
||||
@@ -211,18 +211,18 @@ textarea,
|
||||
.emby-button,
|
||||
.button-flat,
|
||||
.detailButton {
|
||||
border-radius: var(--flixy-radius-control) !important;
|
||||
font-family: var(--flixy-font) !important;
|
||||
border-radius: var(--halo-radius-control) !important;
|
||||
font-family: var(--halo-font) !important;
|
||||
}
|
||||
|
||||
.emby-button.raised {
|
||||
background: var(--flixy-elevated);
|
||||
color: var(--flixy-text);
|
||||
background: var(--halo-elevated);
|
||||
color: var(--halo-text);
|
||||
}
|
||||
|
||||
.emby-button.raised.button-submit,
|
||||
.button-submit {
|
||||
background: var(--flixy-accent) !important;
|
||||
background: var(--halo-accent) !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
@@ -236,22 +236,22 @@ textarea,
|
||||
.emby-input,
|
||||
.emby-select,
|
||||
.emby-textarea {
|
||||
background-color: var(--flixy-elevated) !important;
|
||||
border: 1px solid var(--flixy-separator) !important;
|
||||
border-radius: var(--flixy-radius-control) !important;
|
||||
color: var(--flixy-text) !important;
|
||||
background-color: var(--halo-elevated) !important;
|
||||
border: 1px solid var(--halo-separator) !important;
|
||||
border-radius: var(--halo-radius-control) !important;
|
||||
color: var(--halo-text) !important;
|
||||
}
|
||||
|
||||
.emby-input:focus,
|
||||
.emby-select:focus {
|
||||
border-color: var(--flixy-accent) !important;
|
||||
border-color: var(--halo-accent) !important;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.inputLabel,
|
||||
.selectLabel,
|
||||
.fieldDescription {
|
||||
color: var(--flixy-text-secondary) !important;
|
||||
color: var(--halo-text-secondary) !important;
|
||||
}
|
||||
|
||||
/* ---- Blätter, Menüs, Dialoge --------------------------------------------- */
|
||||
@@ -259,9 +259,9 @@ textarea,
|
||||
.formDialog,
|
||||
.actionSheet,
|
||||
.paper-icon-button-light + .actionSheet {
|
||||
background-color: var(--flixy-elevated) !important;
|
||||
border-radius: var(--flixy-radius-sheet) !important;
|
||||
border: 1px solid var(--flixy-separator);
|
||||
background-color: var(--halo-elevated) !important;
|
||||
border-radius: var(--halo-radius-sheet) !important;
|
||||
border: 1px solid var(--halo-separator);
|
||||
}
|
||||
|
||||
.actionSheetMenuItem:hover,
|
||||
@@ -278,7 +278,7 @@ textarea,
|
||||
|
||||
.detailSectionContent,
|
||||
.detailTableBodyCell {
|
||||
color: var(--flixy-text-secondary);
|
||||
color: var(--halo-text-secondary);
|
||||
}
|
||||
|
||||
/* Das Hintergrundbild soll nach unten in die Seitenfarbe auslaufen, statt an
|
||||
@@ -296,7 +296,7 @@ textarea,
|
||||
|
||||
/* ---- Überschriften ------------------------------------------------------- */
|
||||
.sectionTitle {
|
||||
color: var(--flixy-text) !important;
|
||||
color: var(--halo-text) !important;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
@@ -305,11 +305,11 @@ textarea,
|
||||
.listItem-border,
|
||||
.detailTableBodyCell,
|
||||
hr {
|
||||
border-color: var(--flixy-separator) !important;
|
||||
border-color: var(--halo-separator) !important;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--flixy-accent);
|
||||
background: var(--halo-accent);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
/* Der Flixy-Web-Server.
|
||||
/* Der Halo-Web-Server.
|
||||
----------------------------------------------------------------------------
|
||||
Zwei Aufgaben, und beide lösen etwas, woran die einzelne HTML-Datei
|
||||
gescheitert ist:
|
||||
@@ -16,7 +16,7 @@
|
||||
einem kleinen Container läuft, soll nicht sein halbes Verzeichnis
|
||||
mitschleppen.
|
||||
|
||||
Aufruf: FLIXY_SERVER=http://jellyfin:8096 node index.js
|
||||
Aufruf: HALO_SERVER=http://jellyfin:8096 node index.js
|
||||
*/
|
||||
|
||||
import { createServer } from 'node:http';
|
||||
@@ -25,8 +25,8 @@ import { join, extname, normalize } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const HIER = fileURLToPath(new URL('.', import.meta.url));
|
||||
const DATEIEN = process.env.FLIXY_STATIC ?? join(HIER, '..', 'app', 'dist');
|
||||
const ZIEL = (process.env.FLIXY_SERVER ?? 'http://localhost:8096').replace(/\/+$/, '');
|
||||
const DATEIEN = process.env.HALO_STATIC ?? join(HIER, '..', 'app', 'dist');
|
||||
const ZIEL = (process.env.HALO_SERVER ?? 'http://localhost:8096').replace(/\/+$/, '');
|
||||
const PORT = Number(process.env.PORT ?? 8080);
|
||||
|
||||
const TYPEN = {
|
||||
@@ -128,7 +128,7 @@ createServer((req, res) => {
|
||||
if (req.url?.startsWith('/api')) return weiterreichen(req, res);
|
||||
return ausliefern(req, res);
|
||||
}).listen(PORT, () => {
|
||||
console.log(`Flixy Web läuft auf http://localhost:${PORT}`);
|
||||
console.log(`Halo Web läuft auf http://localhost:${PORT}`);
|
||||
console.log(`Medienserver: ${ZIEL}`);
|
||||
console.log(`Dateien aus: ${DATEIEN}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user