refactor: 移除顶部栏,设置按钮移至底部栏右侧,StatusBar 全面改 MUI Table
- StatusBar: 全 MUI Table 布局,左侧状态/Provider/Token,右侧版本+设置 - Header: 已删除(功能合并到 StatusBar) - App.tsx: 移除 Header 引用
This commit is contained in:
@@ -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 />
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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={{
|
||||||
|
flex: 1, width: 'auto',
|
||||||
|
'& .MuiTableCell-root': { border: 0, py: 0, px: 1, fontSize: 10, whiteSpace: 'nowrap' },
|
||||||
|
}}>
|
||||||
|
<TableBody>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell sx={{ width: 'auto', py: 0, px: 1 }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: 8, height: 8, borderRadius: '50%', flexShrink: 0,
|
width: 7, height: 7, borderRadius: '50%', flexShrink: 0,
|
||||||
bgcolor: AGENT_STATUS_COLORS[agentStatus],
|
bgcolor: AGENT_STATUS_COLORS[agentStatus],
|
||||||
animation: (agentStatus === 'thinking' || agentStatus === 'executing') ? 'pulse 2s infinite' : 'none',
|
animation: (agentStatus === 'thinking' || agentStatus === 'executing') ? 'pulse 2s infinite' : 'none',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 11, lineHeight: 1 }}>
|
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 10, lineHeight: 1 }}>
|
||||||
{AGENT_STATUS_LABELS[agentStatus]}
|
{AGENT_STATUS_LABELS[agentStatus]}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Box>
|
||||||
<Typography variant="caption" sx={{ color: 'divider' }}>|</Typography>
|
</TableCell>
|
||||||
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 11 }}>
|
<TableCell sx={{ color: 'divider', width: 1, p: 0 }}>|</TableCell>
|
||||||
|
<TableCell sx={{ color: 'text.secondary', fontSize: 10 }}>
|
||||||
{PROVIDER_LABELS[provider] ?? provider} {model}
|
{PROVIDER_LABELS[provider] ?? provider} {model}
|
||||||
</Typography>
|
</TableCell>
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Stack direction="row" spacing={1.5} alignItems="center">
|
|
||||||
{tokenUsage.totalTokens > 0 && (
|
{tokenUsage.totalTokens > 0 && (
|
||||||
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 11 }}>
|
<>
|
||||||
Tokens: {formatTokens(tokenUsage.inputTokens)}↓ {formatTokens(tokenUsage.outputTokens)}↑
|
<TableCell sx={{ color: 'divider', width: 1, p: 0 }}>|</TableCell>
|
||||||
</Typography>
|
<TableCell sx={{ fontFamily: 'monospace', color: 'text.secondary', fontSize: 10 }}>
|
||||||
|
{formatTokens(tokenUsage.inputTokens)}↓ {formatTokens(tokenUsage.outputTokens)}↑
|
||||||
|
</TableCell>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<Typography variant="caption" sx={{ color: 'text.secondary', fontSize: 11 }}>MetonaAI v0.1.0</Typography>
|
</TableRow>
|
||||||
</Stack>
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
{/* 右侧:版本 + 设置 */}
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0 }}>
|
||||||
|
<Typography variant="caption" sx={{ color: 'text.disabled', fontSize: 9 }}>v0.1.0</Typography>
|
||||||
|
<Tooltip title="设置 (Ctrl+,)">
|
||||||
|
<IconButton size="small" onClick={openSettings} sx={{ color: 'text.secondary', width: 24, height: 24 }}>
|
||||||
|
<Settings size={12} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user