feat(ui): voice drawer replaces full-screen call UI
Discord-style bottom drawer for voice instead of navigating away: - "Join Voice" hides the FAB, slides up a persistent bottom bar - Drawer shows: room name, timer, P2P/Relay badge, level meter - Controls: mic, speaker, end call — all in the drawer - Direct call info (identicon, name, P2P badge) shown inline - Lobby stays visible above the drawer at all times - Stats line shows codec/packet/FEC info - Leave voice = drawer slides away, FAB returns Removed: full-screen call-screen, back button, old participant list, old mic/speaker/hangup buttons. All voice interaction happens in the 15% bottom drawer while the lobby stays live. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -238,6 +238,74 @@ body {
|
||||
|
||||
.fab-icon { font-size: 18px; }
|
||||
|
||||
/* ── Voice Drawer (bottom bar) ── */
|
||||
.voice-drawer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--surface);
|
||||
border-top: 1px solid var(--surface2);
|
||||
padding: 0 16px;
|
||||
padding-bottom: env(safe-area-inset-bottom, 8px);
|
||||
z-index: 50;
|
||||
animation: drawerUp 0.25s ease-out;
|
||||
box-shadow: 0 -4px 20px rgba(0,0,0,0.4);
|
||||
}
|
||||
@keyframes drawerUp {
|
||||
from { transform: translateY(100%); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
.voice-drawer-bar {
|
||||
display: flex; flex-direction: column; gap: 6px; padding: 10px 0 6px;
|
||||
}
|
||||
.vd-info {
|
||||
display: flex; align-items: center; gap: 8px; font-size: 13px;
|
||||
}
|
||||
.vd-status-dot {
|
||||
width: 8px; height: 8px; border-radius: 50%; background: var(--green); flex-shrink: 0;
|
||||
}
|
||||
.vd-room { color: var(--green); font-weight: 600; }
|
||||
.vd-timer { color: var(--text-dim); font-family: ui-monospace, monospace; font-size: 12px; }
|
||||
.vd-badge {
|
||||
font-size: 10px; padding: 1px 6px; border-radius: 6px; font-weight: 500;
|
||||
}
|
||||
.vd-badge.direct { background: rgba(74,222,128,0.15); color: var(--green); }
|
||||
.vd-badge.relay { background: rgba(96,165,250,0.15); color: #60a5fa; }
|
||||
.vd-level { height: 3px; background: var(--surface2); border-radius: 2px; overflow: hidden; }
|
||||
.vd-level-fill {
|
||||
height: 100%; width: 0%; background: var(--green); border-radius: 2px; transition: width 0.1s;
|
||||
}
|
||||
.vd-controls {
|
||||
display: flex; align-items: center; justify-content: center; gap: 12px; padding: 4px 0;
|
||||
}
|
||||
.vd-btn {
|
||||
width: 44px; height: 44px; border-radius: 50%; border: none;
|
||||
background: var(--surface2); color: var(--text); font-size: 18px;
|
||||
cursor: pointer; display: flex; align-items: center; justify-content: center;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.vd-btn:hover { background: var(--primary); }
|
||||
.vd-btn.muted { background: var(--red); color: white; }
|
||||
.vd-end { background: var(--red); color: white; }
|
||||
.vd-end:hover { background: #dc2626; }
|
||||
.vd-direct-info {
|
||||
display: flex; align-items: center; gap: 10px; padding: 8px 0 4px;
|
||||
border-top: 1px solid var(--surface2); margin-top: 4px;
|
||||
}
|
||||
.vd-dc-identicon { width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0; }
|
||||
.vd-dc-name { font-size: 13px; font-weight: 600; }
|
||||
.vd-dc-badge {
|
||||
font-size: 10px; padding: 1px 6px; border-radius: 6px;
|
||||
}
|
||||
.vd-dc-badge.direct { background: rgba(74,222,128,0.15); color: var(--green); }
|
||||
.vd-dc-badge.relay { background: rgba(96,165,250,0.15); color: #60a5fa; }
|
||||
.vd-dc-badge.connecting { background: rgba(250,204,21,0.15); color: var(--yellow); }
|
||||
.vd-stats {
|
||||
font-size: 10px; color: var(--text-dim); font-family: ui-monospace, monospace;
|
||||
padding: 2px 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Incoming call banner */
|
||||
.incoming-banner {
|
||||
position: fixed;
|
||||
|
||||
Reference in New Issue
Block a user