dynamic-interaction
Version:
Dynamic interaction 动态交互mcp,用于cursor、windsurf、trae 等 AI 智能编辑器 Agent 运行时交互使用
128 lines • 5.24 kB
JavaScript
/**
* 反馈组件
* 处理用户反馈的收集和发送
*/
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