import { useEffect, useState } from 'react' import { useStore } from '@nanostores/react' import { currentSpace, currentSpaceId, createNote, editingNoteId, clearSelection, deleteSelectedNotes, selectNotesInRect, refreshExistingSpaces } from '../store' import { Note } from './Note' import { NavigationConsole } from './NavigationConsole' import { WelcomeModal, shouldShowWelcome } from './WelcomeModal' export function Space() { const $currentSpace = useStore(currentSpace) const $currentSpaceId = useStore(currentSpaceId) const $editingNoteId = useStore(editingNoteId) const [isSelecting, setIsSelecting] = useState(false) const [selectionStart, setSelectionStart] = useState({ x: 0, y: 0 }) const [selectionEnd, setSelectionEnd] = useState({ x: 0, y: 0 }) const [justFinishedSelection, setJustFinishedSelection] = useState(false) const [showNavigationConsole, setShowNavigationConsole] = useState(false) const [showWelcomeModal, setShowWelcomeModal] = useState(false) // Show welcome modal on first load and refresh existing spaces useEffect(() => { if (shouldShowWelcome()) { setShowWelcomeModal(true) } // Initialize existing spaces list refreshExistingSpaces() }, []) // Click in empty space - clear selection (but not if we just finished a rectangle selection) const handleClick = (e: React.MouseEvent) => { if (justFinishedSelection) { setJustFinishedSelection(false) return } clearSelection() } // Double-click in empty space - create note const handleDoubleClick = async (e: React.MouseEvent) => { const rect = e.currentTarget.getBoundingClientRect() const x = e.clientX - rect.left const y = e.clientY - rect.top const noteId = await createNote(x, y) if (noteId) { editingNoteId.set(noteId) } } // Drag in empty space - rectangle selection const handleMouseDown = (e: React.MouseEvent) => { if (e.detail === 2) return // Ignore double-click const rect = e.currentTarget.getBoundingClientRect() const x = e.clientX - rect.left const y = e.clientY - rect.top setIsSelecting(true) setSelectionStart({ x, y }) setSelectionEnd({ x, y }) } const handleMouseMove = (e: MouseEvent) => { if (!isSelecting) return const spaceElement = document.querySelector('[data-space-canvas]') as HTMLElement if (!spaceElement) return const rect = spaceElement.getBoundingClientRect() const x = e.clientX - rect.left const y = e.clientY - rect.top setSelectionEnd({ x, y }) } const handleMouseUp = (e: MouseEvent) => { if (!isSelecting) return setIsSelecting(false) // Get final mouse position directly from event const spaceElement = document.querySelector('[data-space-canvas]') as HTMLElement if (!spaceElement) return const rect = spaceElement.getBoundingClientRect() const finalX = e.clientX - rect.left const finalY = e.clientY - rect.top // Calculate selection rectangle using actual coordinates const minX = Math.min(selectionStart.x, finalX) const maxX = Math.max(selectionStart.x, finalX) const minY = Math.min(selectionStart.y, finalY) const maxY = Math.max(selectionStart.y, finalY) const width = Math.abs(maxX - minX) const height = Math.abs(maxY - minY) // Only select if rectangle is big enough if (width > 5 || height > 5) { selectNotesInRect(minX, minY, maxX, maxY) setJustFinishedSelection(true) } else { clearSelection() } } useEffect(() => { if (isSelecting) { document.addEventListener('mousemove', handleMouseMove) document.addEventListener('mouseup', handleMouseUp) return () => { document.removeEventListener('mousemove', handleMouseMove) document.removeEventListener('mouseup', handleMouseUp) } } }, [isSelecting, selectionStart]) // Keyboard shortcuts useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Don't handle shortcuts if editing a note, navigation console or welcome modal is open if ($editingNoteId || showNavigationConsole || showWelcomeModal) return if (e.key === ':' && !e.shiftKey) { e.preventDefault() setShowNavigationConsole(true) } else if (e.key === '?' || (e.key === ':' && e.shiftKey)) { e.preventDefault() setShowWelcomeModal(true) } else if (e.key === 'Delete' || e.key === 'Backspace') { deleteSelectedNotes() } else if (e.key === 'Escape') { clearSelection() } } document.addEventListener('keydown', handleKeyDown) return () => document.removeEventListener('keydown', handleKeyDown) }, [$editingNoteId, showNavigationConsole, showWelcomeModal]) if (!$currentSpace) { return (
Loading...
) } const selectionRect = { left: Math.min(selectionStart.x, selectionEnd.x), top: Math.min(selectionStart.y, selectionEnd.y), width: Math.abs(selectionEnd.x - selectionStart.x), height: Math.abs(selectionEnd.y - selectionStart.y) } return (
Space: {$currentSpaceId}
{$currentSpace.notes.map((note) => ( ))} {isSelecting && (
)} setShowNavigationConsole(false)} /> setShowWelcomeModal(false)} />
) }