feat(ui): 优化 Header 和状态栏布局
- ContextUsage: 紧凑模式下始终显示压缩按钮 - StatusBar: 连接状态移至中间位置,显示绿点动画和文字 - StatusBar: 添加内部连接状态检测(通过 health API) - Chat: 移除 Header 中的连接状态指示器
This commit is contained in:
@@ -152,9 +152,27 @@ export function ContextUsage({
|
||||
</div>
|
||||
{/* 数值 */}
|
||||
<span className={cn('text-xs', textColor)}>{formatted}</span>
|
||||
{/* 警告图标 */}
|
||||
{shouldCompress && (
|
||||
<AlertTriangle size={12} className="text-amber-500" />
|
||||
{/* 压缩按钮 */}
|
||||
{showCompressButton && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={cn(
|
||||
'h-5 px-1.5 text-xs',
|
||||
shouldCompress
|
||||
? 'text-amber-500 hover:text-amber-400 hover:bg-amber-500/10'
|
||||
: 'text-fg-muted hover:text-fg-secondary hover:bg-surface-muted'
|
||||
)}
|
||||
onClick={handleCompress}
|
||||
disabled={compressing}
|
||||
title="压缩对话上下文"
|
||||
>
|
||||
{compressing ? (
|
||||
<RefreshCw size={12} className="animate-spin" />
|
||||
) : (
|
||||
<Zap size={12} />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { GitBranch, AlertTriangle, AlertCircle, Wifi, WifiOff, RefreshCw, CheckCircle } from 'lucide-react';
|
||||
import { GitBranch, AlertTriangle, AlertCircle, WifiOff, RefreshCw, CheckCircle } from 'lucide-react';
|
||||
import { cn } from '../utils/cn.js';
|
||||
import { getLSPDiagnostics, getGitInfo, type DiagnosticsSummary, type GitInfo } from '../api/client.js';
|
||||
import { getLSPDiagnostics, getGitInfo, getHealth, type DiagnosticsSummary, type GitInfo } from '../api/client.js';
|
||||
|
||||
interface StatusBarProps {
|
||||
className?: string;
|
||||
@@ -21,13 +21,17 @@ interface StatusBarProps {
|
||||
|
||||
export function StatusBar({
|
||||
className,
|
||||
isConnected = true,
|
||||
isConnected: isConnectedProp,
|
||||
onDiagnosticsClick,
|
||||
refreshInterval = 30000,
|
||||
}: StatusBarProps) {
|
||||
const [diagnostics, setDiagnostics] = useState<DiagnosticsSummary | null>(null);
|
||||
const [gitInfo, setGitInfo] = useState<GitInfo | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [connectionStatus, setConnectionStatus] = useState(true);
|
||||
|
||||
// 如果外部传入了 isConnected,使用外部值;否则使用内部检测
|
||||
const isConnected = isConnectedProp ?? connectionStatus;
|
||||
|
||||
// 加载诊断信息和 Git 信息
|
||||
const loadData = useCallback(async () => {
|
||||
@@ -45,12 +49,37 @@ export function StatusBar({
|
||||
if (gitResult.success && gitResult.data) {
|
||||
setGitInfo(gitResult.data);
|
||||
}
|
||||
|
||||
// 如果没有外部传入连接状态,内部检测
|
||||
if (isConnectedProp === undefined) {
|
||||
setConnectionStatus(true);
|
||||
}
|
||||
} catch {
|
||||
// 忽略错误
|
||||
// 请求失败说明可能断开连接
|
||||
if (isConnectedProp === undefined) {
|
||||
setConnectionStatus(false);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
}, [isConnectedProp]);
|
||||
|
||||
// 检测连接状态(如果没有外部传入)
|
||||
const checkConnection = useCallback(async () => {
|
||||
if (isConnectedProp !== undefined) return;
|
||||
|
||||
try {
|
||||
await getHealth();
|
||||
setConnectionStatus(true);
|
||||
} catch {
|
||||
setConnectionStatus(false);
|
||||
}
|
||||
}, [isConnectedProp]);
|
||||
|
||||
// 初始连接检测
|
||||
useEffect(() => {
|
||||
checkConnection();
|
||||
}, [checkConnection]);
|
||||
|
||||
// 初始加载和定时刷新
|
||||
useEffect(() => {
|
||||
@@ -94,6 +123,30 @@ export function StatusBar({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 中间 - 连接状态 */}
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-1.5',
|
||||
isConnected ? 'text-green-400' : 'text-red-400'
|
||||
)}
|
||||
title={isConnected ? 'Connected to server' : 'Disconnected from server'}
|
||||
>
|
||||
{isConnected ? (
|
||||
<>
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500" />
|
||||
</span>
|
||||
<span>Connected</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<WifiOff size={12} />
|
||||
<span>Disconnected</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 右侧 */}
|
||||
<div className="flex items-center gap-3">
|
||||
{/* 诊断信息 */}
|
||||
@@ -123,17 +176,6 @@ export function StatusBar({
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* 连接状态 */}
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-1',
|
||||
isConnected ? 'text-green-400' : 'text-red-400'
|
||||
)}
|
||||
title={isConnected ? 'Connected to server' : 'Disconnected from server'}
|
||||
>
|
||||
{isConnected ? <Wifi size={12} /> : <WifiOff size={12} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { WifiOff, MessageSquare, Terminal, Plug, Zap, Bot, History, Server, MessagesSquare } from 'lucide-react';
|
||||
import { MessageSquare, Terminal, Plug, Zap, Bot, History, Server, MessagesSquare } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
@@ -129,30 +129,6 @@ export function ChatPage({
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
// 连接状态指示器
|
||||
const ConnectionStatus = () => (
|
||||
<div className="flex items-center gap-1.5 text-sm">
|
||||
{isConnected ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="flex items-center gap-1.5"
|
||||
>
|
||||
<span className="relative flex h-2.5 w-2.5">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-green-500" />
|
||||
</span>
|
||||
<span className="text-green-400 hidden sm:inline">Connected</span>
|
||||
</motion.div>
|
||||
) : (
|
||||
<div className="flex items-center gap-1.5 text-red-400">
|
||||
<WifiOff size={16} />
|
||||
<span className="hidden sm:inline">Disconnected</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full">
|
||||
{/* Header */}
|
||||
@@ -164,14 +140,11 @@ export function ChatPage({
|
||||
<ContextUsage
|
||||
sessionId={sessionId}
|
||||
compact
|
||||
showCompressButton={false}
|
||||
showCompressButton
|
||||
refreshInterval={30000}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 连接状态 */}
|
||||
<ConnectionStatus />
|
||||
|
||||
{/* 工具栏按钮 */}
|
||||
{(onOpenCommands || onOpenMCP || onOpenHooks || onOpenAgents || onOpenCheckpoints || onOpenProviders || onOpenLSP || onOpenDiagnostics || onOpenSessions) && (
|
||||
<div className="flex items-center gap-1.5 border-l border-line-muted pl-3">
|
||||
|
||||
Reference in New Issue
Block a user