Replaced all hardcoded colors and JS template literal styling with Mantine's canonical approach using CSS modules and CSS variables. This ensures colors transition programmatically without JS interpolation. - Updated globals.css to use data-mantine-color-scheme selectors - Created CSS modules for all navigation components and chat page - Removed useComputedColorScheme/useMantineTheme hooks where not needed - Fixed body background to properly adapt to light/dark mode - All borders, backgrounds, and colors now use CSS variables - Maintained full theme support across all components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
146 lines
4.1 KiB
TypeScript
146 lines
4.1 KiB
TypeScript
'use client';
|
|
|
|
/**
|
|
* Desktop Sidebar Navigation
|
|
*
|
|
* Vertical sidebar navigation for desktop (≥ 768px).
|
|
* Shows three navigation links: Convo, Edit, Galaxy
|
|
* Highlights the active mode based on app state machine.
|
|
*/
|
|
|
|
import { Stack, NavLink, Box, Title, Group, Divider, Text } from '@mantine/core';
|
|
import { IconMessageCircle, IconEdit, IconChartBubbleFilled } from '@tabler/icons-react';
|
|
import { useSelector } from '@xstate/react';
|
|
import { useAppMachine } from '@/hooks/useAppMachine';
|
|
import { UserMenu } from '@/components/UserMenu';
|
|
import { ThemeToggle } from '@/components/ThemeToggle';
|
|
import styles from './DesktopSidebar.module.css';
|
|
|
|
export function DesktopSidebar() {
|
|
const actor = useAppMachine();
|
|
const state = useSelector(actor, (state) => state);
|
|
const send = actor.send;
|
|
|
|
const handleNavigation = (target: 'convo' | 'edit' | 'galaxy') => {
|
|
console.log('[Desktop Nav] Navigating to:', target);
|
|
|
|
if (target === 'convo') {
|
|
send({ type: 'NAVIGATE_TO_CONVO' });
|
|
} else if (target === 'edit') {
|
|
send({ type: 'NAVIGATE_TO_EDIT' });
|
|
} else if (target === 'galaxy') {
|
|
send({ type: 'NAVIGATE_TO_GALAXY' });
|
|
}
|
|
};
|
|
|
|
const isConvo = state.matches('convo');
|
|
const isEdit = state.matches('edit');
|
|
const isGalaxy = state.matches('galaxy');
|
|
|
|
console.log('[Desktop Nav] Current state:', state.value, {
|
|
isConvo,
|
|
isEdit,
|
|
isGalaxy,
|
|
});
|
|
|
|
return (
|
|
<Box className={styles.sidebar}>
|
|
<Stack gap="xs">
|
|
<Group gap="md" mb="lg" align="center" wrap="nowrap">
|
|
<img
|
|
src="/logo.svg"
|
|
alt="Ponderants logo"
|
|
width={32}
|
|
height={32}
|
|
style={{ flexShrink: 0, display: 'block' }}
|
|
/>
|
|
<Title order={3} style={{ margin: 0 }}>
|
|
Ponderants
|
|
</Title>
|
|
</Group>
|
|
|
|
<NavLink
|
|
label="Convo"
|
|
leftSection={<IconMessageCircle size={20} />}
|
|
active={isConvo}
|
|
onClick={() => handleNavigation('convo')}
|
|
variant="filled"
|
|
color="blue"
|
|
styles={{
|
|
root: {
|
|
borderRadius: '8px',
|
|
fontWeight: isConvo ? 600 : 400,
|
|
},
|
|
}}
|
|
/>
|
|
|
|
<NavLink
|
|
label="Manual"
|
|
leftSection={<IconEdit size={20} />}
|
|
active={isEdit}
|
|
onClick={() => handleNavigation('edit')}
|
|
variant="filled"
|
|
color="blue"
|
|
styles={{
|
|
root: {
|
|
borderRadius: '8px',
|
|
fontWeight: isEdit ? 600 : 400,
|
|
},
|
|
}}
|
|
/>
|
|
|
|
<NavLink
|
|
label="Galaxy"
|
|
leftSection={<IconChartBubbleFilled size={20} />}
|
|
active={isGalaxy}
|
|
onClick={() => handleNavigation('galaxy')}
|
|
variant="filled"
|
|
color="blue"
|
|
styles={{
|
|
root: {
|
|
borderRadius: '8px',
|
|
fontWeight: isGalaxy ? 600 : 400,
|
|
},
|
|
}}
|
|
/>
|
|
|
|
<Divider my="md" />
|
|
|
|
{/* Theme Toggle */}
|
|
<ThemeToggle />
|
|
|
|
{/* User Menu - styled like other nav items */}
|
|
<UserMenu showLabel={true} />
|
|
|
|
{/* Development state panel */}
|
|
{process.env.NODE_ENV === 'development' && (
|
|
<Box className={styles.devPanel}>
|
|
<Text size="xs" fw={700} c="dimmed" mb="xs">
|
|
DEV: App State
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
State: {JSON.stringify(state.value)}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
Tags: {Array.from(state.tags).join(', ')}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
Mode: {state.context.mode}
|
|
</Text>
|
|
{state.context.pendingNodeDraft && (
|
|
<Text size="xs" c="dimmed">
|
|
Draft: {state.context.pendingNodeDraft.title || '(untitled)'}
|
|
</Text>
|
|
)}
|
|
{state.context.currentNodeId && (
|
|
<Text size="xs" c="dimmed">
|
|
Node: {state.context.currentNodeId}
|
|
</Text>
|
|
)}
|
|
</Box>
|
|
)}
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
}
|