UNPKG

dynamic-interaction

Version:

Dynamic interaction 动态交互mcp,用于cursor、windsurf、trae 等 AI 智能编辑器 Agent 运行时交互使用

214 lines 9.55 kB
/** * 状态栏组件 * 处理工作区目录、会话ID、连接状态、延迟和消息状态的显示 */ import { getElementById } from '../utils/dom.js'; import { eventBus, APP_EVENTS } from '../core/events.js'; import { i18nService } from '../services/i18n.js'; const LATENCY_THRESHOLD = { NORMAL: 100, HIGH: 300 }; class StatusBarComponent { elements = {}; state = { lastPingSent: 0, pingInterval: 0, currentLatency: 0, systemInfo: { workspaceDirectory: i18nService.t('feedback.messages.unknownDirectory'), sessionId: i18nService.t('feedback.messages.unknown'), }, connectionStatus: 'disconnected', messageStatus: 'idle', sessionTimerInterval: null, }; initialize() { this.initializeElements(); this.setupEventListeners(); this.updateConnectionStatus('disconnected'); this.updateMessageStatus('idle'); } updateSystemInfo(info) { this.state.systemInfo = { ...this.state.systemInfo, ...info }; if (this.elements.workspaceDirectory) { this.elements.workspaceDirectory.textContent = this.state.systemInfo.workspaceDirectory; this.elements.workspaceDirectory.title = this.state.systemInfo.workspaceDirectory; } if (this.elements.sessionId) { this.elements.sessionId.textContent = this.state.systemInfo.sessionId; } } updateConnectionStatus(status) { this.state.connectionStatus = status; if (this.elements.connectionStatus && this.elements.statusPulse) { const statusClasses = ['connected', 'disconnected', 'high-latency', 'reconnecting']; statusClasses.forEach(cls => { this.elements.connectionStatus.classList.remove(cls); this.elements.statusPulse.classList.remove(cls); }); this.elements.connectionStatus.classList.add(status); this.elements.statusPulse.classList.add(status); const statusTexts = { connected: i18nService.t('status.connectionStatus.connected'), disconnected: i18nService.t('status.connectionStatus.disconnected'), 'high-latency': i18nService.t('status.connectionStatus.highLatency'), reconnecting: i18nService.t('status.connectionStatus.reconnecting') }; this.elements.connectionStatus.textContent = statusTexts[status]; if (status === 'disconnected' || status === 'reconnecting') { this.resetLatencyDisplay(); } } eventBus.emit(APP_EVENTS.STATUS_CHANGED, { type: 'connection', status }); } updateLatency(latency) { this.state.currentLatency = latency; if (this.elements.latencyValue) { const latencyClasses = ['normal', 'medium', 'high']; latencyClasses.forEach(cls => this.elements.latencyValue.classList.remove(cls)); if (latency < LATENCY_THRESHOLD.NORMAL) { this.elements.latencyValue.classList.add('normal'); } else if (latency < LATENCY_THRESHOLD.HIGH) { this.elements.latencyValue.classList.add('medium'); } else { this.elements.latencyValue.classList.add('high'); } this.elements.latencyValue.textContent = `${latency}ms`; if (latency >= LATENCY_THRESHOLD.HIGH) { this.updateConnectionStatus('high-latency'); } else if (this.state.connectionStatus !== 'disconnected') { this.updateConnectionStatus('connected'); } } } updateMessageStatus(status) { this.state.messageStatus = status; if (this.elements.messageStatus) { const messageClasses = ['idle', 'sending', 'waiting', 'received', 'timeout']; messageClasses.forEach(cls => this.elements.messageStatus.classList.remove(cls)); this.elements.messageStatus.classList.add(status); const statusTexts = { idle: i18nService.t('status.messageStatus.idle'), sending: i18nService.t('status.messageStatus.sending'), waiting: i18nService.t('status.messageStatus.waiting'), received: i18nService.t('status.messageStatus.received'), timeout: i18nService.t('status.messageStatus.timeout') }; this.elements.messageStatus.textContent = statusTexts[status]; } } startSessionTimer(sessionStartTime, totalDurationSeconds) { if (!this.elements.timer || !this.elements.timerValue) return; this.stopSessionTimer(); const sessionEndTime = sessionStartTime + totalDurationSeconds * 1000; const updateTimer = () => { const now = Date.now(); const remainingMilliseconds = sessionEndTime - now; const remainingSeconds = Math.max(0, Math.floor(remainingMilliseconds / 1000)); this.elements.timerValue.textContent = this.formatTime(remainingSeconds); if (remainingSeconds <= 0) { this.stopSessionTimer(); this.updateMessageStatus('timeout'); } }; this.elements.timer.style.display = 'flex'; updateTimer(); this.state.sessionTimerInterval = window.setInterval(updateTimer, 1000); } stopSessionTimer() { if (this.state.sessionTimerInterval) { clearInterval(this.state.sessionTimerInterval); this.state.sessionTimerInterval = null; } if (this.elements.timer && this.elements.timerValue) { if (this.state.messageStatus !== 'timeout') { this.elements.timer.style.display = 'none'; this.elements.timerValue.textContent = '--:--'; } else { this.elements.timer.style.display = 'flex'; this.elements.timerValue.textContent = i18nService.t('feedback.messages.timeout'); } } } startPingInterval() { if (this.state.pingInterval) { clearInterval(this.state.pingInterval); } this.sendPing(); this.state.pingInterval = window.setInterval(() => this.sendPing(), 5000); } stopPingInterval() { if (this.state.pingInterval) { clearInterval(this.state.pingInterval); this.state.pingInterval = 0; } } handlePong(data) { const now = Date.now(); const latency = data?.timestamp ? now - data.timestamp : now - this.state.lastPingSent; this.updateLatency(latency); if (latency < LATENCY_THRESHOLD.HIGH) { this.updateConnectionStatus('connected'); } } initializeElements() { this.elements = { workspaceDirectory: getElementById('workspace-directory-value') || undefined, sessionId: getElementById('session-id-value') || undefined, connectionStatus: getElementById('connection-status-value') || undefined, latencyValue: getElementById('latency-value') || undefined, messageStatus: getElementById('message-status-value') || undefined, statusPulse: getElementById('status-pulse') || undefined, timer: getElementById('session-timer') || undefined, timerValue: getElementById('session-timer-value') || undefined, }; } setupEventListeners() { eventBus.on(APP_EVENTS.WS_CONNECTED, () => { this.updateConnectionStatus('connected'); this.startPingInterval(); }); eventBus.on(APP_EVENTS.WS_DISCONNECTED, () => { this.updateConnectionStatus('disconnected'); this.stopSessionTimer(); this.stopPingInterval(); }); } resetLatencyDisplay() { if (this.elements.latencyValue) { this.elements.latencyValue.textContent = '-- ms'; this.elements.latencyValue.classList.remove('normal', 'medium', 'high'); } } formatTime(totalSeconds) { const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; } sendPing() { if (window.ws && window.ws.readyState === WebSocket.OPEN) { this.state.lastPingSent = Date.now(); window.ws.send(JSON.stringify({ type: 'ping' })); } } } export const statusBarComponent = new StatusBarComponent(); // 将组件方法绑定到全局对象以保持向后兼容 window.statusBar = { updateSystemInfo: statusBarComponent.updateSystemInfo.bind(statusBarComponent), updateConnectionStatus: statusBarComponent.updateConnectionStatus.bind(statusBarComponent), updateLatency: statusBarComponent.updateLatency.bind(statusBarComponent), updateMessageStatus: statusBarComponent.updateMessageStatus.bind(statusBarComponent), handlePong: statusBarComponent.handlePong.bind(statusBarComponent), startSessionTimer: statusBarComponent.startSessionTimer.bind(statusBarComponent), stopSessionTimer: statusBarComponent.stopSessionTimer.bind(statusBarComponent), startPingInterval: statusBarComponent.startPingInterval.bind(statusBarComponent), stopPingInterval: statusBarComponent.stopPingInterval.bind(statusBarComponent), }; //# sourceMappingURL=statusBar.js.map