import React, { useState, useEffect, useRef } from 'react' import { createRootRoute, Outlet, useNavigate } from '@tanstack/react-router' import { bootstrapTheme } from '../lib/theme' import { createKeyboardController } from '../lib/keyboard' import type { RouteName } from '../lib/keyboard' import { TopBar } from '../shell/TopBar' import { Palette } from '../shell/Palette' import '../styles/tokens.css' import '../styles/primitives.css' import '../styles/shell.css' import '../styles/palette.css' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent(): React.JSX.Element { const [paletteOpen, setPaletteOpen] = useState(false) const navigate = useNavigate() // Ref so keyboard controller callbacks always see current state const paletteOpenRef = useRef(paletteOpen) useEffect(() => { paletteOpenRef.current = paletteOpen }, [paletteOpen]) useEffect(() => { // Bootstrap theme once on mount bootstrapTheme() const controller = createKeyboardController({ go: (route: RouteName) => { const paths: Record = { home: '/', projects: '/projects', stats: '/stats', health: '/health', } void navigate({ to: paths[route] }) }, openPalette: () => setPaletteOpen(true), closePalette: () => setPaletteOpen(false), isPaletteOpen: () => paletteOpenRef.current, cursor: { move: (_d: 1 | -1) => { /* reserved for Phase 5 row cursor */ }, activate: () => { /* reserved for Phase 5 row cursor */ }, }, }) document.addEventListener('keydown', controller.onKeyDown) return () => { document.removeEventListener('keydown', controller.onKeyDown) controller.teardown() } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // navigate is stable; intentional empty-dep mount effect return (
setPaletteOpen(true)} /> setPaletteOpen(false)} />
) }