// 键盘输入处理类
class KeyboardInputHandler {
constructor() {
this.onCommand = null; // 命令回调函数
this.commandHistory = [];
this.maxHistorySize = 20;
this.isPanelCollapsed = false;
this.isActive = false; // 面板激活状态
// 拖拽相关属性
this.isDragging = false;
this.dragOffset = { x: 0, y: 0 };
this.currentPosition = { x: null, y: null };
this.init();
}
init() {
this.setupEventListeners();
this.updatePanel();
this.setActiveState(false); // 初始为非激活状态
this.setPanelCollapsed(true); // 默认折叠面板
console.log('键盘输入处理器已初始化');
}
setupEventListeners() {
// 输入框事件
const commandInput = document.getElementById('commandInput');
const sendBtn = document.getElementById('sendCommandBtn');
const toggleBtn = document.getElementById('toggleKeyboardPanel');
if (commandInput) {
// 回车发送命令
commandInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.sendCommand();
}
});
// 历史记录导航(上下键)
commandInput.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
e.preventDefault();
this.navigateHistory(-1);
} else if (e.key === 'ArrowDown') {
e.preventDefault();
this.navigateHistory(1);
}
});
}
// 发送按钮
if (sendBtn) {
sendBtn.addEventListener('click', () => {
this.sendCommand();
});
}
// 面板折叠按钮
if (toggleBtn) {
toggleBtn.addEventListener('click', () => {
this.togglePanel();
});
}
// 快捷指令按钮
const quickCmdBtns = document.querySelectorAll('.quick-cmd');
quickCmdBtns.forEach(btn => {
btn.addEventListener('click', () => {
const command = btn.getAttribute('data-command');
if (command) {
this.executeCommand(command);
}
});
});
// 全局键盘快捷键
document.addEventListener('keydown', (e) => {
this.handleGlobalShortcuts(e);
});
// 历史记录点击事件
this.setupHistoryListener();
// 拖拽功能
this.setupDragFunctionality();
}
setupHistoryListener() {
const historyList = document.getElementById('commandHistory');
if (historyList) {
historyList.addEventListener('click', (e) => {
if (e.target.classList.contains('history-item') ||
e.target.parentElement.classList.contains('history-item')) {
const historyItem = e.target.classList.contains('history-item')
? e.target
: e.target.parentElement;
const command = historyItem.querySelector('.history-command').textContent;
if (command) {
this.fillInputWithCommand(command);
}
}
});
}
}
handleGlobalShortcuts(e) {
// F9 由 main.js 处理,这里不再处理
// if (e.key === 'F9') {
// e.preventDefault();
// this.toggleActiveState();
// return;
// }
// 只在面板激活且没有焦点在输入框时处理全局快捷键
if (!this.isActive || document.activeElement.tagName === 'INPUT') {
return;
}
// Ctrl/Cmd + K 聚焦到输入框
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
this.focusInput();
}
// Ctrl/Cmd + ` 切换面板
if ((e.ctrlKey || e.metaKey) && e.key === '`') {
e.preventDefault();
this.togglePanel();
}
// ESC 清空输入框、取消激活或折叠面板
if (e.key === 'Escape') {
const input = document.getElementById('commandInput');
if (input && input.value) {
input.value = '';
input.blur();
} else if (this.isActive) {
this.setActiveState(false);
} else {
this.togglePanel();
}
}
// 数字键快速命令(1-6) - 仅在激活状态下工作
if (this.isActive && e.key >= '1' && e.key <= '6') {
const quickCommands = [
'cook cook cook pot', // 1
'stop stop stop pot', // 2
'apple', // 3
'banana', // 4
'pizza', // 5
'cake' // 6
];
const commandIndex = parseInt(e.key) - 1;
if (commandIndex < quickCommands.length) {
e.preventDefault();
this.executeCommand(quickCommands[commandIndex]);
}
}
}
sendCommand() {
const input = document.getElementById('commandInput');
if (!input || !this.isActive) return;
const command = input.value.trim();
if (!command) return;
this.executeCommand(command);
input.value = '';
}
executeCommand(command) {
// 添加到历史记录
this.addToHistory(command);
// 显示调试信息
console.log(`[键盘输入] 执行命令: "${command}"`);
// 触发命令回调
if (this.onCommand) {
this.onCommand(command);
}
// 更新界面
this.updatePanel();
this.showCommandFeedback(command);
}
addToHistory(command) {
// 避免重复的连续命令
if (this.commandHistory.length > 0 &&
this.commandHistory[this.commandHistory.length - 1].command === command) {
return;
}
const historyItem = {
command: command,
timestamp: new Date().toLocaleTimeString(),
fullTimestamp: Date.now()
};
this.commandHistory.push(historyItem);
// 限制历史记录大小
if (this.commandHistory.length > this.maxHistorySize) {
this.commandHistory.shift();
}
this.updateHistoryDisplay();
}
navigateHistory(direction) {
// 实现历史记录导航功能
const input = document.getElementById('commandInput');
if (!input || this.commandHistory.length === 0) return;
if (!this.historyIndex) {
this.historyIndex = this.commandHistory.length;
}
this.historyIndex += direction;
if (this.historyIndex < 0) {
this.historyIndex = 0;
} else if (this.historyIndex >= this.commandHistory.length) {
this.historyIndex = this.commandHistory.length;
input.value = '';
return;
}
input.value = this.commandHistory[this.historyIndex].command;
}
fillInputWithCommand(command) {
const input = document.getElementById('commandInput');
if (input) {
input.value = command;
input.focus();
}
}
updateHistoryDisplay() {
const historyList = document.getElementById('commandHistory');
if (!historyList) return;
if (this.commandHistory.length === 0) {
historyList.innerHTML = '
暂无命令历史
';
return;
}
const historyHTML = this.commandHistory
.slice(-10) // 只显示最近10条
.reverse()
.map(item => `
${this.escapeHtml(item.command)}
${item.timestamp}
`).join('');
historyList.innerHTML = historyHTML;
}
showCommandFeedback(command) {
// 创建命令反馈动画
const panel = document.getElementById('keyboardInputPanel');
if (!panel) return;
// 添加闪烁效果
panel.style.borderColor = '#ff6b6b';
setTimeout(() => {
panel.style.borderColor = '#4ecdc4';
}, 200);
// 在面板标题旁显示最后执行的命令
const header = panel.querySelector('.input-header h4');
if (header) {
const originalText = header.textContent;
header.textContent = `🎮 执行: ${command}`;
header.style.color = '#ff6b6b';
setTimeout(() => {
header.textContent = originalText;
header.style.color = 'white';
}, 1500);
}
}
togglePanel() {
this.setPanelCollapsed(!this.isPanelCollapsed);
}
setPanelCollapsed(collapsed) {
const panel = document.getElementById('keyboardInputPanel');
const content = document.getElementById('keyboardInputContent');
const toggleBtn = document.getElementById('toggleKeyboardPanel');
if (!panel || !content || !toggleBtn) return;
this.isPanelCollapsed = collapsed;
if (this.isPanelCollapsed) {
// 完全隐藏面板
panel.style.display = 'none';
panel.classList.add('collapsed');
} else {
// 显示面板
panel.style.display = 'block';
panel.classList.remove('collapsed');
content.style.display = 'block';
toggleBtn.textContent = '⌨️';
toggleBtn.title = '折叠键盘输入面板';
}
console.log(`键盘输入面板${this.isPanelCollapsed ? '已隐藏' : '已显示'}`);
}
focusInput() {
const input = document.getElementById('commandInput');
if (input) {
// 如果面板未激活,先激活
if (!this.isActive) {
this.setActiveState(true);
}
// 如果面板是折叠的,先展开
if (this.isPanelCollapsed) {
this.setPanelCollapsed(false);
}
setTimeout(() => {
input.focus();
}, 100);
}
}
updatePanel() {
this.updateHistoryDisplay();
// 更新快捷指令状态
const quickCmdBtns = document.querySelectorAll('.quick-cmd');
quickCmdBtns.forEach(btn => {
btn.addEventListener('mouseenter', () => {
const command = btn.getAttribute('data-command');
btn.title = `点击执行: ${command}`;
});
});
}
// 工具方法
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 设置面板激活状态
setActiveState(active) {
console.log(`setActiveState called with: ${active}`);
this.isActive = active;
const panel = document.getElementById('keyboardInputPanel');
if (!panel) {
console.log('keyboardInputPanel not found!');
return;
}
panel.classList.remove('active', 'inactive');
if (active) {
panel.classList.add('active');
console.log('Panel activated - adding active class');
this.showActivationIndicator('面板已激活 - 可以输入命令');
} else {
panel.classList.add('inactive');
console.log('Panel deactivated - adding inactive class');
this.showActivationIndicator('面板未激活 - 按F9激活');
}
console.log(`键盘输入面板${active ? '已激活' : '已取消激活'}`);
console.log('Panel classes:', panel.className);
}
// 切换面板激活状态
toggleActiveState() {
console.log('toggleActiveState called, current state:', this.isActive);
this.setActiveState(!this.isActive);
console.log('toggleActiveState completed, new state:', this.isActive);
}
// 显示激活状态指示器
showActivationIndicator(message) {
// 移除之前的指示器
const existingIndicator = document.querySelector('.activation-indicator');
if (existingIndicator) {
existingIndicator.remove();
}
// 创建新的指示器
const indicator = document.createElement('div');
indicator.className = 'activation-indicator';
indicator.textContent = message;
indicator.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: ${this.isActive ? '#4ecdc4' : '#6c757d'};
color: white;
padding: 15px 25px;
border-radius: 25px;
font-family: 'Comic Neue', cursive;
font-weight: bold;
font-size: 16px;
z-index: 15000;
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
animation: activationPulse 0.5s ease-out;
`;
document.body.appendChild(indicator);
// 自动移除
setTimeout(() => {
if (indicator.parentNode) {
indicator.style.animation = 'activationFadeOut 0.3s ease-out forwards';
setTimeout(() => {
if (indicator.parentNode) {
indicator.parentNode.removeChild(indicator);
}
}, 300);
}
}, 2000);
}
// 获取统计信息
getStats() {
return {
totalCommands: this.commandHistory.length,
isCollapsed: this.isPanelCollapsed,
isActive: this.isActive,
lastCommand: this.commandHistory.length > 0
? this.commandHistory[this.commandHistory.length - 1].command
: null
};
}
// 清空历史记录
clearHistory() {
this.commandHistory = [];
this.updateHistoryDisplay();
console.log('命令历史已清空');
}
// 导出历史记录
exportHistory() {
const data = {
exported: new Date().toISOString(),
commands: this.commandHistory
};
const blob = new Blob([JSON.stringify(data, null, 2)],
{ type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `magicpot-commands-${Date.now()}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
console.log('命令历史已导出');
}
// 获取命令提示
getCommandSuggestions(input) {
const suggestions = [
'cook cook cook pot',
'stop stop stop pot',
'apple', 'banana', 'orange', 'strawberry', 'watermelon', 'grape',
'pizza', 'burger', 'bread', 'rice', 'noodles', 'cake', 'cookie',
'cheese', 'fish', 'chicken', 'carrot', 'tomato', 'corn', 'broccoli'
];
if (!input) return suggestions.slice(0, 5);
const filtered = suggestions.filter(cmd =>
cmd.toLowerCase().includes(input.toLowerCase())
);
return filtered.slice(0, 8);
}
// 设置拖拽功能
setupDragFunctionality() {
const panel = document.getElementById('keyboardInputPanel');
const header = panel?.querySelector('.input-header');
if (!panel || !header) return;
// 鼠标按下开始拖拽
header.addEventListener('mousedown', (e) => {
if (!this.isActive) return; // 只有激活状态下才能拖拽
this.isDragging = true;
panel.classList.add('dragging');
const rect = panel.getBoundingClientRect();
this.dragOffset.x = e.clientX - rect.left;
this.dragOffset.y = e.clientY - rect.top;
// 记录当前位置
this.currentPosition.x = rect.left;
this.currentPosition.y = rect.top;
e.preventDefault();
});
// 全局鼠标移动事件
document.addEventListener('mousemove', (e) => {
if (!this.isDragging) return;
e.preventDefault();
// 计算新位置
let newX = e.clientX - this.dragOffset.x;
let newY = e.clientY - this.dragOffset.y;
// 边界检查
const rect = panel.getBoundingClientRect();
const maxX = window.innerWidth - rect.width;
const maxY = window.innerHeight - rect.height;
newX = Math.max(0, Math.min(newX, maxX));
newY = Math.max(0, Math.min(newY, maxY));
// 应用新位置
panel.style.left = newX + 'px';
panel.style.top = newY + 'px';
panel.style.right = 'auto';
panel.style.bottom = 'auto';
this.currentPosition.x = newX;
this.currentPosition.y = newY;
});
// 全局鼠标松开事件
document.addEventListener('mouseup', (e) => {
if (!this.isDragging) return;
this.isDragging = false;
panel.classList.remove('dragging');
// 保存位置到localStorage
this.savePosition();
});
// 加载保存的位置
this.loadPosition();
}
// 保存面板位置
savePosition() {
if (this.currentPosition.x !== null && this.currentPosition.y !== null) {
const position = {
x: this.currentPosition.x,
y: this.currentPosition.y
};
localStorage.setItem('keyboardPanelPosition', JSON.stringify(position));
}
}
// 加载保存的位置
loadPosition() {
const panel = document.getElementById('keyboardInputPanel');
if (!panel) return;
try {
const savedPosition = localStorage.getItem('keyboardPanelPosition');
if (savedPosition) {
const position = JSON.parse(savedPosition);
// 检查位置是否在屏幕范围内
const rect = panel.getBoundingClientRect();
const maxX = window.innerWidth - rect.width;
const maxY = window.innerHeight - rect.height;
if (position.x >= 0 && position.x <= maxX &&
position.y >= 0 && position.y <= maxY) {
panel.style.left = position.x + 'px';
panel.style.top = position.y + 'px';
panel.style.right = 'auto';
panel.style.bottom = 'auto';
this.currentPosition.x = position.x;
this.currentPosition.y = position.y;
}
}
} catch (error) {
console.warn('加载面板位置失败:', error);
}
}
// 重置面板位置到右下角
resetPosition() {
const panel = document.getElementById('keyboardInputPanel');
if (!panel) return;
panel.style.left = 'auto';
panel.style.top = 'auto';
panel.style.right = '20px';
panel.style.bottom = '20px';
this.currentPosition.x = null;
this.currentPosition.y = null;
// 清除保存的位置
localStorage.removeItem('keyboardPanelPosition');
console.log('面板位置已重置到右下角');
}
}
// 添加快捷键提示样式
const keyboardStyle = document.createElement('style');
keyboardStyle.textContent = `
.keyboard-shortcuts-hint {
position: absolute;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 5px 10px;
border-radius: 10px;
font-size: 0.7em;
white-space: nowrap;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
font-family: 'Comic Neue', cursive;
}
.keyboard-input-panel:hover .keyboard-shortcuts-hint {
opacity: 1;
}
.command-input:focus + .keyboard-shortcuts-hint {
opacity: 1;
}
@keyframes activationPulse {
0% {
opacity: 0;
transform: translate(-50%, -50%) scale(0.8);
}
50% {
transform: translate(-50%, -50%) scale(1.1);
}
100% {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
@keyframes activationFadeOut {
from {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
to {
opacity: 0;
transform: translate(-50%, -50%) scale(0.9);
}
}
`;
document.head.appendChild(keyboardStyle);