fix: add mobile navigation and restore chat outline/history access

main
sp mac bookpro 2605 2026-06-11 14:01:00 +08:00
parent faef8b6aea
commit deab555b3a
5 changed files with 86 additions and 8 deletions

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom';
import { Spin } from 'antd';
import { Button, Drawer, Spin } from 'antd';
import { MenuOutlined, SearchOutlined } from '@ant-design/icons';
import Sidebar from './components/Sidebar';
import CommandPalette from './components/CommandPalette';
import AgentList from './pages/AgentList';
@ -25,6 +26,8 @@ export default function App() {
const { user } = useAuth();
const location = useLocation();
const [paletteOpen, setPaletteOpen] = useState(false);
const [mobileNavOpen, setMobileNavOpen] = useState(false);
const [isMobile, setIsMobile] = useState(() => (typeof window === 'undefined' ? false : window.innerWidth < 768));
// 全局快捷键 Ctrl/⌘ + K
useEffect(() => {
@ -39,6 +42,12 @@ export default function App() {
return () => window.removeEventListener('keydown', handler);
}, [user]);
useEffect(() => {
const onResize = () => setIsMobile(window.innerWidth < 768);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
const mainContent = (
<Routes>
<Route path="/" element={<HomeRedirect />} />
@ -73,13 +82,32 @@ export default function App() {
) : (
<div className="layout-shell">
{/* 只有编辑器全屏显示,其他页面均保留侧边栏 */}
{!location.pathname.startsWith('/agents/') || location.pathname.includes('/chat') ? (
{!isMobile && (!location.pathname.startsWith('/agents/') || location.pathname.includes('/chat')) ? (
<Sidebar onOpenPalette={() => setPaletteOpen(true)} />
) : null}
<main className="main">
{mainContent}
{isMobile && (
<div className="mobile-topbar">
<Button type="text" icon={<MenuOutlined />} onClick={() => setMobileNavOpen(true)} />
<div className="mobile-topbar-title">Agent Studio</div>
<Button type="text" icon={<SearchOutlined />} onClick={() => setPaletteOpen(true)} />
</div>
)}
<div className="main-content">{mainContent}</div>
</main>
<CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} />
{isMobile && (
<Drawer
title="导航"
placement="left"
open={mobileNavOpen}
onClose={() => setMobileNavOpen(false)}
width={280}
styles={{ body: { padding: 0 } }}
>
<Sidebar onOpenPalette={() => setPaletteOpen(true)} onNavigate={() => setMobileNavOpen(false)} />
</Drawer>
)}
</div>
)}
</>

View File

@ -18,6 +18,7 @@ import { useTheme } from '../main';
interface Props {
onOpenPalette?: () => void;
onNavigate?: () => void;
}
const NAV_GROUPS: Array<{
@ -54,7 +55,7 @@ const NAV_GROUPS: Array<{
}
];
export default function Sidebar({ onOpenPalette }: Props) {
export default function Sidebar({ onOpenPalette, onNavigate }: Props) {
const { user, logout } = useAuth();
const navigate = useNavigate();
const { mode, toggle } = useTheme();
@ -77,7 +78,10 @@ export default function Sidebar({ onOpenPalette }: Props) {
</div>
<div
onClick={onOpenPalette}
onClick={() => {
onOpenPalette?.();
onNavigate?.();
}}
className="nav-item"
style={{
cursor: 'pointer',
@ -103,6 +107,7 @@ export default function Sidebar({ onOpenPalette }: Props) {
to={it.to}
end={it.end}
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
onClick={onNavigate}
>
<span className="nav-icon">{it.icon}</span>
<span>{it.label}</span>
@ -135,6 +140,7 @@ export default function Sidebar({ onOpenPalette }: Props) {
onClick: async () => {
await logout();
navigate('/login');
onNavigate?.();
}
}
]

View File

@ -16,7 +16,14 @@ export default function ChatOutline(props: { messages: ChatMessage[]; onJump: (i
const { messages, onJump, activeId } = props;
const items = messages.filter((m) => ((m as any)?.speaker?.type ? (m as any).speaker.type === 'agent' : m.role === 'assistant'));
if (items.length === 0) return null;
if (items.length === 0) {
return (
<aside className="chat-outline">
<div className="chat-outline-title"></div>
<div style={{ padding: 12, color: 'var(--color-text-tertiary)', fontSize: 12 }}></div>
</aside>
);
}
return (
<aside className="chat-outline">

View File

@ -100,6 +100,9 @@ export default function ChatPageH5({ logic }: { logic: ChatPageLogicOutput }) {
<Button size="small" onClick={() => setOutlineDrawerOpen(true)}>
</Button>
<Button size="small" onClick={() => setHistoryDrawerOpen(true)}>
</Button>
</Space>
</div>

View File

@ -620,12 +620,42 @@ body {
}
@media (max-width: 768px) {
.sidebar {
.layout-shell > .sidebar {
display: none;
}
.main {
width: 100vw;
display: flex;
flex-direction: column;
overflow: hidden;
}
.mobile-topbar {
height: 48px;
flex: 0 0 auto;
display: flex;
align-items: center;
gap: 8px;
padding: 0 8px;
border-bottom: 1px solid var(--color-border);
background: var(--color-surface);
}
.mobile-topbar-title {
flex: 1;
min-width: 0;
font-weight: 700;
color: var(--color-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.main-content {
flex: 1;
min-height: 0;
overflow: auto;
}
.chat-shell {
@ -677,6 +707,10 @@ body {
}
}
.main-content {
min-height: 0;
}
.chat-body .messages-container {
max-width: 780px;
width: 100%;