Skip to main content
Glama

Office MCP Server

by walkingzzzy
ChatHistory.tsx8.04 kB
import React, { useState } from 'react'; import { ChatSession, ChatMessage } from '../types'; import './ChatHistory.css'; interface ChatHistoryProps { sessions: ChatSession[]; currentSessionId: string | null; onSessionSelect: (sessionId: string) => void; onSessionDelete: (sessionId: string) => void; onSessionRename: (sessionId: string, newTitle: string) => void; onNewSession: () => void; onClearAll: () => void; onExport: () => void; onImport: (data: string) => void; } export const ChatHistory: React.FC<ChatHistoryProps> = ({ sessions, currentSessionId, onSessionSelect, onSessionDelete, onSessionRename, onNewSession, onClearAll, onExport, onImport }) => { const [searchQuery, setSearchQuery] = useState(''); const [editingSessionId, setEditingSessionId] = useState<string | null>(null); const [editingTitle, setEditingTitle] = useState(''); const [showConfirmClear, setShowConfirmClear] = useState(false); const filteredSessions = sessions.filter(session => session.title.toLowerCase().includes(searchQuery.toLowerCase()) || session.messages.some(msg => msg.content.toLowerCase().includes(searchQuery.toLowerCase()) ) ); const handleStartEdit = (session: ChatSession) => { setEditingSessionId(session.id); setEditingTitle(session.title); }; const handleSaveEdit = () => { if (editingSessionId && editingTitle.trim()) { onSessionRename(editingSessionId, editingTitle.trim()); } setEditingSessionId(null); setEditingTitle(''); }; const handleCancelEdit = () => { setEditingSessionId(null); setEditingTitle(''); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { handleSaveEdit(); } else if (e.key === 'Escape') { handleCancelEdit(); } }; const formatDate = (timestamp: number) => { const date = new Date(timestamp); const now = new Date(); const diffDays = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24)); if (diffDays === 0) { return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); } else if (diffDays === 1) { return '昨天'; } else if (diffDays < 7) { return `${diffDays}天前`; } else { return date.toLocaleDateString('zh-CN'); } }; const getSessionPreview = (session: ChatSession): string => { const lastMessage = session.messages[session.messages.length - 1]; if (!lastMessage) return '暂无消息'; const preview = lastMessage.content.replace(/\n/g, ' ').trim(); return preview.length > 50 ? preview.substring(0, 50) + '...' : preview; }; const handleImportFile = (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { const content = event.target?.result as string; if (content) { onImport(content); } }; reader.readAsText(file); e.target.value = ''; }; return ( <div className="chat-history"> <div className="chat-history-header"> <h3>聊天历史</h3> <div className="history-actions"> <button className="action-button new-session" onClick={onNewSession} title="新建对话" > ➕ </button> <button className="action-button export" onClick={onExport} title="导出历史" > 📤 </button> <label className="action-button import" title="导入历史"> 📥 <input type="file" accept=".json" onChange={handleImportFile} style={{ display: 'none' }} /> </label> <button className="action-button clear" onClick={() => setShowConfirmClear(true)} title="清空历史" > 🗑️ </button> </div> </div> <div className="search-container"> <input type="text" placeholder="搜索对话..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} className="search-input" /> </div> <div className="sessions-list"> {filteredSessions.length === 0 ? ( <div className="empty-state"> {searchQuery ? ( <> <div className="empty-icon">🔍</div> <p>未找到匹配的对话</p> </> ) : ( <> <div className="empty-icon">💬</div> <p>暂无聊天历史</p> <button className="start-chat-button" onClick={onNewSession}> 开始新对话 </button> </> )} </div> ) : ( filteredSessions.map((session) => ( <div key={session.id} className={`session-item ${ session.id === currentSessionId ? 'active' : '' }`} onClick={() => onSessionSelect(session.id)} > <div className="session-content"> {editingSessionId === session.id ? ( <input type="text" value={editingTitle} onChange={(e) => setEditingTitle(e.target.value)} onKeyDown={handleKeyDown} onBlur={handleSaveEdit} className="session-title-input" autoFocus /> ) : ( <> <div className="session-title">{session.title}</div> <div className="session-preview"> {getSessionPreview(session)} </div> <div className="session-meta"> <span className="session-date"> {formatDate(session.updatedAt)} </span> <span className="session-count"> {session.messages.length} 条消息 </span> </div> </> )} </div> <div className="session-actions"> <button className="session-action edit" onClick={(e) => { e.stopPropagation(); handleStartEdit(session); }} title="重命名" > ✏️ </button> <button className="session-action delete" onClick={(e) => { e.stopPropagation(); onSessionDelete(session.id); }} title="删除" > 🗑️ </button> </div> </div> )) )} </div> {showConfirmClear && ( <div className="confirm-dialog"> <div className="confirm-content"> <h4>确认清空历史</h4> <p>此操作将删除所有聊天历史,无法恢复。确定要继续吗?</p> <div className="confirm-actions"> <button className="confirm-button cancel" onClick={() => setShowConfirmClear(false)} > 取消 </button> <button className="confirm-button confirm" onClick={() => { onClearAll(); setShowConfirmClear(false); }} > 确认清空 </button> </div> </div> </div> )} </div> ); }; export default ChatHistory;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/walkingzzzy/office-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server