UNPKG

dynamic-interaction

Version:

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

128 lines 5.24 kB
/** * 反馈组件 * 处理用户反馈的收集和发送 */ import { getElementById } from '../utils/dom.js'; import { eventBus, APP_EVENTS } from '../core/events.js'; import { webSocketService } from '../services/websocket.js'; import { imageHandler, getAttachedImageData } from './imageHandler.js'; import { i18nService } from '../services/i18n.js'; class FeedbackComponent { elements = {}; isEnabled = true; initialize() { this.initializeElements(); this.setupEventListeners(); this.updateMessageStatus('idle'); } sendCompositeFeedback() { if (!this.elements.feedbackInput) return false; const text = this.elements.feedbackInput.innerText.trim(); const images = getAttachedImageData(); if (!text && images.length === 0) { this.showMessage(i18nService.t('feedback.messages.enterFeedback')); this.updateMessageStatus('idle'); return false; } this.updateMessageStatus('sending'); // 立即清空输入和预览 const feedbackData = { text, images: [...images] }; this.elements.feedbackInput.innerText = ''; imageHandler.clearPreview(); this.updateMessageStatus('waiting'); this.disableFeedbackInput(); const success = webSocketService.sendFeedback(feedbackData.text, feedbackData.images); if (success) { this.showMessage(i18nService.t('feedback.messages.feedbackSent')); eventBus.emit(APP_EVENTS.FEEDBACK_SEND, feedbackData); } else { this.showMessage(i18nService.t('feedback.messages.sendFailed')); this.updateMessageStatus('idle'); this.enableFeedbackInput(); eventBus.emit(APP_EVENTS.FEEDBACK_ERROR, { error: i18nService.t('feedback.messages.sendFailed') }); } return success; } disableFeedbackInput() { if (this.elements.feedbackInput && this.elements.dropZone) { this.elements.feedbackInput.contentEditable = 'false'; this.elements.feedbackInput.classList.add('disabled'); this.elements.dropZone.classList.add('disabled'); } this.isEnabled = false; } resetAndEnableInput() { if (this.elements.feedbackInput) { this.elements.feedbackInput.innerHTML = ''; imageHandler.clearPreview(); // 清空图片预览 } this.enableFeedbackInput(); this.updateMessageStatus('idle'); } enableFeedbackInput() { if (this.elements.feedbackInput && this.elements.dropZone) { this.elements.feedbackInput.contentEditable = 'true'; this.elements.feedbackInput.classList.remove('disabled'); this.elements.dropZone.classList.remove('disabled'); } this.isEnabled = true; } initializeElements() { this.elements = { feedbackInput: getElementById('feedback-input') || undefined, resultsDiv: getElementById('results') || undefined, dropZone: getElementById('drop-zone') || undefined, sendButton: getElementById('send-feedback-btn') || undefined, }; } setupEventListeners() { // 发送按钮点击事件 if (this.elements.sendButton) { this.elements.sendButton.addEventListener('click', () => { this.sendCompositeFeedback(); }); } // 监听来自AI的新消息,以重置和重新启用输入 eventBus.on(APP_EVENTS.FEEDBACK_SEND, this.resetAndEnableInput.bind(this)); // Ctrl+Enter 快捷键 if (this.elements.feedbackInput) { this.elements.feedbackInput.addEventListener('keydown', (event) => { if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') { event.preventDefault(); this.sendCompositeFeedback(); } }); } // 页面卸载前提示 window.addEventListener('beforeunload', (e) => { if (this.hasUnsavedContent()) { e.preventDefault(); e.returnValue = i18nService.t('feedback.messages.unsavedContent'); return e.returnValue; } }); } updateMessageStatus(status) { if (window.statusBar) { window.statusBar.updateMessageStatus(status); } } showMessage(message) { if (this.elements.resultsDiv) { this.elements.resultsDiv.textContent = message; } } hasUnsavedContent() { const hasText = (this.elements.feedbackInput?.innerText.trim().length || 0) > 0; const hasImages = getAttachedImageData().length > 0; return hasText || hasImages; } } export const feedbackComponent = new FeedbackComponent(); // 导出向后兼容的函数 export const sendCompositeFeedback = feedbackComponent.sendCompositeFeedback.bind(feedbackComponent); export const enableFeedbackInput = feedbackComponent.enableFeedbackInput.bind(feedbackComponent); export const disableFeedbackInput = feedbackComponent.disableFeedbackInput.bind(feedbackComponent); //# sourceMappingURL=feedback.js.map