diff --git a/desktop/index.html b/desktop/index.html index ed754ec..cb1de11 100644 --- a/desktop/index.html +++ b/desktop/index.html @@ -1,3 +1,4 @@ + @@ -8,7 +9,7 @@ - + @@ -116,8 +117,7 @@
@@ -144,6 +144,10 @@
+ + @@ -268,4 +272,4 @@ - \ No newline at end of file + diff --git a/desktop/styles.css b/desktop/styles.css deleted file mode 100644 index fda1104..0000000 --- a/desktop/styles.css +++ /dev/null @@ -1,1629 +0,0 @@ -:root { - /* ── Backgrounds ─────────────────────────────────────────── */ - --bg-darker: #16171b; - --bg-sidebar: #1e2025; - --bg-main: #23252b; - --bg-secondary: #1a1c21; - --bg-tertiary: #111216; - --bg-modifier-selected: rgba(99, 102, 241, 0.15); - --bg-modifier-hover: rgba(255, 255, 255, 0.04); - --bg-input: #2a2d35; - - /* ── Text ────────────────────────────────────────────────── */ - --text-normal: #c9cdd4; - --text-muted: #7c818a; - --text-strong: #eef0f4; - --text-link: #818cf8; - - /* ── Accents ─────────────────────────────────────────────── */ - --brand: #6366f1; - --brand-hover: #4f46e5; - --brand-gradient: linear-gradient(135deg, #6366f1, #a855f7); - --brand-glow: rgba(99, 102, 241, 0.35); - --green: #22c55e; - --danger: #ef4444; - --yellow: #f59e0b; - - /* ── Misc ────────────────────────────────────────────────── */ - --font-main: "Inter", "Noto Sans", "Helvetica Neue", Helvetica, Arial, - sans-serif; - --radius-sm: 6px; - --radius-md: 10px; - --radius-lg: 14px; - --radius-xl: 20px; - --transition: 200ms cubic-bezier(0.4, 0, 0.2, 1); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Reset & Base */ -/* ═══════════════════════════════════════════════════════════ */ -* { - box-sizing: border-box; -} - -body { - margin: 0; - height: 100vh; - height: 100dvh; - background: var(--bg-darker); - color: var(--text-normal); - font-family: var(--font-main); - overflow: hidden; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -/* ── Scrollbars ────────────────────────────────────────────── */ -::-webkit-scrollbar { - width: 6px; - height: 6px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.08); - border-radius: 100px; -} - -::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.14); -} - -/* ── Base Elements ─────────────────────────────────────────── */ -button { - border: 0; - cursor: pointer; - transition: all var(--transition); - background: none; - color: inherit; - font: inherit; - padding: 0; -} - -input, -select { - border: 0; - outline: none; - background: var(--bg-input); - color: var(--text-normal); - font: inherit; -} - -.hidden { - display: none !important; -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Auth Screen */ -/* ═══════════════════════════════════════════════════════════ */ -.auth-screen { - display: grid; - place-items: center; - height: 100vh; - /* Subtle animated gradient background */ - background: linear-gradient(135deg, #0f0c29, #16171b 40%, #1a1c2e 70%, #0f0c29); - background-size: 400% 400%; - animation: gradientShift 12s ease infinite; -} - -@keyframes gradientShift { - - 0%, - 100% { - background-position: 0% 50%; - } - - 50% { - background-position: 100% 50%; - } -} - -.auth-card { - width: min(440px, 92vw); - background: rgba(30, 32, 37, 0.75); - backdrop-filter: blur(24px) saturate(1.4); - -webkit-backdrop-filter: blur(24px) saturate(1.4); - border: 1px solid rgba(255, 255, 255, 0.06); - border-radius: var(--radius-xl); - padding: 40px 36px; - box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5), 0 0 80px rgba(99, 102, 241, 0.08); - text-align: center; -} - -.brand-large { - width: 80px; - height: 80px; - background: var(--brand-gradient); - color: #fff; - border-radius: 22px; - display: grid; - place-items: center; - font-size: 38px; - font-weight: 800; - margin: 0 auto 24px; - box-shadow: 0 4px 24px var(--brand-glow); - transition: transform var(--transition), box-shadow var(--transition); -} - -.brand-large:hover { - transform: scale(1.05); - box-shadow: 0 6px 32px var(--brand-glow); -} - -.auth-card h1 { - margin: 0 0 8px; - color: var(--text-strong); - font-weight: 800; - font-size: 28px; - letter-spacing: -0.5px; -} - -.auth-card p { - margin: 0 0 28px; - color: var(--text-muted); - font-size: 15px; -} - -#login-btn { - width: 100%; - background: var(--brand-gradient); - color: #fff; - padding: 14px; - border-radius: var(--radius-md); - font-weight: 600; - font-size: 16px; - letter-spacing: 0.2px; - box-shadow: 0 2px 16px var(--brand-glow); - transition: all var(--transition); -} - -#login-btn:hover { - transform: translateY(-1px); - box-shadow: 0 4px 24px var(--brand-glow); - filter: brightness(1.08); -} - -#login-btn:active { - transform: translateY(0); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Main Layout */ -/* ═══════════════════════════════════════════════════════════ */ -.shell { - height: 100vh; - height: 100dvh; - display: grid; - grid-template-columns: 72px 248px 1fr 248px; - background: var(--bg-main); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Server Rail */ -/* ═══════════════════════════════════════════════════════════ */ -.server-rail { - background: var(--bg-darker); - display: flex; - flex-direction: column; - align-items: center; - padding: 12px 0; - gap: 8px; - overflow-y: auto; - scrollbar-width: none; -} - -.server-rail::-webkit-scrollbar { - display: none; -} - -.guild-list { - display: flex; - flex-direction: column; - gap: 8px; - padding: 6px 0; -} - -.brand, -.guild-pill { - width: 48px; - height: 48px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - transition: all var(--transition); - position: relative; - background: var(--bg-sidebar); -} - -.brand { - background: var(--brand-gradient); - color: #fff; - border-radius: 16px; - margin-bottom: 2px; - box-shadow: 0 2px 12px var(--brand-glow); -} - -.brand:hover { - border-radius: 16px; - box-shadow: 0 4px 20px var(--brand-glow); -} - -.separator { - width: 32px; - height: 2px; - background: rgba(255, 255, 255, 0.06); - margin-bottom: 2px; - border-radius: 1px; -} - -.guild-pill:hover, -.guild-pill.active { - border-radius: 16px; - background: var(--brand); - color: #fff; - box-shadow: 0 2px 12px var(--brand-glow); -} - -.guild-pill::before { - content: ""; - position: absolute; - left: -12px; - width: 4px; - height: 0; - background: #fff; - border-radius: 0 4px 4px 0; - transition: all var(--transition); -} - -.guild-pill:hover::before { - height: 20px; -} - -.guild-pill.active::before { - height: 40px; -} - -.action-pill { - color: var(--green); -} - -.action-pill:hover { - background: var(--green); - color: #fff; - box-shadow: 0 2px 12px rgba(34, 197, 94, 0.3); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Channel Sidebar */ -/* ═══════════════════════════════════════════════════════════ */ -.channel-sidebar { - background: var(--bg-sidebar); - display: flex; - flex-direction: column; -} - -.sidebar-header { - padding: 0 16px; - height: 48px; - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid rgba(255, 255, 255, 0.04); - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.15); - cursor: pointer; - transition: background-color var(--transition); -} - -.sidebar-header:hover { - background: var(--bg-modifier-hover); -} - -.sidebar-header h2 { - margin: 0; - font-size: 15px; - font-weight: 700; - color: var(--text-strong); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - letter-spacing: -0.2px; -} - -.sidebar-header-actions { - display: flex; - align-items: center; - gap: 8px; -} - -.header-action-btn { - width: 28px; - height: 28px; - border-radius: var(--radius-sm); - display: grid; - place-items: center; - color: var(--text-muted); - transition: all var(--transition); -} - -.header-action-btn:hover { - background: var(--bg-modifier-hover); - color: var(--text-strong); -} - -.copied-badge { - background: var(--brand); - color: #fff; - font-size: 11px; - font-weight: 700; - padding: 2px 6px; - border-radius: 4px; - text-transform: uppercase; - letter-spacing: 0.4px; - animation: fadeInOut 2s ease forwards; - pointer-events: none; - white-space: nowrap; - order: -1; - margin-right: 4px; -} - -@keyframes fadeInOut { - 0% { - opacity: 0; - transform: translateY(4px); - } - - 15% { - opacity: 1; - transform: translateY(0); - } - - 85% { - opacity: 1; - transform: translateY(0); - } - - 100% { - opacity: 0; - transform: translateY(-4px); - } -} - -.header-action-btn i { - width: 16px; - height: 16px; -} - -.sidebar-scroll { - flex: 1; - overflow-y: auto; - padding-top: 12px; -} - -.sidebar-group { - margin-bottom: 20px; -} - -.group-title { - padding: 0 8px 0 2px; - display: flex; - align-items: center; - color: var(--text-muted); - font-size: 11px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.5px; - cursor: pointer; - transition: color var(--transition); -} - -.group-title:hover { - color: var(--text-normal); -} - -.group-toggle { - width: 12px; - height: 12px; - margin-right: 2px; -} - -.group-title span { - flex: 1; -} - -.add-btn { - width: 20px; - height: 20px; - border-radius: var(--radius-sm); - opacity: 0.5; - transition: opacity var(--transition), background-color var(--transition), - transform var(--transition); - display: grid; - place-items: center; -} - -.add-btn:hover { - opacity: 1; - background: var(--bg-modifier-hover); - transform: scale(1.1); -} - -.add-btn i { - width: 16px; - height: 16px; -} - -.channel-list { - padding: 0 8px; - display: flex; - flex-direction: column; - gap: 2px; -} - -.channel-row { - display: flex; - align-items: center; - padding: 7px 10px; - border-radius: var(--radius-sm); - color: var(--text-muted); - font-weight: 500; - gap: 8px; - transition: all var(--transition); - border-left: 3px solid transparent; -} - -.channel-row:hover { - background: var(--bg-modifier-hover); - color: var(--text-normal); -} - -.channel-row.active { - background: var(--bg-modifier-selected); - color: var(--text-strong); - border-left-color: var(--brand); -} - -.channel-row i { - width: 20px; - height: 20px; - opacity: 0.5; -} - -.channel-row.active i { - opacity: 0.9; -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Sidebar Footer */ -/* ═══════════════════════════════════════════════════════════ */ -.sidebar-footer { - background: var(--bg-secondary); - padding: 0; -} - -.user-panel { - padding: 8px 10px; - display: flex; - align-items: center; - gap: 10px; - height: 52px; - transition: background-color var(--transition); - border-radius: var(--radius-sm); - margin: 2px; -} - -.user-panel:hover { - background: var(--bg-modifier-hover); -} - -.avatar-wrapper { - position: relative; -} - -.avatar { - width: 32px; - height: 32px; - border-radius: 50%; - background: var(--brand-gradient); - color: #fff; - display: grid; - place-items: center; - font-weight: 700; - font-size: 13px; -} - -.status-dot { - position: absolute; - bottom: -2px; - right: -2px; - width: 14px; - height: 14px; - border-radius: 50%; - border: 3px solid var(--bg-secondary); -} - -.status-dot.online { - background: var(--green); - box-shadow: 0 0 6px rgba(34, 197, 94, 0.4); -} - -.status-dot.idle { - background: var(--yellow); - box-shadow: 0 0 6px rgba(245, 158, 11, 0.4); -} - -.voice-muted-icon { - width: 14px; - height: 14px; - color: var(--danger); - margin-left: 6px; - vertical-align: text-bottom; -} - -.user-volume-control { - width: 100%; - display: none; - /* Hidden by default */ - align-items: center; - gap: 8px; - padding: 4px 8px 4px 28px; - margin-top: 2px; - box-sizing: border-box; - background: rgba(0, 0, 0, 0.2); - border-radius: 4px; - overflow: hidden; -} - -.user-volume-control.show-volume { - display: flex; - /* Show when active */ -} - -.volume-slider { - -webkit-appearance: none; - appearance: none; - background: var(--bg-darker); - border-radius: 2px; - outline: none; - flex: 1; - height: 4px; - cursor: pointer; - min-width: 0; -} - -.vol-pct { - font-size: 10px; - opacity: 0.6; - min-width: 30px; - text-align: right; -} - -.volume-slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 10px; - height: 10px; - border-radius: 50%; - background: var(--brand); - cursor: pointer; - box-shadow: 0 0 4px var(--brand-glow); -} - -.volume-slider::-moz-range-thumb { - width: 10px; - height: 10px; - border-radius: 50%; - background: var(--brand); - cursor: pointer; - box-shadow: 0 0 4px var(--brand-glow); -} - -.update-notifier { - display: flex; - align-items: center; - margin-right: 8px; -} - -.update-notifier button { - background: none; - border: none; - padding: 4px; - color: var(--brand); - cursor: pointer; - transition: transform 0.2s; - display: flex; - align-items: center; - justify-content: center; -} - -.update-notifier button:hover { - transform: scale(1.2); - color: var(--brand-glow); -} - -.update-notifier button i { - width: 16px; - height: 16px; -} - - -.user-info { - flex: 1; - min-width: 0; -} - -.display-name { - font-size: 14px; - font-weight: 600; - color: var(--text-strong); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.user-status { - font-size: 12px; - color: var(--text-muted); -} - -.user-actions { - display: flex; - gap: 2px; -} - -.user-actions button { - width: 32px; - height: 32px; - border-radius: var(--radius-sm); - display: grid; - place-items: center; - color: var(--text-muted); - transition: all var(--transition); -} - -.user-actions button:hover { - background: var(--bg-modifier-hover); - color: var(--text-strong); -} - -.user-actions i { - width: 20px; - height: 20px; -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Voice Connection */ -/* ═══════════════════════════════════════════════════════════ */ -.voice-connection { - padding: 10px 12px; - background: var(--bg-secondary); - border-bottom: 1px solid rgba(255, 255, 255, 0.03); -} - -.vc-info { - display: flex; - align-items: center; - gap: 8px; -} - -.vc-icon { - color: var(--green); - width: 20px; - filter: drop-shadow(0 0 4px rgba(34, 197, 94, 0.4)); -} - -.vc-text { - flex: 1; - display: flex; - flex-direction: column; -} - -.vc-status { - color: var(--green); - font-size: 14px; - font-weight: 700; -} - -.vc-name { - color: var(--text-muted); - font-size: 12px; -} - -.vc-actions { - display: flex; - gap: 4px; -} - -.vc-actions button { - width: 32px; - height: 32px; - border-radius: var(--radius-sm); - color: var(--text-muted); - display: grid; - place-items: center; - transition: all var(--transition); -} - -.vc-actions button:hover { - background: var(--bg-modifier-hover); - color: var(--text-strong); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Video Grid */ -/* ═══════════════════════════════════════════════════════════ */ -.video-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); - gap: 16px; - padding: 16px; - background: var(--bg-tertiary); - flex: 1; - min-height: 0; - overflow-y: auto; -} - -.video-grid.hidden { - display: none; -} - -.video-item { - position: relative; - aspect-ratio: 16 / 9; - background: #000; - border-radius: var(--radius-lg); - overflow: hidden; - transition: all var(--transition); - cursor: pointer; - z-index: 1; -} - -.video-item:hover { - box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.2); - z-index: 2; -} - -.video-item video { - width: 100%; - height: 100%; - object-fit: contain; - background: #000; - pointer-events: none; -} - -/* Fullscreen Feed Support */ -.video-grid.has-fullscreen { - display: flex; - padding: 0; - overflow: hidden; - position: absolute; - top: 48px; - /* Below chat-header */ - left: 0; - right: 0; - bottom: 0; - z-index: 100; - background: var(--bg-main); -} - -.video-grid.has-fullscreen .video-item { - display: none; -} - -.video-grid.has-fullscreen .video-item.fullscreen { - display: block; - flex: 1; - width: 100%; - height: 100%; - border-radius: 0; - cursor: zoom-out; - z-index: 10; - aspect-ratio: auto; -} - -.video-item .video-label { - position: absolute; - bottom: 8px; - left: 8px; - background: rgba(0, 0, 0, 0.6); - backdrop-filter: blur(8px); - color: #fff; - padding: 3px 10px; - border-radius: 100px; - font-size: 12px; - font-weight: 500; - pointer-events: none; - z-index: 5; -} - -/* Voice Activity */ -.voice-speaking .avatar { - box-shadow: 0 0 0 2px var(--green), 0 0 8px rgba(34, 197, 94, 0.3); -} - -.video-item.speaking { - box-shadow: 0 0 0 3px var(--green), 0 0 16px rgba(34, 197, 94, 0.25); -} - -.video-item.speaking .video-label { - background: var(--green); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Chat Input */ -/* ═══════════════════════════════════════════════════════════ */ -.chat-input-wrapper { - padding: 0 16px 24px; -} - -.message-form { - background: var(--bg-input); - border-radius: var(--radius-md); - padding: 2px 12px; - display: flex; - align-items: center; - gap: 12px; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15); -} - -.input-action-btn { - color: var(--text-muted); - width: 36px; - height: 36px; - display: flex; - align-items: center; - justify-content: center; - border-radius: var(--radius-sm); -} - -.input-action-btn:hover { - color: var(--text-normal); - background: var(--bg-modifier-hover); -} - -.message-form input { - flex: 1; - background: none; - border: 0; - padding: 12px 0; - font-size: 15px; - color: var(--text-normal); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* GIF Picker Modal */ -/* ═══════════════════════════════════════════════════════════ */ -.gif-modal { - width: min(500px, 95vw) !important; - max-height: 600px; - display: flex; - flex-direction: column; - padding: 0 !important; - overflow: hidden; -} - -.gif-modal .modal-header { - padding: 16px 20px; - display: flex; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); -} - -.gif-modal .modal-header h2 { - margin: 0; - font-size: 18px; -} - -.gif-search-wrapper { - padding: 16px 20px; - position: relative; -} - -.gif-search-wrapper input { - width: 100%; - background: var(--bg-darker); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: var(--radius-md); - padding: 12px 14px 12px 40px; - color: #fff; - font-size: 14px; - transition: border-color var(--transition); -} - -.gif-search-wrapper input:focus { - border-color: var(--brand); -} - -.gif-search-wrapper .search-icon { - position: absolute; - left: 32px; - top: 50%; - transform: translateY(-50%); - width: 18px; - height: 18px; - color: var(--text-muted); -} - -.gif-results-grid { - flex: 1; - overflow-y: auto; - padding: 0 20px 20px; - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-auto-rows: min-content; - gap: 8px; - min-height: 300px; -} - -.gif-item { - border-radius: var(--radius-sm); - overflow: hidden; - cursor: pointer; - background: var(--bg-sidebar); - width: 100%; - aspect-ratio: 16 / 9; - transition: transform var(--transition); -} - -.gif-item:hover { - transform: scale(1.02); - filter: brightness(1.1); -} - -.gif-item img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.gif-loading { - grid-column: 1 / -1; - text-align: center; - padding: 40px; - color: var(--text-muted); -} - -/* ── GIF in messages ────────────────────────────────────────── */ -.msg-gif { - margin-top: 8px; - max-width: 100%; - max-height: 300px; - border-radius: var(--radius-md); - overflow: hidden; - display: block; -} - -.msg-gif img { - max-width: 100%; - max-height: 300px; - display: block; - object-fit: contain; -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Sound Board */ -/* ═══════════════════════════════════════════════════════════ */ -.soundboard-container { - padding: 12px; - background: var(--bg-secondary); - border-top: 1px solid rgba(255, 255, 255, 0.03); - display: flex; - flex-direction: column; - gap: 8px; -} - -.soundboard-header { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.4px; - color: var(--text-muted); -} - -.soundboard-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); - gap: 8px; - max-height: 200px; - overflow-y: auto; -} - -.sound-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; - padding: 10px 8px; - background: var(--bg-darker); - border-radius: var(--radius-md); - cursor: pointer; - transition: all var(--transition); - border: 1px solid transparent; - position: relative; -} - -.sound-item:hover { - background: var(--bg-modifier-hover); - transform: translateY(-2px); - border-color: rgba(255, 255, 255, 0.06); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); -} - -.sound-item:active { - transform: scale(0.95); -} - -.sound-delete { - position: absolute; - top: 4px; - right: 4px; - width: 18px; - height: 18px; - border-radius: 50%; - background: rgba(0, 0, 0, 0.6); - color: var(--text-muted); - display: grid; - place-items: center; - opacity: 0; - transition: all var(--transition); - z-index: 2; -} - -.sound-delete:hover { - background: var(--danger); - color: #fff; - transform: scale(1.1); -} - -.sound-item:hover .sound-delete { - opacity: 1; -} - -.sound-icon { - font-size: 24px; - display: block; - object-fit: contain; -} - -.sound-name { - font-size: 11px; - text-align: center; - color: var(--text-normal); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; -} - -.btn-add-sound { - font-size: 12px; - color: var(--brand); - cursor: pointer; - transition: color var(--transition); -} - -.btn-add-sound:hover { - color: var(--text-link); - text-decoration: underline; -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Chat Pane */ -/* ═══════════════════════════════════════════════════════════ */ -.chat-pane { - position: relative; - display: flex; - flex-direction: column; - background: var(--bg-main); - min-width: 0; - min-height: 0; -} - -.chat-header { - height: 48px; - padding: 0 16px; - display: flex; - align-items: center; - gap: 8px; - border-bottom: 1px solid rgba(255, 255, 255, 0.04); - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.15); -} - -.header-icon { - color: var(--text-muted); - width: 24px; -} - -.chat-header h3 { - margin: 0; - font-size: 16px; - font-weight: 700; - color: var(--text-strong); - letter-spacing: -0.2px; -} - -.message-list { - flex: 1; - overflow-y: scroll; - padding: 16px 0; -} - -.msg { - padding: 4px 16px; - display: flex; - gap: 16px; - margin-top: 1.0625rem; - border-radius: var(--radius-sm); - transition: background-color var(--transition); - border-left: 3px solid transparent; -} - -.msg:hover { - background: rgba(255, 255, 255, 0.015); - border-left-color: rgba(99, 102, 241, 0.3); -} - -.msg-avatar { - width: 40px; - height: 40px; - border-radius: 50%; - background: var(--brand-gradient); - flex-shrink: 0; - display: grid; - place-items: center; - color: #fff; - font-weight: 600; - font-size: 15px; -} - -.msg-content { - flex: 1; - min-width: 0; -} - -.msg-header { - display: flex; - align-items: baseline; - gap: 8px; - margin-bottom: 4px; -} - -.msg-author { - font-weight: 600; - color: var(--text-strong); - cursor: pointer; - font-size: 1rem; - transition: color var(--transition); -} - -.msg-author:hover { - color: var(--text-link); - text-decoration: underline; -} - -.msg-time { - font-size: 11px; - color: var(--text-muted); -} - -.msg-body { - color: var(--text-normal); - line-height: 1.45rem; - white-space: pre-wrap; - word-wrap: break-word; -} - -.msg-grouped { - margin-top: 0; - padding-top: 0; - padding-bottom: 0; -} - -.msg-grouped .msg-avatar, -.msg-grouped .msg-header { - display: none; -} - -.msg-grouped .msg-content { - padding-left: 56px; -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Chat Input */ -/* ═══════════════════════════════════════════════════════════ */ -.chat-input-wrapper { - padding: 0 16px 24px; - padding-bottom: clamp(24px, calc(24px + env(safe-area-inset-bottom)), 40px); -} - -.message-form { - background: var(--bg-input); - border-radius: var(--radius-lg); - padding: 12px 18px; - border: 1px solid rgba(255, 255, 255, 0.04); - transition: border-color var(--transition), box-shadow var(--transition); -} - -.message-form:focus-within { - border-color: rgba(99, 102, 241, 0.4); - box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); -} - -.message-form input { - width: 100%; - background: transparent; - color: var(--text-normal); - font-size: 15px; -} - -.message-form input::placeholder { - color: var(--text-muted); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Member List / Utility Sidebar */ -/* ═══════════════════════════════════════════════════════════ */ -.utility-sidebar { - background: var(--bg-sidebar); - display: flex; - flex-direction: column; -} - -.member-list-wrapper { - flex: 1; - overflow-y: auto; - padding: 12px 8px; -} - -.member-row { - display: flex; - align-items: center; - gap: 12px; - padding: 7px 10px; - border-radius: var(--radius-sm); - color: var(--text-muted); - cursor: pointer; - transition: all var(--transition); -} - -.member-row:hover { - background: var(--bg-modifier-hover); - color: var(--text-normal); -} - -.member-avatar { - width: 32px; - height: 32px; - border-radius: 50%; - background: var(--brand-gradient); - color: #fff; - display: grid; - place-items: center; - font-size: 13px; - font-weight: 600; - flex-shrink: 0; -} - -.member-name { - overflow: hidden; - text-overflow: ellipsis; -} - -/* ── Presence Badges ───────────────────────────────────────── */ -.avatar-wrapper { - position: relative; - display: inline-block; -} - -.status-badge { - position: absolute; - bottom: -2px; - right: -2px; - width: 12px; - height: 12px; - border-radius: 50%; - border: 2px solid var(--bg-sidebar); - background: #747f8d; -} - -.status-badge.online { - background: var(--green); - box-shadow: 0 0 6px rgba(34, 197, 94, 0.4); -} - -.member-avatar, -.msg-avatar { - position: relative; -} - -.status-dot { - position: absolute; - bottom: 0; - right: 0; - width: 10px; - height: 10px; - border-radius: 50%; - border: 2px solid var(--bg-sidebar); - background: #747f8d; -} - -.status-dot.online { - background: var(--green); - box-shadow: 0 0 6px rgba(34, 197, 94, 0.4); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Modals */ -/* ═══════════════════════════════════════════════════════════ */ -.modal-container { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(6px); - -webkit-backdrop-filter: blur(6px); - display: grid; - place-items: center; - z-index: 1000; -} - -.modal { - background: rgba(30, 32, 37, 0.92); - backdrop-filter: blur(20px) saturate(1.4); - -webkit-backdrop-filter: blur(20px) saturate(1.4); - border: 1px solid rgba(255, 255, 255, 0.06); - width: min(460px, 95vw); - border-radius: var(--radius-xl); - padding: 28px; - color: var(--text-normal); - box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5); -} - -.modal h2 { - margin: 0 0 20px; - text-align: center; - color: var(--text-strong); - font-weight: 700; - font-size: 22px; - letter-spacing: -0.3px; -} - -.form-item { - margin-bottom: 20px; -} - -.form-item label { - display: block; - font-size: 11px; - font-weight: 700; - color: var(--text-muted); - margin-bottom: 8px; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.form-item input { - width: 100%; - padding: 11px 14px; - border-radius: var(--radius-md); - background: var(--bg-darker); - border: 1px solid rgba(255, 255, 255, 0.04); - transition: border-color var(--transition), box-shadow var(--transition); -} - -.form-item input:focus { - border-color: rgba(99, 102, 241, 0.4); - box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); -} - -.modal-footer { - margin-top: 24px; - display: flex; - justify-content: flex-end; - gap: 12px; -} - -.cancel-btn { - padding: 10px 20px; - color: var(--text-muted); - font-weight: 500; - border-radius: var(--radius-md); - transition: all var(--transition); -} - -.cancel-btn:hover { - color: var(--text-strong); - background: var(--bg-modifier-hover); -} - -.submit-btn { - background: var(--brand-gradient); - color: #fff; - padding: 10px 24px; - border-radius: var(--radius-md); - font-weight: 600; - box-shadow: 0 2px 12px var(--brand-glow); - transition: all var(--transition); -} - -.submit-btn:hover { - transform: translateY(-1px); - box-shadow: 0 4px 20px var(--brand-glow); - filter: brightness(1.06); -} - -.submit-btn:active { - transform: translateY(0); -} - -/* ── Radio Group (Channel Type) ────────────────────────────── */ -.radio-group { - display: flex; - flex-direction: column; - gap: 8px; -} - -.radio-item { - cursor: pointer; - position: relative; -} - -.radio-item input { - position: absolute; - opacity: 0; -} - -.radio-box { - display: flex; - align-items: center; - gap: 12px; - padding: 12px; - background: var(--bg-modifier-hover); - border-radius: var(--radius-md); - border: 1px solid transparent; - transition: all var(--transition); -} - -.radio-item input:checked+.radio-box { - background: var(--bg-modifier-selected); - border-color: rgba(99, 102, 241, 0.3); - color: var(--text-strong); -} - -.radio-box i { - width: 24px; - height: 24px; - color: var(--text-muted); -} - -.radio-text { - display: flex; - flex-direction: column; -} - -.radio-text strong { - font-size: 16px; -} - -.radio-text span { - font-size: 12px; - color: var(--text-muted); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Mobile Overlay */ -/* ═══════════════════════════════════════════════════════════ */ -.overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - z-index: 90; - opacity: 1; - transition: opacity 0.2s ease; -} - -.overlay.hidden { - opacity: 0; - pointer-events: none; - visibility: hidden; -} - -/* ── Mobile Toggle Buttons ─────────────────────────────────── */ -.mobile-toggle-btn { - display: none; - width: 32px; - height: 32px; - align-items: center; - justify-content: center; - color: var(--text-normal); - background: transparent; - margin-right: 8px; -} - -.mobile-toggle-btn:hover { - background: var(--bg-modifier-hover); - border-radius: var(--radius-sm); -} - -/* ═══════════════════════════════════════════════════════════ */ -/* Responsive */ -/* ═══════════════════════════════════════════════════════════ */ -@media (max-width: 1100px) { - .shell { - grid-template-columns: 72px 248px 1fr; - } - - .utility-sidebar { - position: fixed; - top: 0; - bottom: 0; - right: 0; - width: 248px; - z-index: 100; - box-shadow: -2px 0 20px rgba(0, 0, 0, 0.5); - transform: translateX(100%); - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - } - - .utility-sidebar.active { - transform: translateX(0); - display: flex; - } - - .chat-header .mobile-toggle-btn { - display: flex; - } -} - -@media (max-width: 768px) { - .shell { - grid-template-columns: 1fr; - } - - .server-rail, - .channel-sidebar { - position: fixed; - top: 0; - bottom: 0; - z-index: 100; - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - } - - .server-rail { - left: 0; - width: 72px; - transform: translateX(-100%); - } - - .channel-sidebar { - left: 72px; - width: 248px; - transform: translateX(-320px); - box-shadow: 2px 0 20px rgba(0, 0, 0, 0.5); - } - - .shell.menu-open .server-rail { - transform: translateX(0); - } - - .shell.menu-open .channel-sidebar { - transform: translateX(0); - display: flex; - } - - .chat-header { - padding: 0 8px; - } -} \ No newline at end of file diff --git a/package.json b/package.json index 6967283..f5c75b8 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,12 @@ }, "main": "main.js", "scripts": { - "start": "electron .", - "dev": "electron .", - "dist": "electron-builder --publish never", - "dist:linux": "electron-builder --linux --publish never", - "dist:win": "electron-builder --win --publish never" + "build:html": "node scripts/generate-html.js", + "start": "npm run build:html && electron .", + "dev": "npm run build:html && electron .", + "dist": "npm run build:html && electron-builder --publish never", + "dist:linux": "npm run build:html && electron-builder --linux --publish never", + "dist:win": "npm run build:html && electron-builder --win --publish never" }, "build": { "appId": "me.flegr.chattz", diff --git a/scripts/generate-html.js b/scripts/generate-html.js new file mode 100644 index 0000000..1b3497b --- /dev/null +++ b/scripts/generate-html.js @@ -0,0 +1,53 @@ +const fs = require('fs'); +const path = require('path'); + +const root = path.resolve(__dirname, '..'); +const templatePath = path.join(root, 'shared-html', 'index.template.html'); +const template = fs.readFileSync(templatePath, 'utf8'); + +const variants = { + web: { + styles_href: '/static/styles.css', + app_src: '/static/app.js?v=20260227-shared-core-1', + web_downloads: `
+ Desktop downloads: + Windows + Linux (RPM) +
`, + desktop_updater: '', + outPath: path.join(root, 'static', 'index.html'), + }, + desktop: { + styles_href: '../static/styles.css', + app_src: 'app.js?v=20260227-shared-core-1', + web_downloads: '', + desktop_updater: ``, + outPath: path.join(root, 'desktop', 'index.html'), + }, +}; + +for (const variant of Object.values(variants)) { + let output = template; + for (const [key, value] of Object.entries(variant)) { + if (key === 'outPath') continue; + const token = `{{${key}}}`; + if (value === '') { + output = output.replace(new RegExp(`^[ \\t]*\\{\\{${key}\\}\\}[ \\t]*\\n?`, 'm'), ''); + continue; + } + output = output.replaceAll(token, value); + } + + if (/\{\{[a-z_]+\}\}/i.test(output)) { + throw new Error(`Unresolved template placeholders remain in ${variant.outPath}`); + } + + fs.writeFileSync( + variant.outPath, + `\n${output}`, + 'utf8' + ); +} diff --git a/shared-html/index.template.html b/shared-html/index.template.html new file mode 100644 index 0000000..9780068 --- /dev/null +++ b/shared-html/index.template.html @@ -0,0 +1,272 @@ + + + + + + + Chattz + + + + + + + + + +
+
+
C
+

Chattz

+

Sign in to open your servers.

+ + {{web_downloads}} +

+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/static/index.html b/static/index.html index 6386f77..65093cb 100644 --- a/static/index.html +++ b/static/index.html @@ -1,3 +1,4 @@ + diff --git a/static/shared/app-core.js b/static/shared/app-core.js index 0aa2e4f..f7013a9 100644 --- a/static/shared/app-core.js +++ b/static/shared/app-core.js @@ -1914,25 +1914,27 @@ async function init() { } catch (err) { alert(err.message); } }; - el.mediaUploadBtn.onclick = () => { - if (!state.selectedTextChannelId && !state.selectedDmUserId) { - alert("Select a chat first."); - return; - } - el.mediaFileInput.click(); - }; + if (el.mediaUploadBtn && el.mediaFileInput) { + el.mediaUploadBtn.onclick = () => { + if (!state.selectedTextChannelId && !state.selectedDmUserId) { + alert("Select a chat first."); + return; + } + el.mediaFileInput.click(); + }; - el.mediaFileInput.onchange = async () => { - const file = el.mediaFileInput.files?.[0]; - if (!file) return; - try { - await uploadMediaFile(file); - } catch (err) { - alert(err.message); - } finally { - el.mediaFileInput.value = ""; - } - }; + el.mediaFileInput.onchange = async () => { + const file = el.mediaFileInput.files?.[0]; + if (!file) return; + try { + await uploadMediaFile(file); + } catch (err) { + alert(err.message); + } finally { + el.mediaFileInput.value = ""; + } + }; + } // 4. Voice/Soundboard Controls el.voiceVideoBtn.onclick = toggleVideo;