diff --git a/bun.lock b/bun.lock index aa2d464..0bbbd4b 100644 --- a/bun.lock +++ b/bun.lock @@ -15,6 +15,7 @@ "dexie-react-hooks": "^1.1.7", "dompurify": "^3.2.3", "fuse.js": "^7.0.0", + "lucide-react": "^1.17.0", "nanoid": "^5.0.9", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -1026,6 +1027,8 @@ "lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], + "lucide-react": ["lucide-react@1.17.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], diff --git a/package.json b/package.json index 4c34288..befd849 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "dexie-react-hooks": "^1.1.7", "dompurify": "^3.2.3", "fuse.js": "^7.0.0", + "lucide-react": "^1.17.0", "nanoid": "^5.0.9", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/src/app/RootLayout.tsx b/src/app/RootLayout.tsx index b5d011e..0d39c32 100644 --- a/src/app/RootLayout.tsx +++ b/src/app/RootLayout.tsx @@ -1,5 +1,10 @@ import { useEffect, useRef, useState } from 'react'; import { Link, Outlet, useRouterState } from '@tanstack/react-router'; +import { + Crown, LayoutDashboard, UserRound, Swords, Map as MapIcon, Dices, BookOpenText, RadioTower, + LibraryBig, Settings as SettingsIcon, Search, PanelLeftClose, PanelLeftOpen, Sun, Moon, PanelRight, + type LucideIcon, +} from 'lucide-react'; import { useUiStore } from '@/stores/uiStore'; import { CommandPalette } from '@/components/CommandPalette'; import { useActiveCampaign, useCampaigns } from '@/features/campaigns/hooks'; @@ -16,14 +21,22 @@ import { PlayerSessionBadge } from '@/features/play/PlayerConnection'; import { SessionSidebar } from '@/features/play/SessionSidebar'; import { useSessionStore } from '@/stores/sessionStore'; -const NAV = [ - { to: '/', label: 'Campaigns', exact: true }, - { to: '/dashboard', label: 'Dashboard', exact: false }, - { to: '/characters', label: 'Characters', exact: false }, - { to: '/combat', label: 'Combat', exact: false }, - { to: '/dice', label: 'Dice', exact: false }, - { to: '/compendium', label: 'Compendium', exact: false }, -] as const; +type NavItem = { to: string; label: string; icon: LucideIcon; exact: boolean; group: 'top' | 'play' | 'reference' }; +const NAV: NavItem[] = [ + { to: '/', label: 'Campaigns', icon: LibraryBig, exact: true, group: 'top' }, + { to: '/dashboard', label: 'Dashboard', icon: LayoutDashboard, exact: false, group: 'play' }, + { to: '/characters', label: 'Characters', icon: UserRound, exact: false, group: 'play' }, + { to: '/combat', label: 'Combat', icon: Swords, exact: false, group: 'play' }, + { to: '/maps', label: 'Battle Map', icon: MapIcon, exact: false, group: 'play' }, + { to: '/dice', label: 'Dice', icon: Dices, exact: false, group: 'play' }, + { to: '/compendium', label: 'Compendium', icon: BookOpenText, exact: false, group: 'reference' }, + { to: '/play', label: 'Live Session', icon: RadioTower, exact: false, group: 'reference' }, +]; +const GROUPS: { id: NavItem['group']; label: string | null }[] = [ + { id: 'top', label: null }, + { id: 'play', label: 'At the Table' }, + { id: 'reference', label: 'Reference' }, +]; function CampaignSwitcher() { const campaigns = useCampaigns(); @@ -55,7 +68,7 @@ function ThemeToggle() { const toggle = useUiStore((s) => s.toggleTheme); return ( ); } @@ -65,6 +78,8 @@ export function RootLayout() { const [paletteOpen, setPaletteOpen] = useState(false); const sessionDockOpen = useUiStore((s) => s.sessionDockOpen); const setSessionDock = useUiStore((s) => s.setSessionDock); + const railCollapsed = useUiStore((s) => s.railCollapsed); + const toggleRail = useUiStore((s) => s.toggleRail); const sessionConnected = useSessionStore((s) => s.status === 'connected'); const sessionRole = useSessionStore((s) => s.role); const joinIntent = useSessionStore((s) => s.joinIntent); @@ -94,56 +109,120 @@ export function RootLayout() { } }, [joinIntent, sessionRole, setSessionDock]); - return ( -