refactor: 移除顶部栏,设置按钮移至底部栏右侧,StatusBar 全面改 MUI Table

- StatusBar: 全 MUI Table 布局,左侧状态/Provider/Token,右侧版本+设置
- Header: 已删除(功能合并到 StatusBar)
- App.tsx: 移除 Header 引用
This commit is contained in:
thzxx
2026-06-27 22:41:59 +08:00
parent a5ef816763
commit 239bd8703b
3 changed files with 53 additions and 76 deletions
-2
View File
@@ -9,7 +9,6 @@
import { ThemeProvider, CssBaseline } from '@mui/material'; import { ThemeProvider, CssBaseline } from '@mui/material';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Header } from '@renderer/components/layout/Header';
import { Sidebar } from '@renderer/components/layout/Sidebar'; import { Sidebar } from '@renderer/components/layout/Sidebar';
import { DetailPanel } from '@renderer/components/layout/DetailPanel'; import { DetailPanel } from '@renderer/components/layout/DetailPanel';
import { StatusBar } from '@renderer/components/layout/StatusBar'; import { StatusBar } from '@renderer/components/layout/StatusBar';
@@ -55,7 +54,6 @@ export default function App(): React.JSX.Element {
className="h-screen w-screen flex flex-col overflow-hidden select-none" className="h-screen w-screen flex flex-col overflow-hidden select-none"
style={{ background: muiTheme.palette.background.default, color: muiTheme.palette.text.primary }} style={{ background: muiTheme.palette.background.default, color: muiTheme.palette.text.primary }}
> >
<Header />
<div className="flex-1 flex overflow-hidden min-h-0"> <div className="flex-1 flex overflow-hidden min-h-0">
<Sidebar /> <Sidebar />
<ChatPanel /> <ChatPanel />
-44
View File
@@ -1,44 +0,0 @@
/**
* Header — 标题栏
*
* Logo + 会话标题 + 设置按钮。
*/
import { Box, IconButton, Tooltip, Stack, Typography } from '@mui/material';
import { Settings } from 'lucide-react';
import { useUIStore } from '@renderer/stores/ui-store';
import { useSessionStore } from '@renderer/stores/session-store';
type WebkitCSS = React.CSSProperties & { WebkitAppRegion?: string };
export function Header(): React.JSX.Element {
const openSettings = useUIStore((s) => s.openSettings);
const currentSessionId = useSessionStore((s) => s.currentSessionId);
const sessions = useSessionStore((s) => s.sessions);
const currentSession = sessions.find((s) => s.id === currentSessionId);
return (
<header
style={{
flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '0 16px', height: 40, userSelect: 'none',
background: 'var(--bg-secondary)', borderBottom: '1px solid var(--border-color)',
WebkitAppRegion: 'drag',
} as WebkitCSS}
>
<Stack direction="row" spacing={1} alignItems="center" sx={{ minWidth: 0 }}>
<Box component="img" src="./assets/logo.png" alt="Metona" sx={{ width: 20, height: 20, borderRadius: 0.5 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: 'primary.main', fontSize: 13 }}>MetonaAI</Typography>
{currentSession && (
<>
<Typography variant="body2" sx={{ color: 'divider' }}>·</Typography>
<Typography variant="caption" sx={{ color: 'text.secondary', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{currentSession.title}</Typography>
</>
)}
</Stack>
<Stack direction="row" spacing={0.5} sx={{ WebkitAppRegion: 'no-drag' } as WebkitCSS}>
<Tooltip title="设置 (Ctrl+,)"><IconButton size="small" onClick={openSettings} sx={{ color: 'text.secondary' }}><Settings size={14} /></IconButton></Tooltip>
</Stack>
</header>
);
}
+53 -30
View File
@@ -1,11 +1,13 @@
/** /**
* StatusBar — 底部状态栏 * StatusBar — 底部状态栏
* *
* 显示 Agent 状态指示灯、Provider 名称、Token 统计、版本号 * 完全使用 MUI 组件:Table 展示状态/Provider/Token,右侧设置按钮
*/ */
import { Box, Typography, Stack, Chip } from '@mui/material'; import { Box, Typography, IconButton, Tooltip, Table, TableBody, TableRow, TableCell } from '@mui/material';
import { Settings } from 'lucide-react';
import { useAgentStore } from '@renderer/stores/agent-store'; import { useAgentStore } from '@renderer/stores/agent-store';
import { useUIStore } from '@renderer/stores/ui-store';
import { AGENT_STATUS_COLORS, AGENT_STATUS_LABELS, PROVIDER_LABELS } from '@renderer/lib/constants'; import { AGENT_STATUS_COLORS, AGENT_STATUS_LABELS, PROVIDER_LABELS } from '@renderer/lib/constants';
import { formatTokens } from '@renderer/lib/formatters'; import { formatTokens } from '@renderer/lib/formatters';
@@ -14,42 +16,63 @@ export function StatusBar(): React.JSX.Element {
const provider = useAgentStore((s) => s.provider); const provider = useAgentStore((s) => s.provider);
const model = useAgentStore((s) => s.model); const model = useAgentStore((s) => s.model);
const tokenUsage = useAgentStore((s) => s.tokenUsage); const tokenUsage = useAgentStore((s) => s.tokenUsage);
const openSettings = useUIStore((s) => s.openSettings);
return ( return (
<Box <Box
component="footer" component="footer"
sx={{ sx={{
flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexShrink: 0, display: 'flex', alignItems: 'center',
px: 2, height: 24, bgcolor: 'background.paper', borderTop: 1, borderColor: 'divider', userSelect: 'none', px: 1.5, height: 28, bgcolor: 'background.paper',
borderTop: 1, borderColor: 'divider', userSelect: 'none',
}} }}
> >
<Stack direction="row" spacing={1.5} alignItems="center"> {/* 左侧:状态信息表格 */}
<Stack direction="row" spacing={0.75} alignItems="center"> <Table size="small" sx={{
<Box flex: 1, width: 'auto',
sx={{ '& .MuiTableCell-root': { border: 0, py: 0, px: 1, fontSize: 10, whiteSpace: 'nowrap' },
width: 8, height: 8, borderRadius: '50%', flexShrink: 0, }}>
bgcolor: AGENT_STATUS_COLORS[agentStatus], <TableBody>
animation: (agentStatus === 'thinking' || agentStatus === 'executing') ? 'pulse 2s infinite' : 'none', <TableRow>
}} <TableCell sx={{ width: 'auto', py: 0, px: 1 }}>
/> <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 11, lineHeight: 1 }}> <Box
{AGENT_STATUS_LABELS[agentStatus]} sx={{
</Typography> width: 7, height: 7, borderRadius: '50%', flexShrink: 0,
</Stack> bgcolor: AGENT_STATUS_COLORS[agentStatus],
<Typography variant="caption" sx={{ color: 'divider' }}>|</Typography> animation: (agentStatus === 'thinking' || agentStatus === 'executing') ? 'pulse 2s infinite' : 'none',
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 11 }}> }}
{PROVIDER_LABELS[provider] ?? provider} {model} />
</Typography> <Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 10, lineHeight: 1 }}>
</Stack> {AGENT_STATUS_LABELS[agentStatus]}
</Typography>
</Box>
</TableCell>
<TableCell sx={{ color: 'divider', width: 1, p: 0 }}>|</TableCell>
<TableCell sx={{ color: 'text.secondary', fontSize: 10 }}>
{PROVIDER_LABELS[provider] ?? provider} {model}
</TableCell>
{tokenUsage.totalTokens > 0 && (
<>
<TableCell sx={{ color: 'divider', width: 1, p: 0 }}>|</TableCell>
<TableCell sx={{ fontFamily: 'monospace', color: 'text.secondary', fontSize: 10 }}>
{formatTokens(tokenUsage.inputTokens)} {formatTokens(tokenUsage.outputTokens)}
</TableCell>
</>
)}
</TableRow>
</TableBody>
</Table>
<Stack direction="row" spacing={1.5} alignItems="center"> {/* 右侧:版本 + 设置 */}
{tokenUsage.totalTokens > 0 && ( <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0 }}>
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 11 }}> <Typography variant="caption" sx={{ color: 'text.disabled', fontSize: 9 }}>v0.1.0</Typography>
Tokens: {formatTokens(tokenUsage.inputTokens)} {formatTokens(tokenUsage.outputTokens)} <Tooltip title="设置 (Ctrl+,)">
</Typography> <IconButton size="small" onClick={openSettings} sx={{ color: 'text.secondary', width: 24, height: 24 }}>
)} <Settings size={12} />
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 11 }}>MetonaAI v0.1.0</Typography> </IconButton>
</Stack> </Tooltip>
</Box>
</Box> </Box>
); );
} }