dynamic-interaction
Version:
Dynamic interaction 动态交互mcp,用于cursor、windsurf、trae 等 AI 智能编辑器 Agent 运行时交互使用
214 lines • 9.55 kB
JavaScript
/**
* 状态栏组件
* 处理工作区目录、会话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