/* Chat view — messages, composer, hover actions */ const renderText = (text, onMention) => { if (!text) return null; const parts = []; const regex = /(@\w+|#[\w-]+|https?:\/\/\S+|`[^`]+`)/g; let last = 0; text.replace(regex, (m, _, idx) => { if (idx > last) parts.push(text.slice(last, idx)); if (m.startsWith('@')) parts.push( onMention?.(m)}>{m}); else if (m.startsWith('#')) parts.push({m}); else if (m.startsWith('http')) parts.push({m}); else if (m.startsWith('`')) parts.push({m.slice(1, -1)}); last = idx + m.length; return m; }); if (last < text.length) parts.push(text.slice(last)); return parts; }; const Message = ({ msg, onOpenProfile, onReact, onOpenActions, onOpenEmoji }) => { const hasAvatar = !msg.continuation; return (
{hasAvatar ? (
onOpenProfile?.(msg.author, e)}> {msg.avatar}
) : (
{msg.time}
)}
{hasAvatar && (
onOpenProfile?.(msg.author, e)}>{msg.author} {msg.time}
)} {msg.text &&
{renderText(msg.text)}
} {msg.image && (
{msg.image}
)} {msg.reactions && msg.reactions.length > 0 && (
{msg.reactions.map((r, i) => ( ))}
)}
); }; const ChatHeader = ({ room, roomsData, onToggleMembers, membersOpen, onOpenSearch, onToggleRail, railCollapsed }) => { const roomDef = findRoom(roomsData, room); const isDM = roomDef?.type === 'dm'; const title = roomDef?.name || room; const topic = isDM ? `direct message with ${roomDef.name}` : 'hi-fi corner for paintings, zines, riso, and whatever else'; return (
{isDM ? (
{roomDef.avatar}
) : ( {roomIcon(roomDef?.type || 'text')} )} {title}
{!isDM &&
{topic}
} {isDM &&
}
{isDM && ( <>
)}
); }; const findRoom = (roomsData, id) => { if (!roomsData) return null; for (const section of roomsData.sections) { const r = section.rooms.find(r => r.id === id); if (r) return r; } return null; }; const Composer = ({ room, onSend, onOpenEmoji }) => { const [text, setText] = React.useState(''); const ref = React.useRef(null); const submit = () => { if (text.trim()) { onSend(text); setText(''); } }; const handleKey = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submit(); } }; const roomName = room?.name || room; const placeholder = typeof roomName === 'string' && roomName.startsWith('dm-') ? `Message…` : `Message #${typeof roomName === 'string' ? roomName.replace('dm-', '') : 'room'}`; return (