Files
bella/chatInterface.js
你的名字 7f6cf5c40c feat(chat): add chat interface and integrate with BellaAI core
- Implement chat interface with toggle functionality
- Add chat control panel to main UI
- Integrate chat with BellaAI for message processing
- Include test chat interface for debugging
- Add styling for chat components
- Support both local and cloud AI providers
2025-07-24 10:12:34 +08:00

441 lines
15 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// chatInterface.js - 贝拉的聊天界面组件
// 这个模块负责创建和管理优雅的聊天界面,体现贝拉的温暖个性
class ChatInterface {
constructor() {
this.isVisible = false;
this.messages = [];
this.maxMessages = 50; // 最多显示50条消息
this.chatContainer = null;
this.messageContainer = null;
this.inputContainer = null;
this.messageInput = null;
this.sendButton = null;
this.toggleButton = null;
this.settingsPanel = null;
this.isSettingsVisible = false;
this.init();
}
// 初始化聊天界面
init() {
this.createChatContainer();
this.createToggleButton();
this.createSettingsPanel();
this.bindEvents();
this.addWelcomeMessage();
}
// 创建聊天容器
createChatContainer() {
// 主聊天容器
this.chatContainer = document.createElement('div');
this.chatContainer.className = 'bella-chat-container';
this.chatContainer.innerHTML = `
<div class="bella-chat-header">
<div class="bella-chat-title">
<div class="bella-avatar">💝</div>
<div class="bella-title-text">
<h3>贝拉</h3>
<span class="bella-status">在线</span>
</div>
</div>
<div class="bella-chat-controls">
<button class="bella-settings-btn" title="设置">
<i class="fas fa-cog"></i>
</button>
<button class="bella-minimize-btn" title="最小化">
<i class="fas fa-minus"></i>
</button>
</div>
</div>
<div class="bella-chat-messages"></div>
<div class="bella-chat-input-container">
<div class="bella-input-wrapper">
<input type="text" class="bella-message-input" placeholder="和贝拉聊聊天..." maxlength="500">
<button class="bella-send-btn" title="发送">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div class="bella-input-hint">
按 Enter 发送Shift + Enter 换行
</div>
</div>
`;
// 获取关键元素引用
this.messageContainer = this.chatContainer.querySelector('.bella-chat-messages');
this.inputContainer = this.chatContainer.querySelector('.bella-chat-input-container');
this.messageInput = this.chatContainer.querySelector('.bella-message-input');
this.sendButton = this.chatContainer.querySelector('.bella-send-btn');
document.body.appendChild(this.chatContainer);
}
// 创建切换按钮
createToggleButton() {
this.toggleButton = document.createElement('button');
this.toggleButton.className = 'bella-chat-toggle';
this.toggleButton.innerHTML = `
<div class="bella-toggle-icon">
<i class="fas fa-comments"></i>
</div>
<div class="bella-toggle-text">与贝拉聊天</div>
`;
this.toggleButton.title = '打开聊天窗口';
document.body.appendChild(this.toggleButton);
}
// 创建设置面板
createSettingsPanel() {
this.settingsPanel = document.createElement('div');
this.settingsPanel.className = 'bella-settings-panel';
this.settingsPanel.innerHTML = `
<div class="bella-settings-header">
<h4>聊天设置</h4>
<button class="bella-settings-close">
<i class="fas fa-times"></i>
</button>
</div>
<div class="bella-settings-content">
<div class="bella-setting-group">
<label>AI服务提供商</label>
<select class="bella-provider-select">
<option value="local">本地模型</option>
<option value="openai">OpenAI GPT</option>
<option value="qwen">通义千问</option>
<option value="ernie">文心一言</option>
<option value="glm">智谱AI</option>
</select>
</div>
<div class="bella-setting-group bella-api-key-group" style="display: none;">
<label>API密钥</label>
<input type="password" class="bella-api-key-input" placeholder="请输入API密钥">
<button class="bella-api-key-save">保存</button>
</div>
<div class="bella-setting-group">
<label>聊天模式</label>
<select class="bella-mode-select">
<option value="casual">轻松聊天</option>
<option value="assistant">智能助手</option>
<option value="creative">创意伙伴</option>
</select>
</div>
<div class="bella-setting-group">
<button class="bella-clear-history">清除聊天记录</button>
</div>
</div>
`;
document.body.appendChild(this.settingsPanel);
}
// 绑定事件
bindEvents() {
// 切换聊天窗口
this.toggleButton.addEventListener('click', () => {
this.toggle();
});
// 最小化按钮
this.chatContainer.querySelector('.bella-minimize-btn').addEventListener('click', () => {
this.hide();
});
// 设置按钮
this.chatContainer.querySelector('.bella-settings-btn').addEventListener('click', () => {
this.toggleSettings();
});
// 发送消息
this.sendButton.addEventListener('click', () => {
this.sendMessage();
});
// 输入框事件
this.messageInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.sendMessage();
}
});
// 输入框自动调整高度
this.messageInput.addEventListener('input', () => {
this.adjustInputHeight();
});
// 设置面板事件
this.bindSettingsEvents();
}
// 绑定设置面板事件
bindSettingsEvents() {
// 关闭设置面板
this.settingsPanel.querySelector('.bella-settings-close').addEventListener('click', () => {
this.hideSettings();
});
// 提供商选择
const providerSelect = this.settingsPanel.querySelector('.bella-provider-select');
const apiKeyGroup = this.settingsPanel.querySelector('.bella-api-key-group');
providerSelect.addEventListener('change', (e) => {
const provider = e.target.value;
if (provider === 'local') {
apiKeyGroup.style.display = 'none';
} else {
apiKeyGroup.style.display = 'block';
}
// 触发提供商切换事件
this.onProviderChange?.(provider);
});
// API密钥保存
this.settingsPanel.querySelector('.bella-api-key-save').addEventListener('click', () => {
const provider = providerSelect.value;
const apiKey = this.settingsPanel.querySelector('.bella-api-key-input').value;
if (apiKey.trim()) {
this.onAPIKeySave?.(provider, apiKey.trim());
this.showNotification('API密钥已保存', 'success');
}
});
// 清除聊天记录
this.settingsPanel.querySelector('.bella-clear-history').addEventListener('click', () => {
this.clearMessages();
this.onClearHistory?.();
this.hideSettings();
});
}
// 添加欢迎消息
addWelcomeMessage() {
this.addMessage('assistant', '你好我是贝拉你的AI伙伴。很高兴见到你有什么想聊的吗', true);
}
// 切换聊天窗口显示/隐藏
toggle() {
console.log('ChatInterface.toggle() 被调用');
console.log('切换前 isVisible:', this.isVisible);
if (this.isVisible) {
this.hide();
} else {
this.show();
}
console.log('切换后 isVisible:', this.isVisible);
}
// 显示聊天窗口
show() {
console.log('ChatInterface.show() 被调用');
console.log('显示前 isVisible:', this.isVisible);
console.log('显示前 chatContainer.className:', this.chatContainer.className);
this.isVisible = true;
this.chatContainer.classList.add('visible');
console.log('显示后 isVisible:', this.isVisible);
console.log('显示后 chatContainer.className:', this.chatContainer.className);
console.log('chatContainer 计算样式 opacity:', window.getComputedStyle(this.chatContainer).opacity);
console.log('chatContainer 计算样式 transform:', window.getComputedStyle(this.chatContainer).transform);
this.toggleButton.classList.add('active');
this.messageInput.focus();
this.scrollToBottom();
}
// 隐藏聊天窗口
hide() {
this.isVisible = false;
this.chatContainer.classList.remove('visible');
this.toggleButton.classList.remove('active');
this.hideSettings();
}
// 切换设置面板
toggleSettings() {
if (this.isSettingsVisible) {
this.hideSettings();
} else {
this.showSettings();
}
}
// 显示设置面板
showSettings() {
this.isSettingsVisible = true;
this.settingsPanel.classList.add('visible');
}
// 隐藏设置面板
hideSettings() {
this.isSettingsVisible = false;
this.settingsPanel.classList.remove('visible');
}
// 发送消息
sendMessage() {
const text = this.messageInput.value.trim();
if (!text) return;
// 添加用户消息
this.addMessage('user', text);
// 清空输入框
this.messageInput.value = '';
this.adjustInputHeight();
// 触发消息发送事件
this.onMessageSend?.(text);
}
// 添加消息到聊天界面
addMessage(role, content, isWelcome = false) {
const messageElement = document.createElement('div');
messageElement.className = `bella-message bella-message-${role}`;
if (isWelcome) {
messageElement.classList.add('bella-welcome-message');
}
const timestamp = new Date().toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
});
messageElement.innerHTML = `
<div class="bella-message-avatar">
${role === 'user' ? '👤' : '💝'}
</div>
<div class="bella-message-content">
<div class="bella-message-text">${this.formatMessage(content)}</div>
<div class="bella-message-time">${timestamp}</div>
</div>
`;
this.messageContainer.appendChild(messageElement);
this.messages.push({ role, content, timestamp: Date.now() });
// 限制消息数量
if (this.messages.length > this.maxMessages) {
const oldMessage = this.messageContainer.firstChild;
if (oldMessage) {
this.messageContainer.removeChild(oldMessage);
}
this.messages.shift();
}
// 滚动到底部
this.scrollToBottom();
// 添加动画效果
setTimeout(() => {
messageElement.classList.add('bella-message-appear');
}, 10);
}
// 格式化消息内容
formatMessage(content) {
// 简单的文本格式化,支持换行
return content
.replace(/\n/g, '<br>')
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>');
}
// 显示打字指示器
showTypingIndicator() {
const existingIndicator = this.messageContainer.querySelector('.bella-typing-indicator');
if (existingIndicator) return;
const typingElement = document.createElement('div');
typingElement.className = 'bella-typing-indicator';
typingElement.innerHTML = `
<div class="bella-message-avatar">💝</div>
<div class="bella-message-content">
<div class="bella-typing-dots">
<span class="bella-typing-dot"></span>
<span class="bella-typing-dot"></span>
<span class="bella-typing-dot"></span>
</div>
</div>
`;
this.messageContainer.appendChild(typingElement);
this.scrollToBottom();
// 添加显示动画
setTimeout(() => {
typingElement.classList.add('bella-typing-show');
}, 10);
}
// 隐藏打字指示器
hideTypingIndicator() {
const indicator = this.messageContainer.querySelector('.bella-typing-indicator');
if (indicator) {
this.messageContainer.removeChild(indicator);
}
}
// 清除所有消息
clearMessages() {
this.messageContainer.innerHTML = '';
this.messages = [];
this.addWelcomeMessage();
}
// 滚动到底部
scrollToBottom() {
setTimeout(() => {
this.messageContainer.scrollTop = this.messageContainer.scrollHeight;
}, 10);
}
// 调整输入框高度
adjustInputHeight() {
this.messageInput.style.height = 'auto';
this.messageInput.style.height = Math.min(this.messageInput.scrollHeight, 120) + 'px';
}
// 显示通知
showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `bella-notification bella-notification-${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('bella-notification-show');
}, 10);
setTimeout(() => {
notification.classList.remove('bella-notification-show');
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
}
// 检查聊天窗口是否可见
getVisibility() {
return this.isVisible;
}
// 设置回调函数
onMessageSend = null;
onProviderChange = null;
onAPIKeySave = null;
onClearHistory = null;
}
// ES6模块导出
export { ChatInterface };