feat: v0.3.0 版本 - SSH终端、SFTP、端口转发功能实现

核心功能:
- SSH终端: 密码/密钥认证、XTerm.js终端、多主题配色、IP高亮
- SFTP管理: 双栏文件管理、上传下载、传输队列
- 端口转发: 本地/远程转发、SOCKS5动态代理、规则管理

架构改进:
- 拆分大型组件 (SFTPView/TabBar/Sidebar)
- 添加统一日志和错误处理
- 修复React严格模式下的终端黑屏问题

UI更新:
- 新增端口转发视图
- 更新UI预览文档
- 左侧栏右键菜单支持SFTP快捷打开
This commit is contained in:
Logan.Li committed 2026-04-06 01:20:47 +08:00
1 parent cf1c054381
commit 484d850abd
105 files changed
+25330 -64

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
/**
* ContextMenu - 右键菜单组件
*/
import { useEffect, useRef } from 'react'
export interface MenuItem {
icon: React.ReactNode
label: string
action: () => void
danger?: boolean
}
interface ContextMenuProps {
x: number
y: number
items: MenuItem[]
onClose: () => void
}
export function ContextMenu({ x, y, items, onClose }: ContextMenuProps) {
const menuRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const handleClick = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
onClose()
}
}
const handleKeydown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose()
}
document.addEventListener('mousedown', handleClick)
document.addEventListener('keydown', handleKeydown)
return () => {
document.removeEventListener('mousedown', handleClick)
document.removeEventListener('keydown', handleKeydown)
}
}, [onClose])
return (
<div
ref={menuRef}
className="fixed z-50 bg-dark-700 border border-slate-700 rounded-lg shadow-xl py-1 min-w-[180px]"
style={{ left: Math.min(x, window.innerWidth - 200), top: Math.min(y, window.innerHeight - 300) }}
>
{items.map((item, index) => (
<button
key={index}
onClick={() => {
item.action()
onClose()
}}
className={`w-full flex items-center gap-3 px-4 py-2 text-sm transition-colors ${
item.danger ? 'text-red-400 hover:bg-red-500/20' : 'text-gray-200 hover:bg-slate-700 hover:text-white'
}`}
>
{item.icon}
<span>{item.label}</span>
</button>
))}
</div>
)
}