import { useState, useEffect, useRef } from 'react' import { navigateToSpace, currentSpace } from '../store' import { db } from '../db' import { useStore } from '@nanostores/react' import { generateShareLink, copyToClipboard } from '../utils/shareSpace' interface NavigationConsoleProps { isOpen: boolean onClose: () => void } export function NavigationConsole({ isOpen, onClose }: NavigationConsoleProps) { const [input, setInput] = useState('') const [existingSpaces, setExistingSpaces] = useState([]) const [filteredSpaces, setFilteredSpaces] = useState([]) const [selectedIndex, setSelectedIndex] = useState(0) const [isSharing, setIsSharing] = useState(false) const inputRef = useRef(null) const space = useStore(currentSpace) useEffect(() => { const loadSpaces = async () => { const spaces = await db.getAllSpaceIds() setExistingSpaces(spaces) } if (isOpen) { loadSpaces() } }, [isOpen]) useEffect(() => { if (isOpen && inputRef.current) { inputRef.current.focus() } }, [isOpen]) useEffect(() => { if (input.trim() === '') { setFilteredSpaces(existingSpaces.slice(0, 8)) } else { const filtered = existingSpaces.filter(space => space.toLowerCase().includes(input.toLowerCase()) ).slice(0, 8) if (filtered.length === 0 || !filtered.includes(input)) { filtered.unshift(input) } setFilteredSpaces(filtered) } setSelectedIndex(0) }, [input, existingSpaces]) const handleShare = async () => { if (!space) { return } const shareLink = generateShareLink(space) const success = await copyToClipboard(shareLink) if (success) { setIsSharing(true) setTimeout(() => { onClose() setIsSharing(false) }, 1500) } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Escape') { onClose() } else if (e.key === 'Enter') { e.preventDefault() if (input.toLowerCase() === 'share') { handleShare() return } const targetSpace = filteredSpaces[selectedIndex] || input if (targetSpace.trim()) { navigateToSpace(sanitizeSpaceId(targetSpace.trim())) onClose() } } else if (e.key === 'ArrowDown') { e.preventDefault() setSelectedIndex(prev => (prev + 1) % filteredSpaces.length) } else if (e.key === 'ArrowUp') { e.preventDefault() setSelectedIndex(prev => (prev - 1 + filteredSpaces.length) % filteredSpaces.length) } } const sanitizeSpaceId = (input: string): string => { if (input.startsWith('http://') || input.startsWith('https://')) { try { const url = new URL(input) const path = url.pathname.slice(1) || url.hostname return path.toLowerCase().replace(/[^a-z0-9]/g, '-') } catch { return input.toLowerCase().replace(/[^a-z0-9]/g, '-') } } return input.toLowerCase().replace(/[^a-z0-9]/g, '-') } const handleItemClick = (space: string) => { navigateToSpace(sanitizeSpaceId(space)) onClose() } if (!isOpen) { return null } return ( <>
{isSharing ? (
✓ Link copied to clipboard!
) : ( setInput(e.target.value)} onKeyDown={handleKeyDown} placeholder="Name a space" style={{ width: '100%', background: 'transparent', border: 'none', outline: 'none', color: 'white', fontSize: '14px', fontFamily: 'monospace' }} /> )}
{input.toLowerCase() === 'share' && (
Press Enter to share the current space.
)} {filteredSpaces.length > 0 && input.toLowerCase() !== 'share' && (
{filteredSpaces.map((space, index) => { const isExisting = existingSpaces.includes(space) const isSelected = index === selectedIndex const isUrl = space.startsWith('http://') || space.startsWith('https://') return (
handleItemClick(space)} style={{ padding: '8px 12px', cursor: 'pointer', background: isSelected ? '#333' : 'transparent', color: isExisting ? '#4CAF50' : (isUrl ? '#2196F3' : '#FFA726'), borderLeft: isSelected ? '3px solid white' : '3px solid transparent' }} > {space} {!isExisting && ( (nouveau) )}
) })}
)}
) }