import { useState, useEffect } from 'react' import { useNavigate } from 'react-router-dom' import { uniqueId } from 'tldraw' import { apiUrl } from '../config' import { generateBoardName } from '../utils/nameGenerator' import { saveRoom } from './storageUtils' import { useAuth } from '../hooks/useAuth' import { AuthModal } from '../components/AuthModal' import { BackupList } from '../components/BackupList' interface SavedRoom { id: string name: string lastVisited: number } interface ColorRmProject { id: string name: string lastMod: number ownerId?: string } export function Lobby() { const navigate = useNavigate() const { user, isAuthenticated, token, logout } = useAuth() const [name, setName] = useState(localStorage.getItem('tldraw_user_name') || '') const [recentRooms, setRecentRooms] = useState([]) const [colorRmProjects, setColorRmProjects] = useState([]) const [isCreating, setIsCreating] = useState(false) const [isAuthModalOpen, setIsAuthModalOpen] = useState(false) const [useBetaSync, setUseBetaSync] = useState(() => { return localStorage.getItem('colorRm_useBetaSync') === 'true' }) useEffect(() => { const stored = localStorage.getItem('tldraw_saved_rooms') if (stored) { try { const parsed = JSON.parse(stored) setRecentRooms(parsed.sort((a: SavedRoom, b: SavedRoom) => b.lastVisited - a.lastVisited)) } catch (e) { console.error("Failed to parse recent rooms", e) } } }, []) useEffect(() => { if (isAuthenticated && token) { fetch(apiUrl('/api/color_rm/registry'), { headers: { 'Authorization': `Bearer ${token}` } }) .then(res => { if (res.ok) return res.json() throw new Error('Failed to fetch registry') }) .then((data: any) => { if (data.projects) { setColorRmProjects(data.projects.sort((a: ColorRmProject, b: ColorRmProject) => b.lastMod - a.lastMod)) } }) .catch(e => console.error("Error fetching ColorRM projects:", e)) } else { setColorRmProjects([]) } }, [isAuthenticated, token, isAuthModalOpen]) const handleCreate = async () => { if (isCreating) return setIsCreating(true) saveName() try { const boardName = await generateBoardName() const newId = uniqueId() // Save to local storage with the generated name immediately saveRoom(newId, boardName) navigate(`/${newId}`) } catch (e) { console.error("Failed to create room", e) setIsCreating(false) } } const handleJoin = (id: string) => { saveName() saveRoom(id) // Updates last visited navigate(`/${id}`) } const saveName = () => { if (name.trim()) { localStorage.setItem('tldraw_user_name', name.trim()) } } // Removed addToRecents as it's replaced by saveRoom from storageUtils return (
{/* Header / Nav */} setIsAuthModalOpen(false)} />

Create, collaborate,
and remove.

A minimalist suite of professional tools for designers and engineers. Real-time whiteboarding and SOTA color extraction.

{/* Display Name Input (Global for the session) */}
setName(e.target.value)} placeholder="Enter your display name..." style={{ background: 'transparent', border: 'none', borderBottom: '1px solid #333', padding: '8px 0', color: '#fff', fontSize: '1.1rem', outline: 'none', flex: 1 }} onFocus={e => e.currentTarget.style.borderColor = '#fff'} onBlur={e => e.currentTarget.style.borderColor = '#333'} />
{/* Tool 1: Whiteboard */}
Multiplayer Core

Whiteboard

Endless canvas for brainstorming and system design. Built on tldraw SDK.

{/* Tool 2: ColorRM Pro */}
Advanced Extraction

ColorRM Pro

Professional PDF/Image sync with SOTA color removal. Collaborative precision.

{/* Beta Sync Toggle */}
{ const newValue = !useBetaSync setUseBetaSync(newValue) localStorage.setItem('colorRm_useBetaSync', String(newValue)) }} > {}} style={{ width: '16px', height: '16px', accentColor: '#8b5cf6', cursor: 'pointer' }} />
Beta Sync {useBetaSync && ON}
Self-hosted sync (no Liveblocks fees)
e.currentTarget.style.background = '#0062d1'} onMouseOut={e => e.currentTarget.style.background = '#0070f3'} > Open ColorRM {useBetaSync && '(Beta)'}
{recentRooms.length > 0 && (

Recent Whiteboards (Local)

{recentRooms.map((room, i) => (
handleJoin(room.id)} style={{ padding: '16px 24px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer', transition: '0.2s', borderBottom: i === recentRooms.length - 1 ? 'none' : '1px solid #333', background: '#000' }} onMouseOver={e => e.currentTarget.style.background = '#111'} onMouseOut={e => e.currentTarget.style.background = '#000'} >
{room.name ? room.name.charAt(0).toUpperCase() : (i + 1)}
{room.name || 'Untitled Board'} {room.id}
{new Date(room.lastVisited).toLocaleDateString()}
))}
)} {/* Color RM Projects (Cloud) */} {colorRmProjects.length > 0 && ( )} {/* Cloud Backups Section */} {isAuthenticated && }
) }