mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-08 15:43:42 +08:00
- 整体布局:按新版 UI 重构左侧导航、对象树、连接分组和右键菜单体系 - 数据视图:优化 DDL 侧栏、横向滚动、筛选输入、编辑入口和虚拟表格体验 - AI 面板:重构新版入口、输入区、模型选择、快捷键和悬浮布局 - 标签与快捷键:补齐 Tab 悬浮信息、复制交互和 Mac/Windows 快捷键配置 - 工程质量:新增 v2 主题样式、菜单组件、外观工具和回归测试覆盖
152 lines
7.3 KiB
TypeScript
152 lines
7.3 KiB
TypeScript
import React from 'react';
|
|
import { Button, Tooltip } from 'antd';
|
|
import { HistoryOutlined, RobotOutlined, ClearOutlined, SettingOutlined, CloseOutlined, ExportOutlined, PlusOutlined, ThunderboltOutlined } from '@ant-design/icons';
|
|
import type { OverlayWorkbenchTheme } from '../../utils/overlayWorkbenchTheme';
|
|
import type { AIChatMessage } from '../../types';
|
|
|
|
interface AIChatHeaderProps {
|
|
darkMode: boolean;
|
|
mutedColor: string;
|
|
textColor: string;
|
|
overlayTheme: OverlayWorkbenchTheme;
|
|
isV2Ui?: boolean;
|
|
onHistoryClick: () => void;
|
|
onClear: () => void;
|
|
onSettingsClick: () => void;
|
|
onClose: () => void;
|
|
messages?: AIChatMessage[];
|
|
sessionTitle?: string;
|
|
activeMode?: 'chat' | 'insights' | 'history';
|
|
onModeChange?: (mode: 'chat' | 'insights' | 'history') => void;
|
|
}
|
|
|
|
const exportToMarkdown = (messages: AIChatMessage[], title: string) => {
|
|
const lines: string[] = [`# ${title}`, '', `> 导出时间:${new Date().toLocaleString()}`, ''];
|
|
messages.forEach(msg => {
|
|
const role = msg.role === 'user' ? '👤 You' : '🤖 GoNavi AI';
|
|
lines.push(`## ${role}`);
|
|
lines.push('');
|
|
lines.push(msg.content);
|
|
lines.push('');
|
|
lines.push('---');
|
|
lines.push('');
|
|
});
|
|
const blob = new Blob([lines.join('\n')], { type: 'text/markdown;charset=utf-8' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `${title.replace(/[/\\?%*:|"<>]/g, '-')}.md`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
};
|
|
|
|
export const AIChatHeader: React.FC<AIChatHeaderProps> = ({
|
|
darkMode, mutedColor, textColor, overlayTheme,
|
|
isV2Ui = false,
|
|
onHistoryClick, onClear, onSettingsClick, onClose,
|
|
messages = [], sessionTitle = '新对话',
|
|
activeMode = 'chat',
|
|
onModeChange,
|
|
}) => {
|
|
if (!isV2Ui) {
|
|
return (
|
|
<div className="ai-chat-header" style={{ borderBottom: 'none', padding: '10px 16px', background: darkMode ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.01)' }}>
|
|
<div className="ai-chat-header-left" style={{ gap: 8 }}>
|
|
<Tooltip title="历史会话">
|
|
<Button type="text" size="small" icon={<HistoryOutlined />} onClick={onHistoryClick} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
<div className="ai-logo" style={{ background: overlayTheme.iconBg, color: overlayTheme.iconColor, display: 'flex', alignItems: 'center', justifyContent: 'center', width: 20, height: 20, borderRadius: 6, fontSize: 12 }}>
|
|
<RobotOutlined />
|
|
</div>
|
|
<span className="ai-title" style={{ color: textColor, fontSize: 13, fontWeight: 600 }}>GoNavi AI</span>
|
|
</div>
|
|
<div className="ai-chat-header-right">
|
|
{messages.length > 0 && (
|
|
<Tooltip title="导出为 Markdown">
|
|
<Button type="text" size="small" icon={<ExportOutlined />} onClick={() => exportToMarkdown(messages, sessionTitle)} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
)}
|
|
<Tooltip title="新对话 (清空当前)">
|
|
<Button type="text" size="small" icon={<ClearOutlined />} onClick={onClear} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
<Tooltip title="AI 设置">
|
|
<Button type="text" size="small" icon={<SettingOutlined />} onClick={onSettingsClick} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
<Tooltip title="关闭面板">
|
|
<Button type="text" size="small" icon={<CloseOutlined />} onClick={onClose} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="ai-chat-header gn-v2-ai-header" style={{ borderBottom: 'none', padding: '10px 16px', background: darkMode ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.01)' }}>
|
|
<div className="gn-v2-ai-header-top">
|
|
<div className="ai-chat-header-left gn-v2-ai-brand" style={{ gap: 8 }}>
|
|
<div className="ai-logo" style={{ background: overlayTheme.iconBg, color: overlayTheme.iconColor, display: 'flex', alignItems: 'center', justifyContent: 'center', width: 20, height: 20, borderRadius: 6, fontSize: 12 }}>
|
|
<RobotOutlined />
|
|
</div>
|
|
<div className="ai-title-stack">
|
|
<span className="ai-title" style={{ color: textColor, fontSize: 13, fontWeight: 600 }}>GoNavi AI</span>
|
|
<small>{sessionTitle} · 已连接</small>
|
|
</div>
|
|
<span className="gn-v2-ai-provider-badge">BETA</span>
|
|
</div>
|
|
<div className="ai-chat-header-right gn-v2-ai-header-actions">
|
|
<Tooltip title="新对话">
|
|
<Button type="text" size="small" icon={<PlusOutlined />} onClick={onClear} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
<Tooltip title="历史会话">
|
|
<Button type="text" size="small" icon={<HistoryOutlined />} onClick={onHistoryClick} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
<Tooltip title="AI 设置">
|
|
<Button type="text" size="small" icon={<SettingOutlined />} onClick={onSettingsClick} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
<Tooltip title="关闭面板">
|
|
<Button type="text" size="small" icon={<CloseOutlined />} onClick={onClose} style={{ color: mutedColor }} />
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="gn-v2-ai-mode-tabs" aria-label="AI 工作模式">
|
|
<button
|
|
type="button"
|
|
className={activeMode === 'chat' ? 'is-active' : undefined}
|
|
onClick={() => onModeChange?.('chat')}
|
|
>
|
|
<RobotOutlined />
|
|
<span>对话</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={activeMode === 'insights' ? 'is-active' : undefined}
|
|
onClick={() => onModeChange?.('insights')}
|
|
>
|
|
<ThunderboltOutlined />
|
|
<span>自动洞察</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={activeMode === 'history' ? 'is-active' : undefined}
|
|
onClick={() => onModeChange?.('history')}
|
|
>
|
|
<HistoryOutlined />
|
|
<span>历史</span>
|
|
</button>
|
|
</div>
|
|
|
|
{messages.length > 0 && (
|
|
<div className="gn-v2-ai-session-row">
|
|
<button type="button" className="gn-v2-ai-export-button" onClick={() => exportToMarkdown(messages, sessionTitle)}>
|
|
<ExportOutlined />
|
|
<span>导出</span>
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|