diff --git a/desktop/index.html b/desktop/index.html index cb1de11..ed754ec 100644 --- a/desktop/index.html +++ b/desktop/index.html @@ -1,4 +1,3 @@ - @@ -9,7 +8,7 @@ - + @@ -117,7 +116,8 @@
@@ -144,10 +144,6 @@
- - @@ -272,4 +268,4 @@ - + \ No newline at end of file diff --git a/desktop/styles.css b/desktop/styles.css new file mode 100644 index 0000000..fda1104 --- /dev/null +++ b/desktop/styles.css @@ -0,0 +1,1629 @@ +: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 f5c75b8..6967283 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,11 @@ }, "main": "main.js", "scripts": { - "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" + "start": "electron .", + "dev": "electron .", + "dist": "electron-builder --publish never", + "dist:linux": "electron-builder --linux --publish never", + "dist:win": "electron-builder --win --publish never" }, "build": { "appId": "me.flegr.chattz", diff --git a/scripts/generate-html.js b/scripts/generate-html.js deleted file mode 100644 index 1b3497b..0000000 --- a/scripts/generate-html.js +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 9780068..0000000 --- a/shared-html/index.template.html +++ /dev/null @@ -1,272 +0,0 @@ - - - - - - - Chattz - - - - - - - - - -
-
-
C
-

Chattz

-

Sign in to open your servers.

- - {{web_downloads}} -

-
-
- -
- - - - - - - - - - - - - - - diff --git a/static/index.html b/static/index.html index 65093cb..6386f77 100644 --- a/static/index.html +++ b/static/index.html @@ -1,4 +1,3 @@ - diff --git a/static/shared/app-core.js b/static/shared/app-core.js index f7013a9..0aa2e4f 100644 --- a/static/shared/app-core.js +++ b/static/shared/app-core.js @@ -1914,27 +1914,25 @@ async function init() { } catch (err) { alert(err.message); } }; - if (el.mediaUploadBtn && el.mediaFileInput) { - el.mediaUploadBtn.onclick = () => { - if (!state.selectedTextChannelId && !state.selectedDmUserId) { - alert("Select a chat first."); - return; - } - el.mediaFileInput.click(); - }; + 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;