Files
EuerChat/ui/index.html
T

594 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EuerChat - euer.tv</title>
<link rel="stylesheet" href="style.css">
</head>
<body class="dark-theme">
<div id="app" class="app-container">
<!-- Top Header Bar (Optionen) -->
<header class="app-header">
<div class="header-left">
<div class="header-brand">
<img class="header-logo" src="logo.png" alt="">
<span class="header-brand-title">EuerChat</span>
</div>
<!-- Nur sichtbar, wenn ein Lua-Skript (.luac) geladen und aktiv ist -->
<button class="btn-modi-switch hidden" id="btnModiToggle" title="Zwischen normalem Hörer und Radio-Moderator wechseln">
<span class="modi-dot" id="modiDot"></span>
<span id="modiToggleLabel">📻 Zum Modi wechseln</span>
</button>
</div>
<div class="header-right">
<!-- Options Button -->
<button class="btn-icon" id="btnSettingsModal" title="Optionen & Einstellungen">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
<span>Optionen</span>
</button>
</div>
</header>
<!-- Main Content Area -->
<main class="main-layout">
<!-- Chat Box (Expanded full width on the left) -->
<section class="chat-area">
<div class="messages-container" id="messagesContainer">
<!-- Messages populated dynamically -->
</div>
<!-- Chat Input Bar -->
<div class="chat-input-wrapper">
<div class="chat-input-box">
<input type="text" id="chatInput" placeholder="Nachricht an #erde... (Enter zum Senden)" autocomplete="off">
<div class="input-actions">
<button class="btn-emote" id="btnEmotePicker" title="Emotes & GIFs öffnen">
😊
</button>
<button class="btn-send" id="btnSend">
Senden
</button>
</div>
</div>
</div>
<!-- Bottom Bar: Everything related to Chat (Compact Status, Tabs, User identity) -->
<div class="bottom-bar">
<div class="bottom-chat-left">
<div class="connection-status-compact" id="connStatus" title="IRC Verbindungsstatus">
<span class="status-dot disconnected" id="statusDot"></span>
<span class="status-ping hidden" id="statusPing">-- ms</span>
</div>
<button class="btn-connect-header" id="btnHeaderConnect" title="Verbindung herstellen oder trennen">
Verbinden
</button>
</div>
<div class="channel-tabs-container">
<div class="channel-tabs" id="channelList">
<div class="channel-tab active" data-target="#erde">
<span class="chan-prefix">#</span>
<span class="chan-name">erde</span>
<span class="chan-badge unread-badge hidden">0</span>
</div>
<div class="channel-tab" data-target="*server*">
<span class="chan-prefix">ℹ</span>
<span class="chan-name">Server</span>
</div>
</div>
<button class="btn-tab-add" id="btnAddChan" title="Kanal beitreten">+</button>
</div>
<div class="bottom-user-status">
<span class="bottom-user-avatar" id="userAvatar">?</span>
<div class="bottom-user-info">
<span class="bottom-user-nick" id="displayNick">Hoerer</span>
</div>
</div>
</div>
</section>
<!-- Draggable Splitter to manually resize the user list & video player width -->
<div class="sidebar-resizer" id="sidebarResizer" title="Ziehen zum Vergrößern/Verkleinern der Leiste"></div>
<!-- Right Sidebar: User List & Embedded Video Player below it -->
<aside class="sidebar-users">
<div class="sidebar-section-title">
<span>BENUTZER</span>
<span class="user-count" id="userCount">0</span>
</div>
<div class="user-list" id="userList">
<!-- Users populated dynamically -->
</div>
<!-- Media Area: Interpret/Titel drüber, Video Player in der Mitte, Hörer & Grußbox unten -->
<div class="sidebar-media-area" id="sidebarMediaArea">
<!-- Track Info: Interpret / Titel DIREKT ÜBER DEM PLAYER (ohne Cover) -->
<div class="sidebar-track-header" id="sidebarTrackHeader" title="Aktueller Titel & Interpret">
<div class="sidebar-track-info" id="sidebarTrackInfo">
<div class="sidebar-artist" id="sidebarArtist">euer.tv Radio</div>
<div class="sidebar-track" id="sidebarTrack">Lade aktuellen Titel...</div>
</div>
</div>
<!-- Video Player Box -->
<div class="sidebar-video-box" id="sidebarVideoBox">
<div class="sidebar-video-wrapper">
<!-- Video element: MUTED AT START! -->
<video id="streamVideo" playsinline muted autoplay></video>
</div>
<!-- Video Player Controls: Mute, Volume, Quality (1080p, 720p, 360p), Resync -->
<!-- Video Player Controls: Mute, Lautstärke, Hörer, Grußbox, Qualität, Resync in EINER Zeile! -->
<div class="sidebar-player-controls" id="sidebarPlayerControls">
<div class="player-ctrls-left">
<button class="btn-video-mute is-muted" id="btnStreamMute" title="Ton stummschalten oder aktivieren">
<span id="streamMuteIcon">🔇</span>
</button>
<input type="range" id="streamVolSlider" min="0" max="1" step="0.05" value="0.1" class="mini-volume-slider" title="Lautstärke">
</div>
<div class="sidebar-listeners-display twitch-viewer-badge" id="sidebarListenersDisplay" title="Aktuelle Zuhörer*innen">
<svg class="twitch-viewer-icon" viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="10" cy="6.2" r="3.2"></circle>
<path d="M4.5 16.5c0-2.8 2.5-4.8 5.5-4.8s5.5 2 5.5 4.8"></path>
</svg>
<span class="stat-val" id="statTotalListeners">0</span>
</div>
<button class="btn-grussbox-compact" id="btnOpenGrussbox" title="Grußbox öffnen & Nachricht direkt ins Studio senden">
✉ Grußbox
</button>
</div>
<div class="player-ctrls-right">
<!-- Quality selector: 1080p, 720p, 360p -->
<select id="videoQualitySelect" class="video-quality-select" title="Stream-Qualität wählen">
<option value="1080p" selected>1080p</option>
<option value="720p">720p</option>
<option value="360p">360p</option>
</select>
<button class="btn-video-resync" id="btnResyncStream" title="Livestream neu synchronisieren">🔄</button>
</div>
</div>
</div>
</div>
</aside>
</main>
<!-- Emote Picker Popover -->
<div class="emote-picker hidden" id="emotePicker">
<div class="emote-picker-header">
<input type="text" id="emoteSearch" placeholder="Emote suchen (z.B. CATJAM, pepe, :) )">
<button class="btn-close-mini" id="btnCloseEmote">✕</button>
</div>
<div class="emote-picker-tabs">
<button class="emote-tab active" data-tab="all">Alle</button>
<button class="emote-tab" data-tab="gifs">GIFs</button>
<button class="emote-tab" data-tab="smilies">Smilies</button>
</div>
<div class="emote-grid" id="emoteGrid">
<!-- Emotes rendered here -->
</div>
</div>
<!-- Complete Unified Options Modal -->
<div class="modal-overlay hidden" id="settingsModal">
<div class="modal-card modal-card-wide">
<div class="modal-header">
<h2>Optionen & Einstellungen</h2>
<button class="modal-close" id="btnCloseSettingsModal">✕</button>
</div>
<div class="modal-tabs">
<button class="tab-btn active" data-tab-target="secDisplay">Darstellung</button>
<button class="tab-btn" data-tab-target="secServer">Server & ZNC</button>
<button class="tab-btn hidden" data-tab-target="secRadio" id="tabBtnRadio">Radio & Modi</button>
<button class="tab-btn" data-tab-target="secLua" id="tabBtnLua">Lua Addons</button>
<button class="tab-btn" data-tab-target="secUpdate" id="tabBtnUpdate">Updates</button>
</div>
<div class="modal-body settings-body">
<!-- 1. Display Section -->
<div class="settings-section-pane active" id="secDisplay">
<div class="settings-grid">
<div class="settings-col">
<h3 class="settings-section-title">Schrift & Text</h3>
<div class="form-group">
<label for="optFontFamily">Schriftart (System & Windows)</label>
<select id="optFontFamily" class="form-select">
<option value="system">Standard Sans (Segoe UI, System)</option>
<option value="monospace">Monospace / Konsole (Consolas, Courier)</option>
<option value="segoe">Segoe UI</option>
<option value="arial">Arial</option>
<option value="calibri">Calibri</option>
<option value="verdana">Verdana</option>
<option value="comic">Comic Sans MS</option>
<option value="trebuchet">Trebuchet MS</option>
</select>
</div>
<div class="form-group" style="margin-top: 4px;">
<label for="optFontCustom" style="font-size:11.5px;color:var(--text-dim);">Eigene installierte Schriftart manuell eingeben:</label>
<input type="text" id="optFontCustom" class="form-input" placeholder="z. B. Cascadia Code, Bahnschrift, Fira Code..." style="font-size:12px;padding:5px 8px;background:var(--bg-card);border:1px solid var(--border-color);border-radius:var(--radius-sm);color:#fff;width:100%;">
</div>
<div class="form-group">
<div class="label-with-value">
<label for="optFontSize">Schriftgröße</label>
<span class="range-val-badge" id="valFontSize">14px</span>
</div>
<input type="range" id="optFontSize" min="11" max="22" step="1" value="14" class="form-range">
</div>
<div class="form-group">
<label for="optLineHeight">Zeilenabstand & Dichte</label>
<select id="optLineHeight" class="form-select">
<option value="compact">Kompakt (eng wie mIRC / HexChat)</option>
<option value="normal" selected>Normal (ausgewogen)</option>
<option value="cozy">Komfortabel (mehr Abstand)</option>
</select>
</div>
<div class="form-group">
<label for="optTimestamp">Zeitstempel-Format</label>
<select id="optTimestamp" class="form-select">
<option value="hm" selected>HH:mm (z. B. 21:13)</option>
<option value="hms">HH:mm:ss (z. B. 21:13:45)</option>
<option value="none">Ausblenden</option>
</select>
</div>
<div class="form-group">
<label for="optUiScale">Skalierung der Oberfläche</label>
<select id="optUiScale" class="form-select">
<option value="75">75 %</option>
<option value="90">90 %</option>
<option value="100" selected>100 % (Standard)</option>
<option value="110">110 %</option>
<option value="125">125 %</option>
<option value="150">150 %</option>
<option value="175">175 %</option>
<option value="200">200 %</option>
</select>
</div>
</div>
<div class="settings-col">
<h3 class="settings-section-title">Chat & Emotes</h3>
<div class="form-group">
<label for="optEmoteSize">Emote-Größe</label>
<select id="optEmoteSize" class="form-select">
<option value="20">Klein (20px)</option>
<option value="26" selected>Normal (26px)</option>
<option value="34">Groß (34px)</option>
<option value="42">Riesig (42px)</option>
</select>
</div>
<div class="settings-checkboxes">
<label class="form-checkbox">
<input type="checkbox" id="optIrcColors" checked>
<span>mIRC Farbcodes & Formatierung anzeigen</span>
</label>
<label class="form-checkbox">
<input type="checkbox" id="optEmotes" checked>
<span>Emotes & animierte GIFs ersetzen</span>
</label>
<label class="form-checkbox">
<input type="checkbox" id="optHighlight" checked>
<span>Eigenen Nicknamen hervorheben</span>
</label>
<label class="form-checkbox">
<input type="checkbox" id="optAutoScroll" checked>
<span>Automatisch nach unten scrollen</span>
</label>
</div>
<div class="settings-checkboxes" style="margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border-color);">
<span class="preview-title" style="display:block; margin-bottom: 6px; font-size:10.5px; color:var(--text-dim); font-weight:700;">SICHTBARKEIT & LAYOUT</span>
<label class="form-checkbox">
<input type="checkbox" id="optShowMediaArea" checked>
<span>Livestream-Player, Hörer & Grußbox anzeigen</span>
</label>
</div>
</div>
</div>
<!-- Live Preview -->
<div class="settings-preview-wrapper">
<span class="preview-title">VORSCHAU</span>
<div class="settings-preview" id="settingsPreview">
<div class="chat-msg">
<span class="msg-time">21:13</span>
<span class="msg-nick" style="color:#48dbfb;">DJ_Radio</span>
<span class="msg-text">Hallo zusammen! Viel Spaß mit der Musik <img class="chat-emote" src="https://chat.euer.tv/static/emocache/catjam.gif" alt="CATJAM"></span>
</div>
<div class="chat-msg action-msg">
<span class="msg-time">21:14</span>
<span class="msg-text"><span class="msg-action-star">*</span> <span class="msg-nick" style="color:#1dd1a1;">Kellnerin</span> <span style="color:#facc15;">Jetzt läuft:</span> <span style="color:#38bdf8;">Usher Feat. Pitbull</span> - <span style="color:#a855f7;">Dj Got Us Fallin' In Love</span></span>
</div>
</div>
</div>
</div>
<!-- 2. Server & ZNC Section -->
<div class="settings-section-pane hidden" id="secServer">
<div class="server-type-selector">
<button class="server-type-btn active" id="btnTypeDirect">Direkt (irc.euer.tv)</button>
<button class="server-type-btn" id="btnTypeZnc">ZNC Bouncer</button>
</div>
<!-- Direct Form -->
<form id="formDirect" class="form-sub-section active">
<p class="section-desc">Verbindet direkt mit dem Radio-Chat <strong>irc.euer.tv</strong> (Port 6697, SSL-verschlüsselt) im Raum <strong>#erde</strong>.</p>
<div class="form-group">
<label for="directNick">Name (Nickname)</label>
<input type="text" id="directNick" placeholder="Dein Name im Chat" maxlength="30" autocomplete="off" required>
</div>
<div class="form-group">
<label for="directPass">Passwort (optional, nur mit registriertem Account)</label>
<input type="password" id="directPass" placeholder="Leer lassen, wenn dein Name nicht registriert ist" autocomplete="current-password">
</div>
<div class="form-checkbox">
<input type="checkbox" id="directAutoConnect">
<label for="directAutoConnect">Automatisch beim Start direkt verbinden</label>
</div>
<div class="server-connect-action">
<button type="button" class="btn-success" id="btnSaveDirect" title="Direkt-Einstellungen speichern">💾 Direkt Speichern</button>
<button type="button" class="btn-primary" id="btnConnectNow" title="Mit diesen Daten direkt verbinden">⚡ Jetzt Verbinden</button>
<button type="button" class="btn-secondary" id="btnClearDirect" title="Alle Direkt-Felder leeren">🗑️ Leeren</button>
</div>
<div class="config-save-feedback hidden" id="directSaveFeedback"></div>
</form>
<!-- ZNC Form -->
<form id="formZnc" class="form-sub-section hidden">
<div class="form-row">
<div class="form-group flex-2">
<label>ZNC Host / IP</label>
<input type="text" id="zncHost" placeholder="z. B. meinserver.example.org" required>
</div>
<div class="form-group flex-1">
<label>ZNC Port</label>
<input type="number" id="zncPort" value="8250" placeholder="8250" required>
</div>
</div>
<div class="form-row">
<div class="form-group flex-1">
<label>ZNC Benutzer (z.B. user oder user/network)</label>
<input type="text" id="zncUser" placeholder="z. B. benutzer oder benutzer/netzwerk" required>
</div>
<div class="form-group flex-1">
<label>ZNC Passwort</label>
<input type="password" id="zncPass" placeholder="ZNC Passwort" required>
</div>
</div>
<div class="form-row">
<div class="form-group flex-1">
<label>Nickname</label>
<input type="text" id="zncNick" placeholder="Dein Nickname">
</div>
<div class="form-group flex-1">
<label>Standard-Raum</label>
<input type="text" id="zncChannel" value="#erde">
</div>
</div>
<div class="form-checkbox">
<input type="checkbox" id="zncSsl">
<label for="zncSsl">SSL / TLS Verschlüsselung aktivieren</label>
</div>
<div class="form-checkbox">
<input type="checkbox" id="zncAutoConnect">
<label for="zncAutoConnect">Automatisch beim Start über ZNC verbinden</label>
</div>
<div class="server-connect-action">
<button type="button" class="btn-success" id="btnSaveZnc" title="ZNC-Einstellungen speichern">💾 ZNC Speichern</button>
<button type="button" class="btn-primary" id="btnConnectZncNow" title="Über ZNC verbinden und als Standard speichern">⚡ Über ZNC Verbinden</button>
<button type="button" class="btn-secondary" id="btnClearZnc" title="Alle ZNC-Felder leeren">🗑️ Leeren</button>
</div>
<div class="config-save-feedback hidden" id="zncSaveFeedback"></div>
</form>
</div>
<!-- 3. Radio & Modi Section -->
<div class="settings-section-pane hidden" id="secRadio">
<h3 class="settings-section-title">Radio & Sende-Modus (Modi-Verwaltung)</h3>
<p class="section-desc">
Konfiguriere hier deinen normalen Hörer-Nick und deinen Moderatoren-Nick. Ein Klick auf den Modi-Button oben schaltet automatisch um:
</p>
<div class="form-row">
<div class="form-group flex-1">
<label for="cfgNormalNick">Normaler Nickname (Hörer)</label>
<input type="text" id="cfgNormalNick" placeholder="Dein normaler Nickname">
</div>
<div class="form-group flex-1">
<label for="cfgModiNick">Modi-Nickname (Auf Sendung)</label>
<input type="text" id="cfgModiNick" placeholder="z. B. DJ_Name">
</div>
</div>
<div class="form-group">
<label for="cfgInternPass">Passwort für Raum #intern</label>
<input type="password" id="cfgInternPass" placeholder="Passwort (leer lassen, falls kein Schlüssel nötig)" autocomplete="off">
<small class="field-hint">Wird beim Wechsel zum Modi automatisch für <code>JOIN #intern &lt;passwort&gt;</code> verwendet.</small>
</div>
<div class="modi-status-preview-box">
<div class="modi-preview-item">
<span class="preview-badge normal">🎧 Normal</span>
<span>Nur im Raum <strong>#erde</strong> unterwegs.</span>
</div>
<div class="modi-preview-item">
<span class="preview-badge modi">📻 Modi Aktiv</span>
<span>Nick wechselt zu Moderatoren-Name & betritt automatisch <strong>#intern</strong>.</span>
</div>
</div>
</div>
<!-- 4. Lua Addons Section (Clean & Uncluttered by default) -->
<div class="settings-section-pane hidden" id="secLua">
<h3 class="settings-section-title">LUA ERWEITERUNGEN (*.LUA, *.LUAC)</h3>
<p class="section-desc" style="font-size:12.5px;color:var(--text-muted);margin-bottom:12px;">
Hier können optionale Skripte geladen werden.
</p>
<div class="script-upload-box" id="luaPickerBox">
<input type="file" id="luaFileInput" accept=".lua,.luac" style="display:none">
<button type="button" class="btn-secondary" id="btnSelectLua">
📁 Externe .luac / .lua Datei auswählen...
</button>
<span class="selected-file-name" id="luaFileName">Keine Datei ausgewählt</span>
</div>
<div class="loaded-scripts-list" id="loadedScriptsList">
<div class="script-item" id="scriptStatusText" style="color:var(--text-muted);font-size:12.5px;padding:6px;">
○ Inaktiv (Kein Skript geladen)
</div>
</div>
<!-- Radio Moderations-Details: NUR SICHTBAR WENN EIN SKRIPT GELADEN WURDE! -->
<div class="lua-modi-details hidden" id="luaModiDetails" style="margin-top:14px;border-top:1px solid var(--border-color);padding-top:14px;">
<div class="form-checkbox" style="margin-bottom:14px;">
<input type="checkbox" id="chkEnableLua">
<label for="chkEnableLua"><strong>EuerService Moderations-Addon aktiv</strong> (schaltet Modi-Umschalter frei)</label>
</div>
</div>
</div>
<!-- 5. Updates Section -->
<div class="settings-section-pane hidden" id="secUpdate">
<h3 class="settings-section-title">Updates</h3>
<p class="section-desc">Installierte Version: <strong id="updCurrentVersion">–</strong></p>
<div class="form-checkbox">
<input type="checkbox" id="optAutoUpdate" checked>
<label for="optAutoUpdate">Beim Start automatisch nach Updates suchen</label>
</div>
<div class="server-connect-action">
<button type="button" class="btn-primary" id="btnCheckUpdate">Auf Updates prüfen</button>
</div>
<div class="update-status" id="updStatusText"></div>
</div>
</div>
<div class="modal-actions">
<button class="btn-secondary" id="btnResetSettings">Standard</button>
<button class="btn-primary" id="btnSaveSettings">Übernehmen & Schließen</button>
</div>
</div>
</div>
<!-- Grußbox Modal -->
<div class="modal-overlay hidden" id="grussboxModal">
<div class="modal-card modal-card-sm">
<div class="modal-header">
<h2>✉ Grußbox - Radio euer.tv</h2>
<button class="modal-close" id="btnCloseGrussbox">✕</button>
</div>
<div class="modal-body grussbox-body">
<p class="grussbox-desc">Sende deinen Gruß oder Musikwunsch direkt live ins Studio zum aktuellen Moderator!</p>
<div class="form-group">
<label for="gbNameInput">Dein Name / Nickname</label>
<input type="text" id="gbNameInput" class="form-input" placeholder="Dein Name" maxlength="40">
</div>
<div class="form-group">
<label for="gbTextInput">Dein Gruß / Musikwunsch</label>
<textarea id="gbTextInput" class="form-textarea" rows="4" placeholder="Schreib hier deinen Gruß oder Musikwunsch..." maxlength="500"></textarea>
</div>
<div class="grussbox-feedback hidden" id="gbFeedback"></div>
</div>
<div class="modal-actions">
<button class="btn-secondary" id="btnCancelGruss">Abbrechen</button>
<button class="btn-primary btn-submit-gruss" id="btnSubmitGruss">🚀 Gruß senden</button>
</div>
</div>
</div>
<!-- Update Modal -->
<div class="modal-overlay hidden" id="updateModal">
<div class="modal-card modal-card-sm">
<div class="modal-header">
<h2>⬆ Update verfügbar</h2>
<button class="modal-close" id="btnCloseUpdate">✕</button>
</div>
<div class="modal-body update-body">
<p class="update-summary" id="updSummary"></p>
<span class="preview-title">WAS IST NEU?</span>
<div class="update-notes" id="updNotes"></div>
<div class="update-progress hidden" id="updProgress"><div class="update-progress-bar" id="updProgressBar"></div></div>
<div class="update-status" id="updModalStatus"></div>
</div>
<div class="modal-actions">
<button class="btn-secondary" id="btnUpdateLater">Später</button>
<button class="btn-primary" id="btnUpdateInstall">Jetzt installieren</button>
</div>
</div>
</div>
<!-- User Context Menu Popover (Einfacher Klick auf Nicknamen) -->
<div class="user-context-menu hidden" id="userContextMenu">
<div class="ctx-header">
<span class="ctx-prefix" id="ctxUserPrefix"></span>
<span class="ctx-nick" id="ctxUserNick">Nick</span>
</div>
<div class="ctx-list">
<button class="ctx-item" data-action="query">
<span class="ctx-icon">💬</span> Privatnachricht (PN)
</button>
<button class="ctx-item" data-action="whois">
<span class="ctx-icon">ℹ</span> WHOIS Info
</button>
<button class="ctx-item" data-action="grussbox">
<span class="ctx-icon">✉</span> In Grußbox erwähnen
</button>
<button class="ctx-item" data-action="copy">
<span class="ctx-icon">📋</span> Nickname kopieren
</button>
<div class="ctx-divider"></div>
<div class="ctx-subhead">Kanal-Moderation</div>
<button class="ctx-item" data-action="op">
<span class="ctx-icon">⚡</span> OP geben (+o)
</button>
<button class="ctx-item" data-action="deop">
<span class="ctx-icon">⚡</span> OP entziehen (-o)
</button>
<button class="ctx-item" data-action="voice">
<span class="ctx-icon">🎤</span> Voice geben (+v)
</button>
<button class="ctx-item" data-action="devoice">
<span class="ctx-icon">🎤</span> Voice entziehen (-v)
</button>
<div class="ctx-divider"></div>
<button class="ctx-item ctx-danger" data-action="kick">
<span class="ctx-icon">👢</span> Kicken
</button>
<button class="ctx-item ctx-danger" data-action="ban">
<span class="ctx-icon">⛔</span> Kicken & Bannen
</button>
</div>
</div>
</div>
<script src="hls.min.js"></script>
<script src="app.js"></script>
</body>
</html>