/* Sidebar: Spaces Rail + Rooms panel */ const SpacesRail = ({ spaces, activeSpace, onSelect, collapsed }) => { if (collapsed) return null; return ( ); }; const RailAddBtn = () => { const [open, setOpen] = React.useState(false); const ref = React.useRef(null); React.useEffect(() => { if (!open) return; const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; setTimeout(() => document.addEventListener('mousedown', h), 50); return () => document.removeEventListener('mousedown', h); }, [open]); return (
setOpen(v => !v)}> Create · Join · Discover
{open && (
Create
setOpen(false)}> New space ⌘N
setOpen(false)}> Group DM
Join
setOpen(false)}> Paste invite link
setOpen(false)}> Join by space ID
Discover
setOpen(false)}> Browse public rooms
setOpen(false)}> Featured communities
)}
); }; const roomIcon = (type) => { if (type === 'voice') return ; if (type === 'announce') return ; if (type === 'lock') return ; return ; }; const RoomsPanel = ({ space, roomsData, activeRoom, onSelectRoom, user, onOpenProfile, onOpenSettings, call, onLeaveCall, onToggleStreamCollapse, onToggleFullscreen, onToggleMic, onToggleDeaf, onToggleScreen, onToggleCam, onJumpToCall }) => { const [collapsed, setCollapsed] = React.useState({}); const [q, setQ] = React.useState(''); const [menuOpen, setMenuOpen] = React.useState(false); const toggle = (id) => setCollapsed(c => ({ ...c, [id]: !c[id] })); const matchesQ = (name) => !q || name.toLowerCase().includes(q.toLowerCase()); return ( ); }; const MiniCall = ({ call, onLeave, onToggleStreamCollapse, onToggleFullscreen, onToggleMic, onToggleDeaf, onToggleScreen, onToggleCam, onJumpToCall }) => { const [elapsed, setElapsed] = React.useState(0); React.useEffect(() => { const i = setInterval(() => setElapsed(e => e + 1), 1000); return () => clearInterval(i); }, []); const fmt = (s) => { const m = Math.floor(s/60), sec = s%60; return `${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')}`; }; return (
{call.streaming && (
{call.streamer}'s stream
{!call.streamCollapsed && ( <> )}
{!call.streamCollapsed && (
LIVE · 1080p · 60fps
)}
)}
Voice connected
{call.roomName}
{fmt(elapsed)} · {call.participants.length} here
e.stopPropagation()}>
e.stopPropagation()}>
); }; const RoomItem = ({ room, active, onClick }) => { if (room.type === 'dm' || room.type === 'group') { return (
{room.avatar} {room.presence && }
{room.name}
{room.unread > 0 &&
{room.unread}
}
); } return (
{roomIcon(room.type)}
{room.name}
{room.type === 'voice' && room.users > 0 && (
{room.users}
)} {room.unread > 0 &&
{room.unread}
}
); }; const SpaceMenu = ({ onClose }) => { React.useEffect(() => { const h = (e) => onClose(); setTimeout(() => document.addEventListener('click', h, { once: true }), 0); return () => document.removeEventListener('click', h); }, []); return (
Invite people ⌘I
Create room
Create category
Space settings
Notification prefs
Copy space link
Leave space
); }; const UserPanel = ({ user, onOpenProfile, onOpenSettings }) => { const [mic, setMic] = React.useState(true); const [head, setHead] = React.useState(true); return (
{user.avatar}
{user.name}
{user.statusText}
); }; Object.assign(window, { SpacesRail, RoomsPanel, RoomItem, SpaceMenu, UserPanel, MiniCall });