mirror of
https://github.com/ancsemi/Haven.git
synced 2026-11-06 22:13:34 +00:00
- Voice join/leave audio cues (ascending/descending synth tones) - Talking indicator: username glows with accent pulse when speaking - Multi-stream screen sharing: CSS Grid tile layout, per-user labels - Fix remoteStream variable reference bug in voice.js _createPeer - Message pinning (admin-only): pin/unpin with DB table, panel, 50-pin limit - Enhanced markdown: fenced code blocks, blockquotes, improved inline code - Link previews: server-side OG metadata fetch with 30-min cache, preview cards - CSP updated for preview images - .gitignore: exclude .vs/
469 lines
22 KiB
HTML
469 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||
<meta name="theme-color" content="#1a1a2e">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="mobile-web-app-capable" content="yes">
|
||
<title>Haven</title>
|
||
<link rel="stylesheet" href="/css/style.css">
|
||
<script src="/js/theme-init.js"></script>
|
||
</head>
|
||
<body>
|
||
|
||
<div id="app">
|
||
<div id="app-body">
|
||
|
||
<!-- ─── Server Bar (far left — like Discord) ─────── -->
|
||
<nav class="server-bar" id="server-bar">
|
||
<div class="server-icon home active" id="home-server" title="This Server">
|
||
<span class="server-icon-text">⬡</span>
|
||
<span class="server-status-dot online"></span>
|
||
</div>
|
||
<div class="server-separator"></div>
|
||
<div id="server-list"></div>
|
||
<button class="server-icon add-server" id="add-server-btn" title="Add Server">
|
||
<span class="server-icon-text">+</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<!-- ─── Left Sidebar ────────────────────────────── -->
|
||
<aside class="sidebar">
|
||
<div class="sidebar-header">
|
||
<div class="brand">
|
||
<span class="logo-sm">⬡</span>
|
||
<span class="brand-text">HAVEN</span>
|
||
</div>
|
||
<div class="user-bar">
|
||
<span class="led on" id="connection-led" title="Server connection"></span>
|
||
<span class="current-user" id="current-user"></span>
|
||
<button id="rename-btn" class="icon-btn small" title="Change username">✏️</button>
|
||
<button id="logout-btn" class="icon-btn" title="Logout">⏻</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sidebar-section">
|
||
<h5 class="section-label">Join a Channel</h5>
|
||
<div class="input-row">
|
||
<input type="text" id="channel-code-input"
|
||
placeholder="Enter code..." maxlength="8" spellcheck="false">
|
||
<button id="join-channel-btn" class="btn-sm">Join</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sidebar-section" id="admin-controls" style="display:none">
|
||
<h5 class="section-label">Create Channel</h5>
|
||
<div class="input-row">
|
||
<input type="text" id="new-channel-name"
|
||
placeholder="Channel name..." maxlength="50">
|
||
<button id="create-channel-btn" class="btn-sm btn-accent">+</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sidebar-section channel-section">
|
||
<h5 class="section-label">Channels</h5>
|
||
<div class="channel-list" id="channel-list"></div>
|
||
</div>
|
||
|
||
<!-- Theme Selector -->
|
||
<div class="sidebar-section theme-sidebar-section" style="margin-top:auto">
|
||
<h5 class="section-label">Theme</h5>
|
||
<div class="theme-selector" id="theme-selector">
|
||
<button class="theme-btn active" data-theme="haven" title="Haven"><span class="theme-icon">⬡</span></button>
|
||
<button class="theme-btn" data-theme="discord" title="Discord"><span class="theme-icon">🎮</span></button>
|
||
<button class="theme-btn" data-theme="matrix" title="Matrix"><span class="theme-icon">Ⅿ</span></button>
|
||
<button class="theme-btn" data-theme="tron" title="Tron"><span class="theme-icon">◈</span></button>
|
||
<button class="theme-btn" data-theme="halo" title="HALO"><span class="theme-icon">⌁</span></button>
|
||
<button class="theme-btn" data-theme="lotr" title="LoTR"><span class="theme-icon">⚜</span></button>
|
||
<button class="theme-btn" data-theme="cyberpunk" title="Cyberpunk"><span class="theme-icon">⚡</span></button>
|
||
<button class="theme-btn" data-theme="nord" title="Nord"><span class="theme-icon">❄</span></button>
|
||
<button class="theme-btn" data-theme="dracula" title="Dracula"><span class="theme-icon">🧛</span></button>
|
||
<button class="theme-btn" data-theme="bloodborne" title="Bloodborne"><span class="theme-icon">🩸</span></button>
|
||
<button class="theme-btn" data-theme="ice" title="Ice"><span class="theme-icon">🧊</span></button>
|
||
<button class="theme-btn" data-theme="abyss" title="Abyss"><span class="theme-icon">🌊</span></button>
|
||
</div>
|
||
<button class="btn-game-sidebar" id="play-flappy-btn" title="Play Shippy Container">� Shippy Container</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ─── Main Content ────────────────────────────── -->
|
||
<main class="main">
|
||
<header class="channel-header">
|
||
<button id="mobile-menu-btn" class="mobile-menu-btn" title="Menu" aria-label="Menu">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
|
||
<line x1="3" y1="6" x2="21" y2="6"/>
|
||
<line x1="3" y1="12" x2="21" y2="12"/>
|
||
<line x1="3" y1="18" x2="21" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
<div class="channel-info">
|
||
<h3 id="channel-header-name">Select a channel</h3>
|
||
<span class="channel-code-tag" id="channel-code-display" title="Channel code"></span>
|
||
<button id="copy-code-btn" class="icon-btn small" title="Copy code"
|
||
style="display:none">📋</button>
|
||
<button id="delete-channel-btn" class="icon-btn small danger" title="Delete channel"
|
||
style="display:none">🗑️</button>
|
||
</div>
|
||
<div class="voice-controls">
|
||
<div class="search-container" id="search-container" style="display:none">
|
||
<input type="text" id="search-input" class="search-input" placeholder="Search messages..." maxlength="100">
|
||
<button id="search-close-btn" class="icon-btn small" title="Close search">×</button>
|
||
</div>
|
||
<button id="search-toggle-btn" class="icon-btn small" title="Search messages (Ctrl+F)" style="display:none">🔍</button>
|
||
<button id="pinned-toggle-btn" class="icon-btn small" title="Pinned messages" style="display:none">📌</button>
|
||
<button id="mobile-users-btn" class="mobile-users-btn" title="Members" aria-label="Members">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||
<circle cx="9" cy="7" r="4"/>
|
||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
||
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
||
</svg>
|
||
</button>
|
||
<button id="voice-join-btn" class="btn-voice" style="display:none">
|
||
🎤 Join Voice
|
||
</button>
|
||
<button id="voice-mute-btn" class="btn-voice" style="display:none">
|
||
🔇 Mute
|
||
</button>
|
||
<button id="voice-deafen-btn" class="btn-voice" style="display:none">
|
||
🔇 Deafen
|
||
</button>
|
||
<button id="screen-share-btn" class="btn-voice" style="display:none" title="Share Screen">
|
||
🖥️ Share
|
||
</button>
|
||
<button id="voice-leave-btn" class="btn-voice btn-danger" style="display:none">
|
||
📞 Leave
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Welcome Screen -->
|
||
<div id="no-channel-msg" class="welcome-screen">
|
||
<div class="welcome-content">
|
||
<div class="welcome-icon">⬡</div>
|
||
<h2>Welcome to Haven</h2>
|
||
<p>Join a channel with a code from your friends,<br>
|
||
or create one if you're the admin.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Chat Area -->
|
||
<div id="message-area" class="message-area" style="display:none">
|
||
<!-- Screen Share Viewer (multi-stream) -->
|
||
<div id="screen-share-container" class="screen-share-container" style="display:none">
|
||
<div class="screen-share-header">
|
||
<span id="screen-share-label">🖥️ Streams</span>
|
||
<button id="screen-share-close" class="icon-btn small" title="Minimize">×</button>
|
||
</div>
|
||
<div id="screen-share-grid" class="screen-share-grid"></div>
|
||
</div>
|
||
<div id="search-results-panel" class="search-results-panel" style="display:none">
|
||
<div class="search-results-header">
|
||
<span id="search-results-count">Results</span>
|
||
<button id="search-results-close" class="icon-btn small">×</button>
|
||
</div>
|
||
<div id="search-results-list" class="search-results-list"></div>
|
||
</div>
|
||
<div id="pinned-panel" class="pinned-panel" style="display:none">
|
||
<div class="pinned-panel-header">
|
||
<span id="pinned-count">📌 Pinned Messages</span>
|
||
<button id="pinned-close" class="icon-btn small">×</button>
|
||
</div>
|
||
<div id="pinned-list" class="pinned-list"></div>
|
||
</div>
|
||
<div class="messages" id="messages"></div>
|
||
<div class="typing-indicator" id="typing-indicator"></div>
|
||
<div id="reply-bar" class="reply-bar" style="display:none">
|
||
<span class="reply-bar-text" id="reply-preview-text"></span>
|
||
<button class="reply-bar-close" id="reply-close-btn" title="Cancel reply">×</button>
|
||
</div>
|
||
<div class="message-input-area">
|
||
<button id="upload-btn" class="btn-upload" title="Upload Image">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
||
<path d="M21 15l-5-5L5 21"/>
|
||
</svg>
|
||
</button>
|
||
<button id="emoji-btn" class="btn-emoji" title="Emoji">😀</button>
|
||
<div id="emoji-picker" class="emoji-picker" style="display:none"></div>
|
||
<button id="gif-btn" class="btn-gif" title="Search GIFs">GIF</button>
|
||
<div id="gif-picker" class="gif-picker" style="display:none">
|
||
<div class="gif-picker-header">
|
||
<input type="text" id="gif-search-input" placeholder="Search Tenor..." maxlength="100" autocomplete="off">
|
||
</div>
|
||
<div class="gif-picker-grid" id="gif-grid"></div>
|
||
<div class="gif-picker-footer">Powered by Tenor</div>
|
||
</div>
|
||
<div id="mention-dropdown" class="mention-dropdown" style="display:none"></div>
|
||
<div id="slash-dropdown" class="slash-dropdown" style="display:none"></div>
|
||
<textarea id="message-input" placeholder="Type a message... (/ for commands)"
|
||
rows="1" maxlength="2000"></textarea>
|
||
<button id="send-btn" class="btn-send" title="Send">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<input type="file" id="file-input" accept="image/jpeg,image/png,image/gif,image/webp" style="display:none">
|
||
</div>
|
||
</main>
|
||
|
||
<!-- ─── Right Sidebar ───────────────────────────── -->
|
||
<aside class="right-sidebar" id="right-sidebar">
|
||
<div class="panel">
|
||
<h5 class="panel-title">🔊 Voice</h5>
|
||
<div id="voice-users" class="user-list">
|
||
<p class="muted-text">No one in voice</p>
|
||
</div>
|
||
</div>
|
||
<div class="panel">
|
||
<h5 class="panel-title">👥 Online</h5>
|
||
<div id="online-users" class="user-list">
|
||
<p class="muted-text">Select a channel</p>
|
||
</div>
|
||
</div>
|
||
<div class="panel sidebar-settings-panel">
|
||
<button class="btn-settings-popout" id="open-settings-btn" title="Settings">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||
<circle cx="12" cy="12" r="3"/>
|
||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.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-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||
</svg>
|
||
<span>Settings</span>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- Mobile overlay (click to close sidebars) -->
|
||
<div id="mobile-overlay" class="mobile-overlay"></div>
|
||
</div>
|
||
|
||
<!-- ─── Status Bar (bottom) ───────────────────────── -->
|
||
<div class="status-bar" id="status-bar">
|
||
<div class="status-item">
|
||
<span class="led on" id="status-server-led"></span>
|
||
<span class="label">Server</span>
|
||
<span class="value" id="status-server-text">Connected</span>
|
||
</div>
|
||
<div class="divider"></div>
|
||
<div class="status-item">
|
||
<span class="led off" id="status-voice-led"></span>
|
||
<span class="label">Voice</span>
|
||
<span class="value" id="status-voice-text">Off</span>
|
||
</div>
|
||
<div class="divider"></div>
|
||
<div class="status-item">
|
||
<span class="label">Ping</span>
|
||
<span class="value" id="status-ping">--</span>
|
||
<span class="label">ms</span>
|
||
</div>
|
||
<div class="divider"></div>
|
||
<div class="status-item">
|
||
<span class="label">Channel</span>
|
||
<span class="value" id="status-channel">None</span>
|
||
</div>
|
||
<div class="divider"></div>
|
||
<div class="status-item">
|
||
<span class="label">Online</span>
|
||
<span class="value" id="status-online-count">0</span>
|
||
</div>
|
||
<span class="spacer"></span>
|
||
<div class="status-item">
|
||
<span class="value" id="status-clock"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Hidden audio container for voice streams -->
|
||
<div id="audio-container" style="display:none"></div>
|
||
|
||
<!-- Toast notifications -->
|
||
<div id="toast-container"></div>
|
||
|
||
<!-- Add Server Modal -->
|
||
<div class="modal-overlay" id="add-server-modal" style="display:none">
|
||
<div class="modal">
|
||
<h3>Add a Server</h3>
|
||
<p class="modal-desc">Connect to a friend's Haven server</p>
|
||
<div class="form-group">
|
||
<label>Server Name</label>
|
||
<input type="text" id="server-name-input" placeholder='e.g. Jake's Haven' maxlength="30">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Server Address</label>
|
||
<input type="text" id="server-url-input" placeholder="e.g. https://192.168.1.5:3000">
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="btn-sm" id="cancel-server-btn">Cancel</button>
|
||
<button class="btn-sm btn-accent" id="save-server-btn">Add Server</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Rename Modal -->
|
||
<div class="modal-overlay" id="rename-modal" style="display:none">
|
||
<div class="modal">
|
||
<h3>Change Username</h3>
|
||
<p class="modal-desc">Letters, numbers, and underscores only (3–20 chars)</p>
|
||
<div class="form-group">
|
||
<label>New Username</label>
|
||
<input type="text" id="rename-input" placeholder="New username..." maxlength="20">
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="btn-sm" id="cancel-rename-btn">Cancel</button>
|
||
<button class="btn-sm btn-accent" id="save-rename-btn">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Admin Action Modal (Kick/Ban/Mute) -->
|
||
<div class="modal-overlay" id="admin-action-modal" style="display:none">
|
||
<div class="modal">
|
||
<h3 id="admin-action-title">Moderate User</h3>
|
||
<p class="modal-desc" id="admin-action-desc"></p>
|
||
<div class="form-group" id="admin-reason-group">
|
||
<label>Reason (optional)</label>
|
||
<input type="text" id="admin-action-reason" placeholder="Reason..." maxlength="200">
|
||
</div>
|
||
<div class="form-group" id="admin-duration-group" style="display:none">
|
||
<label>Duration (minutes)</label>
|
||
<input type="number" id="admin-action-duration" value="10" min="1" max="43200" step="1">
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="btn-sm" id="cancel-admin-action-btn">Cancel</button>
|
||
<button class="btn-sm btn-accent btn-danger-fill" id="confirm-admin-action-btn">Confirm</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Settings Popout Modal -->
|
||
<div class="modal-overlay" id="settings-modal" style="display:none">
|
||
<div class="modal modal-settings">
|
||
<div class="settings-header">
|
||
<h3>⚙️ Settings</h3>
|
||
<button class="settings-close-btn" id="close-settings-btn">×</button>
|
||
</div>
|
||
|
||
<!-- Sounds Section -->
|
||
<div class="settings-section">
|
||
<h5 class="settings-section-title">🔔 Sounds</h5>
|
||
<div class="notif-settings">
|
||
<label class="toggle-row">
|
||
<span>Notifications</span>
|
||
<input type="checkbox" id="notif-enabled" checked>
|
||
</label>
|
||
<label class="volume-row">
|
||
<span>Volume</span>
|
||
<input type="range" id="notif-volume" min="0" max="100" value="50" class="slider-sm">
|
||
</label>
|
||
<label class="select-row">
|
||
<span>Messages</span>
|
||
<select id="notif-msg-sound">
|
||
<option value="ping">Ping</option>
|
||
<option value="chime">Chime</option>
|
||
<option value="blip">Blip</option>
|
||
<option value="bell">Bell</option>
|
||
<option value="drop">Drop</option>
|
||
<option value="alert">Alert</option>
|
||
<option value="chord">Chord</option>
|
||
<option value="none">None</option>
|
||
</select>
|
||
</label>
|
||
<div class="notif-divider"></div>
|
||
<label class="volume-row">
|
||
<span>@Mention Vol</span>
|
||
<input type="range" id="notif-mention-volume" min="0" max="100" value="80" class="slider-sm">
|
||
</label>
|
||
<label class="select-row">
|
||
<span>@Mentions</span>
|
||
<select id="notif-mention-sound">
|
||
<option value="bell">Bell</option>
|
||
<option value="alert">Alert</option>
|
||
<option value="chord">Chord</option>
|
||
<option value="ping">Ping</option>
|
||
<option value="chime">Chime</option>
|
||
<option value="blip">Blip</option>
|
||
<option value="drop">Drop</option>
|
||
<option value="none">None</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Admin Section (hidden for non-admins) -->
|
||
<div class="settings-section admin-settings-section" id="admin-mod-panel" style="display:none">
|
||
<h5 class="settings-section-title">🛡️ Admin</h5>
|
||
<div class="admin-settings">
|
||
<label class="select-row">
|
||
<span>Show Members</span>
|
||
<select id="member-visibility-select">
|
||
<option value="online">Online Only</option>
|
||
<option value="all">All Members</option>
|
||
<option value="none">Hidden</option>
|
||
</select>
|
||
</label>
|
||
<button class="btn-sm btn-full" id="view-bans-btn">📋 View Bans</button>
|
||
</div>
|
||
<div class="admin-settings" style="margin-top:10px; padding-top:10px; border-top:1px solid var(--border);">
|
||
<h5 class="settings-section-subtitle">� Whitelist</h5>
|
||
<label class="toggle-row">
|
||
<span>Enabled</span>
|
||
<input type="checkbox" id="whitelist-enabled">
|
||
</label>
|
||
<small class="settings-hint">When enabled, only pre-approved usernames can register</small>
|
||
<div id="whitelist-panel" style="margin-top:8px;">
|
||
<div class="whitelist-add-row">
|
||
<input type="text" id="whitelist-username-input" placeholder="Username..." maxlength="20" class="settings-text-input">
|
||
<button class="btn-sm btn-accent" id="whitelist-add-btn">Add</button>
|
||
</div>
|
||
<div id="whitelist-list" class="whitelist-list">
|
||
<p class="muted-text">Loading...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="admin-settings" style="margin-top:10px; padding-top:10px; border-top:1px solid var(--border);">
|
||
<h5 class="settings-section-subtitle">�🗑️ Auto-Cleanup</h5>
|
||
<label class="toggle-row">
|
||
<span>Enabled</span>
|
||
<input type="checkbox" id="cleanup-enabled">
|
||
</label>
|
||
<label class="select-row">
|
||
<span>Max Age (days)</span>
|
||
<input type="number" id="cleanup-max-age" min="0" max="3650" value="0" class="settings-number-input">
|
||
</label>
|
||
<small class="settings-hint">Delete messages older than N days (0 = off)</small>
|
||
<label class="select-row">
|
||
<span>Max DB Size (MB)</span>
|
||
<input type="number" id="cleanup-max-size" min="0" max="100000" value="0" class="settings-number-input">
|
||
</label>
|
||
<small class="settings-hint">Trim oldest messages when DB exceeds N MB (0 = off)</small>
|
||
<button class="btn-sm btn-full" id="run-cleanup-now-btn" style="margin-top:4px;">🧹 Run Cleanup Now</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Bans List Modal -->
|
||
<div class="modal-overlay" id="bans-modal" style="display:none">
|
||
<div class="modal modal-wide">
|
||
<h3>Banned Users</h3>
|
||
<div id="bans-list" class="bans-list">
|
||
<p class="muted-text">Loading...</p>
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="btn-sm" id="close-bans-btn">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/socket.io/socket.io.js"></script>
|
||
<script src="/js/theme.js"></script>
|
||
<script src="/js/notifications.js"></script>
|
||
<script src="/js/servers.js"></script>
|
||
<script src="/js/voice.js"></script>
|
||
<script src="/js/app.js"></script>
|
||
</body>
|
||
</html>
|