diff --git a/apps/overlay-server/src/bus.ts b/apps/overlay-server/src/bus.ts
index a3590cf..1088ae8 100644
--- a/apps/overlay-server/src/bus.ts
+++ b/apps/overlay-server/src/bus.ts
@@ -32,6 +32,11 @@ export type ChatEvent = {
/** Abzeichen als fertige Bilder, aufgeloest im Hub (#twitch/badges.ts). */
badges: Array<{ url: string; title: string }>;
bits: number;
+ /**
+ * Sendezeit laut Twitch. Fuer die Uhrzeit im Pult: ohne sie stuende nach
+ * dem Neuverbinden an allen Nachrichten aus dem Schnappschuss dieselbe.
+ */
+ ts?: string;
};
export type AlertKind =
diff --git a/apps/overlay-web/README.md b/apps/overlay-web/README.md
index c07a997..7d78367 100644
--- a/apps/overlay-web/README.md
+++ b/apps/overlay-web/README.md
@@ -100,8 +100,20 @@ einen Knopfdruck aus dem Speicher.
| Sounds | Tasten fürs Soundboard. „+ Sound“ lädt MP3, WAV, OGG, WebM oder M4A vom Handy hoch (bis 8 MB). „Bearbeiten“: Name, Emoji, Farbe, Lautstärke, Reihenfolge, Löschen, am Handy anhören. „Stopp“ bricht alle Sounds ab. |
| Szenen | OBS-Szenen umschalten, Clip aus dem Wiedergabepuffer speichern, Toneingänge stumm schalten. Braucht den Stream-PC (siehe unten). |
| Stream | Liegestütze zählen, Vorlesen freigeben, „Sofort still“. |
+| Chat | Der Twitch-Chat, **nur lesend**: Abzeichen, Emotes, GIFs, Erstchatter hervorgehoben, Abos, Raids und Belohnungen als eigene Zeile, von Mods Gelöschtes durchgestrichen. |
| Test | Dieselben Testknöpfe wie auf der Übersicht. |
+**Am Tablet** (ab 760 px Breite und 500 px Höhe) steht der Chat fest rechts
+und links der gewählte Reiter, der Chat-Reiter entfällt. Ohne gemerkten
+Reiter geht das Pult dort auf Szenen auf. Quer gehaltene Handys sind zu
+niedrig dafür und behalten die Reiter.
+
+Der Chat hängt am selben Ereignisstrom wie die Browser-Sources und schreibt
+nie. Er braucht kein Bot-Konto und keine weiteren Rechte bei Twitch. Nach
+dem Neuverbinden kommen die letzten 30 Nachrichten aus dem Schnappschuss,
+im Pult stehen höchstens 250. Wer hochscrollt, bleibt dort; „Neue
+Nachrichten“ springt zurück ans Ende.
+
Die Sonne oben rechts hält den Bildschirm an (Wake Lock), solange das Pult
offen ist.
diff --git a/apps/overlay-web/public/manifest.webmanifest b/apps/overlay-web/public/manifest.webmanifest
index cca7fb5..e8ad418 100644
--- a/apps/overlay-web/public/manifest.webmanifest
+++ b/apps/overlay-web/public/manifest.webmanifest
@@ -1,7 +1,7 @@
{
"name": "abdulive Pult",
"short_name": "Pult",
- "description": "Soundboard, Szenen, Liegestütze und Vorlesen für den Stream von abdulive_.",
+ "description": "Soundboard, Szenen, Chat, Liegestütze und Vorlesen für den Stream von abdulive_.",
"lang": "de",
"id": "/pult",
"start_url": "/pult",
@@ -16,6 +16,7 @@
{ "src": "/icons/pult-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
],
"shortcuts": [
- { "name": "Szenen", "url": "/pult#szenen", "icons": [{ "src": "/icons/pult-192.png", "sizes": "192x192" }] }
+ { "name": "Szenen", "url": "/pult#szenen", "icons": [{ "src": "/icons/pult-192.png", "sizes": "192x192" }] },
+ { "name": "Chat", "url": "/pult#chat", "icons": [{ "src": "/icons/pult-192.png", "sizes": "192x192" }] }
]
}
diff --git a/apps/overlay-web/public/pult.css b/apps/overlay-web/public/pult.css
index a0a2139..9232b91 100644
--- a/apps/overlay-web/public/pult.css
+++ b/apps/overlay-web/public/pult.css
@@ -327,11 +327,85 @@ main {
.gruppe .knoepfe { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; }
.gruppe .knopf { min-height: 48px; font-weight: 500; font-size: 14px; padding: 0 10px; }
+/* ------------------------------------------------------------ Chat */
+
+/* Ein fester Kasten zwischen Kopf und Reitern, damit nur die Liste
+ * scrollt und nicht die Seite: wer im Chat hochscrollt, soll den Kopf mit
+ * LIVE-Anzeige und Verbindungspunkt weiter sehen. */
+.tab.chat.an { display: flex; }
+.chat {
+ position: fixed; z-index: 10; left: 0; right: 0;
+ top: calc(var(--kopf-h) + var(--oben));
+ bottom: calc(var(--reiter-h) + var(--unten));
+ flex-direction: column;
+ background: var(--grund);
+}
+.chat-kopf {
+ display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
+ padding: 14px 16px 10px; border-bottom: 1px solid var(--linie);
+}
+.chat-kopf .abschnitt, .chat-kopf .klein { margin: 0; }
+/* Wenige Nachrichten stehen unten wie in jedem Chat, nicht oben unter dem
+ * Kopf. margin-top: auto statt justify-content: flex-end - mit letzterem
+ * laesst sich in manchen Browsern nicht mehr nach oben scrollen. */
+.chat-liste {
+ display: flex; flex-direction: column;
+ flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
+ padding: 8px 10px 14px; font-size: 15px; line-height: 1.42;
+}
+.chat-leer { position: absolute; top: 50%; left: 0; right: 0; text-align: center;
+ color: var(--leise); font-size: 14px; margin: 0; }
+
+.cz { flex: none; padding: 5px 8px; border-radius: 8px; overflow-wrap: anywhere; }
+.cz:first-child { margin-top: auto; }
+.cz .zeit { color: var(--leise); font-size: 12px; font-variant-numeric: tabular-nums; margin-right: 6px; }
+.cz .abz { height: 18px; width: auto; vertical-align: -3px; margin-right: 3px; }
+.cz .wer { font-weight: 700; }
+.cz .emote { height: 28px; width: auto; vertical-align: -8px; }
+.cz .gif { display: block; max-width: min(100%, 220px); max-height: 140px; border-radius: 6px; margin: 4px 0 2px; }
+.cz.erstmals { background: rgba(74, 222, 128, .1); box-shadow: inset 3px 0 0 var(--gruen); }
+.cz.erstmals::before {
+ content: "Zum ersten Mal hier"; display: block; margin-bottom: 1px;
+ font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--gruen);
+}
+.cz.bits { background: rgba(255, 197, 61, .1); }
+.cz.weg { opacity: .45; }
+.cz.weg .text { text-decoration: line-through; }
+.cz.system {
+ margin: 6px 0; font-size: 13px; color: var(--schrift);
+ background: rgba(169, 112, 255, .1); border: 1px solid rgba(169, 112, 255, .35);
+}
+
+.chat-runter {
+ position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
+ padding: 9px 16px; border-radius: 999px; cursor: pointer;
+ font: 600 14px/1 var(--font); color: #fff;
+ background: var(--lila-tief); border: 1px solid var(--lila);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
+}
+
+/* Ab Tablet-Groesse: Chat fest rechts, links der gewaehlte Reiter. So
+ * stehen Szenen und Chat auf einem Bildschirm. Die Hoehe haelt quer
+ * gehaltene Handys heraus, dort bliebe fuer beides zu wenig. Muss in
+ * pult.js (`geteilt`) gleich lauten. */
+@media (min-width: 760px) and (min-height: 500px) {
+ :root { --chat-b: clamp(300px, 36vw, 440px); }
+ main { max-width: none; margin: 0; padding-right: calc(var(--chat-b) + 16px); }
+ .tab.chat {
+ display: flex; left: auto; width: var(--chat-b); bottom: 0;
+ padding-bottom: var(--unten); border-left: 1px solid var(--linie); animation: none;
+ }
+ .chat-runter { bottom: calc(14px + var(--unten)); }
+ .reiter { right: var(--chat-b); }
+ #nav-chat { display: none; }
+ .meldung { left: calc((100% - var(--chat-b)) / 2); }
+}
+
/* ------------------------------------------------------------ Reiter */
.reiter {
position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
- display: grid; grid-template-columns: repeat(4, 1fr);
+ display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
height: calc(var(--reiter-h) + var(--unten));
padding-bottom: var(--unten);
background: rgba(18, 14, 29, .94);
diff --git a/apps/overlay-web/public/pult.html b/apps/overlay-web/public/pult.html
index 7b1ce26..4265d70 100644
--- a/apps/overlay-web/public/pult.html
+++ b/apps/overlay-web/public/pult.html
@@ -131,6 +131,18 @@
Übersicht und OBS-Adressen · Einrichtung · Abmelden
+
+
+
+
+
Chat
+ nur lesen
+
+
+ Noch keine Nachrichten.
+
+
@@ -150,6 +162,11 @@
Stream
+