feat: Improve UI layout and navigation
- Increase logo size (48x48 desktop, 56x56 mobile) for better visibility - Add logo as favicon - Add logo to mobile header - Move user menu to navigation bars (sidebar on desktop, bottom bar on mobile) - Fix desktop chat layout - container structure prevents voice controls cutoff - Fix mobile bottom bar - use icon-only ActionIcons instead of truncated text buttons - Hide Create Node/New Conversation buttons on mobile to save header space - Make fixed header and voice controls work properly with containers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
127
components/Navigation/DesktopSidebar.tsx
Normal file
127
components/Navigation/DesktopSidebar.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
'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, Text, Group, Image, Divider } from '@mantine/core';
|
||||
import { IconMessageCircle, IconEdit, IconUniverse } from '@tabler/icons-react';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useAppMachine } from '@/hooks/useAppMachine';
|
||||
import { UserMenu } from '@/components/UserMenu';
|
||||
|
||||
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
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRight: '1px solid #dee2e6',
|
||||
padding: '1rem',
|
||||
}}
|
||||
>
|
||||
<Stack gap="xs">
|
||||
<Group gap="sm" mb="md" align="center">
|
||||
<Image
|
||||
src="/logo.svg"
|
||||
alt="Ponderants logo"
|
||||
w={48}
|
||||
h={48}
|
||||
style={{ flexShrink: 0 }}
|
||||
/>
|
||||
<Text fw={700} size="md" c="dimmed">
|
||||
Ponderants
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
<NavLink
|
||||
label="Convo"
|
||||
leftSection={<IconMessageCircle size={20} />}
|
||||
active={isConvo}
|
||||
onClick={() => handleNavigation('convo')}
|
||||
variant="filled"
|
||||
/>
|
||||
|
||||
<NavLink
|
||||
label="Manual"
|
||||
leftSection={<IconEdit size={20} />}
|
||||
active={isEdit}
|
||||
onClick={() => handleNavigation('edit')}
|
||||
variant="filled"
|
||||
/>
|
||||
|
||||
<NavLink
|
||||
label="Galaxy"
|
||||
leftSection={<IconUniverse size={20} />}
|
||||
active={isGalaxy}
|
||||
onClick={() => handleNavigation('galaxy')}
|
||||
variant="filled"
|
||||
/>
|
||||
|
||||
<Divider my="md" />
|
||||
|
||||
<Box style={{ padding: '0.5rem' }}>
|
||||
<UserMenu />
|
||||
</Box>
|
||||
|
||||
{/* Development state panel */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<Box mt="xl" p="sm" style={{ border: '1px solid #495057', borderRadius: '4px' }}>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
95
components/Navigation/MobileBottomBar.tsx
Normal file
95
components/Navigation/MobileBottomBar.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* Mobile Bottom Bar Navigation
|
||||
*
|
||||
* Fixed bottom navigation for mobile devices (< 768px).
|
||||
* Shows three buttons: Convo, Edit, Galaxy
|
||||
* Highlights the active mode based on app state machine.
|
||||
*/
|
||||
|
||||
import { Group, Button, Paper, ActionIcon, Box } from '@mantine/core';
|
||||
import { IconMessageCircle, IconEdit, IconUniverse, IconUser } from '@tabler/icons-react';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useAppMachine } from '@/hooks/useAppMachine';
|
||||
import { UserMenu } from '@/components/UserMenu';
|
||||
|
||||
export function MobileBottomBar() {
|
||||
const actor = useAppMachine();
|
||||
const state = useSelector(actor, (state) => state);
|
||||
const send = actor.send;
|
||||
|
||||
const handleNavigation = (target: 'convo' | 'edit' | 'galaxy') => {
|
||||
console.log('[Mobile 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('[Mobile Nav] Current state:', state.value, {
|
||||
isConvo,
|
||||
isEdit,
|
||||
isGalaxy,
|
||||
});
|
||||
|
||||
return (
|
||||
<Paper
|
||||
withBorder
|
||||
p="md"
|
||||
radius={0}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 100,
|
||||
borderTop: '1px solid #dee2e6',
|
||||
}}
|
||||
>
|
||||
<Group justify="space-around" grow>
|
||||
<ActionIcon
|
||||
variant={isConvo ? 'filled' : 'subtle'}
|
||||
color={isConvo ? 'blue' : 'gray'}
|
||||
onClick={() => handleNavigation('convo')}
|
||||
size={48}
|
||||
radius="md"
|
||||
>
|
||||
<IconMessageCircle size={24} />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
variant={isEdit ? 'filled' : 'subtle'}
|
||||
color={isEdit ? 'blue' : 'gray'}
|
||||
onClick={() => handleNavigation('edit')}
|
||||
size={48}
|
||||
radius="md"
|
||||
>
|
||||
<IconEdit size={24} />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon
|
||||
variant={isGalaxy ? 'filled' : 'subtle'}
|
||||
color={isGalaxy ? 'blue' : 'gray'}
|
||||
onClick={() => handleNavigation('galaxy')}
|
||||
size={48}
|
||||
radius="md"
|
||||
>
|
||||
<IconUniverse size={24} />
|
||||
</ActionIcon>
|
||||
|
||||
<Box style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<UserMenu />
|
||||
</Box>
|
||||
</Group>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
40
components/Navigation/MobileHeader.tsx
Normal file
40
components/Navigation/MobileHeader.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* Mobile Header
|
||||
*
|
||||
* Fixed header for mobile devices showing the Ponderants logo.
|
||||
*/
|
||||
|
||||
import { Group, Image, Text, Paper } from '@mantine/core';
|
||||
|
||||
export function MobileHeader() {
|
||||
return (
|
||||
<Paper
|
||||
withBorder
|
||||
p="md"
|
||||
radius={0}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 100,
|
||||
borderBottom: '1px solid #dee2e6',
|
||||
}}
|
||||
>
|
||||
<Group gap="sm" align="center">
|
||||
<Image
|
||||
src="/logo.svg"
|
||||
alt="Ponderants logo"
|
||||
w={56}
|
||||
h={56}
|
||||
style={{ flexShrink: 0 }}
|
||||
/>
|
||||
<Text fw={700} size="xl">
|
||||
Ponderants
|
||||
</Text>
|
||||
</Group>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user