import React, { useState, useEffect, useRef, useMemo } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { FiTerminal, FiX, FiCommand, FiFolder, FiFile, FiServer, FiDatabase, FiPackage, FiSettings, FiSearch } from 'react-icons/fi'; // 常用命令库 const COMMAND_DATABASE = [ // 文件操作 { cmd: 'ls', desc: '列出目录内容', category: 'file' }, { cmd: 'ls -la', desc: '详细列出所有文件', category: 'file' }, { cmd: 'ls -lh', desc: '人类可读格式', category: 'file' }, { cmd: 'cd', desc: '切换目录', category: 'file' }, { cmd: 'cd ..', desc: '返回上级目录', category: 'file' }, { cmd: 'cd ~', desc: '返回主目录', category: 'file' }, { cmd: 'cd -', desc: '返回上次目录', category: 'file' }, { cmd: 'pwd', desc: '显示当前目录', category: 'file' }, { cmd: 'mkdir', desc: '创建目录', category: 'file' }, { cmd: 'mkdir -p', desc: '递归创建目录', category: 'file' }, { cmd: 'rm', desc: '删除文件', category: 'file' }, { cmd: 'rm -rf', desc: '强制递归删除', category: 'file' }, { cmd: 'cp', desc: '复制文件', category: 'file' }, { cmd: 'cp -r', desc: '递归复制目录', category: 'file' }, { cmd: 'mv', desc: '移动/重命名', category: 'file' }, { cmd: 'touch', desc: '创建空文件', category: 'file' }, { cmd: 'cat', desc: '查看文件内容', category: 'file' }, { cmd: 'head -n 20', desc: '查看前20行', category: 'file' }, { cmd: 'tail -f', desc: '实时追踪日志', category: 'file' }, { cmd: 'less', desc: '分页查看文件', category: 'file' }, { cmd: 'find . -name', desc: '按名称查找', category: 'file' }, { cmd: 'chmod 755', desc: '设置可执行权限', category: 'file' }, { cmd: 'chown', desc: '修改所有者', category: 'file' }, { cmd: 'tar -zxvf', desc: '解压 tar.gz', category: 'file' }, { cmd: 'tar -zcvf', desc: '压缩为 tar.gz', category: 'file' }, { cmd: 'unzip', desc: '解压 zip', category: 'file' }, // 文本处理 { cmd: 'grep', desc: '文本搜索', category: 'text' }, { cmd: 'grep -rn', desc: '递归搜索带行号', category: 'text' }, { cmd: 'sed', desc: '流编辑器', category: 'text' }, { cmd: 'awk', desc: '文本处理', category: 'text' }, { cmd: 'sort', desc: '排序', category: 'text' }, { cmd: 'uniq -c', desc: '去重并计数', category: 'text' }, { cmd: 'wc -l', desc: '统计行数', category: 'text' }, // 系统 { cmd: 'top', desc: '系统监控', category: 'system' }, { cmd: 'htop', desc: '增强版 top', category: 'system' }, { cmd: 'ps aux', desc: '查看所有进程', category: 'system' }, { cmd: 'kill -9', desc: '强制终止进程', category: 'system' }, { cmd: 'df -h', desc: '磁盘使用情况', category: 'system' }, { cmd: 'du -sh *', desc: '当前目录各项大小', category: 'system' }, { cmd: 'free -h', desc: '内存使用情况', category: 'system' }, { cmd: 'uptime', desc: '运行时间', category: 'system' }, { cmd: 'uname -a', desc: '系统信息', category: 'system' }, { cmd: 'whoami', desc: '当前用户', category: 'system' }, // 网络 { cmd: 'ping -c 4', desc: '测试连通性', category: 'network' }, { cmd: 'curl -I', desc: '获取响应头', category: 'network' }, { cmd: 'wget', desc: '下载文件', category: 'network' }, { cmd: 'netstat -tunlp', desc: '监听端口', category: 'network' }, { cmd: 'ss -tunlp', desc: '套接字统计', category: 'network' }, { cmd: 'ip addr', desc: 'IP 地址', category: 'network' }, // 包管理 { cmd: 'apt update', desc: '更新软件源', category: 'package' }, { cmd: 'apt install', desc: '安装软件', category: 'package' }, { cmd: 'yum install', desc: 'RHEL 安装', category: 'package' }, { cmd: 'npm install', desc: 'Node 安装', category: 'package' }, { cmd: 'pip install', desc: 'Python 安装', category: 'package' }, // 服务 { cmd: 'systemctl status', desc: '服务状态', category: 'service' }, { cmd: 'systemctl start', desc: '启动服务', category: 'service' }, { cmd: 'systemctl stop', desc: '停止服务', category: 'service' }, { cmd: 'systemctl restart', desc: '重启服务', category: 'service' }, { cmd: 'journalctl -f', desc: '实时日志', category: 'service' }, // Docker { cmd: 'docker ps', desc: '运行中容器', category: 'docker' }, { cmd: 'docker ps -a', desc: '所有容器', category: 'docker' }, { cmd: 'docker images', desc: '镜像列表', category: 'docker' }, { cmd: 'docker exec -it', desc: '进入容器', category: 'docker' }, { cmd: 'docker logs -f', desc: '查看日志', category: 'docker' }, { cmd: 'docker-compose up -d', desc: '启动服务', category: 'docker' }, { cmd: 'docker-compose down', desc: '停止服务', category: 'docker' }, // Git { cmd: 'git status', desc: '查看状态', category: 'git' }, { cmd: 'git add .', desc: '添加所有文件', category: 'git' }, { cmd: 'git commit -m', desc: '提交', category: 'git' }, { cmd: 'git push', desc: '推送', category: 'git' }, { cmd: 'git pull', desc: '拉取', category: 'git' }, { cmd: 'git log --oneline', desc: '简洁日志', category: 'git' }, // 其他 { cmd: 'clear', desc: '清屏', category: 'other' }, { cmd: 'history', desc: '命令历史', category: 'other' }, { cmd: 'vim', desc: '文本编辑器', category: 'other' }, { cmd: 'nano', desc: '简易编辑器', category: 'other' }, { cmd: 'exit', desc: '退出终端', category: 'other' }, ]; // 获取分类图标 const getCategoryIcon = (category) => { switch (category) { case 'file': return FiFolder; case 'text': return FiFile; case 'system': return FiServer; case 'network': return FiDatabase; case 'package': return FiPackage; case 'service': return FiSettings; case 'docker': return FiPackage; case 'git': return FiSearch; default: return FiTerminal; } }; function CommandSuggestPanel({ visible, input, onSelect, onClose, onOpenCommandPalette, disabled }) { const [selectedIndex, setSelectedIndex] = useState(0); const [searchText, setSearchText] = useState(''); const listRef = useRef(null); const selectedRef = useRef(null); // 使用搜索框或输入内容过滤 const filterText = searchText || input || ''; // 过滤命令 const suggestions = useMemo(() => { if (!filterText) return COMMAND_DATABASE.slice(0, 20); const query = filterText.toLowerCase().trim(); return COMMAND_DATABASE.filter(item => item.cmd.toLowerCase().includes(query) || item.desc.toLowerCase().includes(query) ).slice(0, 20); }, [filterText]); // 重置选中索引 useEffect(() => { setSelectedIndex(0); }, [filterText]); // 滚动到选中项 useEffect(() => { if (selectedRef.current && listRef.current) { selectedRef.current.scrollIntoView({ block: 'nearest' }); } }, [selectedIndex]); // 键盘导航 useEffect(() => { if (!visible) return; const handleKeyDown = (e) => { if (e.key === 'ArrowDown') { e.preventDefault(); setSelectedIndex(prev => (prev + 1) % suggestions.length); } else if (e.key === 'ArrowUp') { e.preventDefault(); setSelectedIndex(prev => (prev - 1 + suggestions.length) % suggestions.length); } else if (e.key === 'Tab' && suggestions[selectedIndex]) { e.preventDefault(); onSelect(suggestions[selectedIndex].cmd); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [visible, suggestions, selectedIndex, onSelect]); // 同步输入到搜索框 useEffect(() => { if (input && !searchText) { // 如果有终端输入且搜索框为空,显示终端输入的内容 } }, [input, searchText]); if (!visible) return null; return ( {/* 头部 */}
SUGGEST
{/* 搜索框 */}
setSearchText(e.target.value)} placeholder={input ? `当前: ${input}` : "搜索命令..."} className="w-full pl-9 pr-3 py-2 bg-shell-card/50 border border-shell-border/50 rounded-lg text-sm text-shell-text placeholder-shell-text-dim focus:outline-none focus:border-shell-accent/50 transition-colors" />
{/* 命令列表 */}
{disabled ? (
请先连接服务器
) : suggestions.length === 0 ? (
未找到匹配的命令
) : ( suggestions.map((item, index) => { const Icon = getCategoryIcon(item.category); const isSelected = index === selectedIndex; return (
!disabled && onSelect(item.cmd)} className={` px-3 py-2 cursor-pointer flex items-center gap-3 transition-all border-l-2 ${isSelected ? 'bg-shell-accent/10 border-shell-accent' : 'border-transparent hover:bg-shell-card/30' } ${disabled ? 'opacity-50 cursor-not-allowed' : ''} `} >
{item.cmd}
{item.desc}
); }) )}
{/* 底部按钮 */}
↑↓ 导航 · Tab 补全 · 点击选择
); } export default CommandSuggestPanel;