实现可执行文件检测和运行功能:支持终端集成和右键菜单操作

主要更新:
- 后端添加可执行文件检测逻辑,支持脚本、二进制文件和权限检测
- 文件列表API返回executable字段标识可执行文件
- 右键菜单新增"打开终端"和"运行"功能
- 终端组件支持初始路径和自动执行命令
- 创建TerminalWindow组件提供完整窗口体验
- 运行功能通过终端执行,支持实时输出和交互

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZacharyZcR
2025-09-17 01:17:00 +08:00
parent e0e4e69159
commit f70a2102db
7 changed files with 428 additions and 6 deletions

View File

@@ -14,7 +14,9 @@ import {
Clipboard,
Eye,
Share,
ExternalLink
ExternalLink,
Terminal,
Play
} from "lucide-react";
import { useTranslation } from "react-i18next";
@@ -27,6 +29,7 @@ interface FileItem {
permissions?: string;
owner?: string;
group?: string;
executable?: boolean;
}
interface ContextMenuProps {
@@ -49,6 +52,9 @@ interface ContextMenuProps {
onPreview?: (file: FileItem) => void;
hasClipboard?: boolean;
onDragToDesktop?: () => void;
onOpenTerminal?: (path: string) => void;
onRunExecutable?: (file: FileItem) => void;
currentPath?: string;
}
interface MenuItem {
@@ -80,7 +86,10 @@ export function FileManagerContextMenu({
onPaste,
onPreview,
hasClipboard = false,
onDragToDesktop
onDragToDesktop,
onOpenTerminal,
onRunExecutable,
currentPath
}: ContextMenuProps) {
const { t } = useTranslation();
const [menuPosition, setMenuPosition] = useState({ x, y });
@@ -181,12 +190,42 @@ export function FileManagerContextMenu({
const isMultipleFiles = files.length > 1;
const hasFiles = files.some(f => f.type === 'file');
const hasDirectories = files.some(f => f.type === 'directory');
const hasExecutableFiles = files.some(f => f.type === 'file' && f.executable);
// 构建菜单项
const menuItems: MenuItem[] = [];
if (isFileContext) {
// 文件/文件夹选中时的菜单
// 打开终端功能 - 支持文件和文件夹
if (onOpenTerminal) {
const targetPath = isSingleFile
? (files[0].type === 'directory' ? files[0].path : files[0].path.substring(0, files[0].path.lastIndexOf('/')))
: files[0].path.substring(0, files[0].path.lastIndexOf('/'));
menuItems.push({
icon: <Terminal className="w-4 h-4" />,
label: files[0].type === 'directory' ? "在此文件夹打开终端" : "在文件位置打开终端",
action: () => onOpenTerminal(targetPath),
shortcut: "Ctrl+T"
});
}
// 运行可执行文件功能 - 仅对单个可执行文件显示
if (isSingleFile && hasExecutableFiles && onRunExecutable) {
menuItems.push({
icon: <Play className="w-4 h-4" />,
label: "运行",
action: () => onRunExecutable(files[0]),
shortcut: "Enter"
});
}
if ((onOpenTerminal || (isSingleFile && hasExecutableFiles && onRunExecutable))) {
menuItems.push({ separator: true } as MenuItem);
}
if (hasFiles && onPreview) {
menuItems.push({
icon: <Eye className="w-4 h-4" />,
@@ -278,6 +317,19 @@ export function FileManagerContextMenu({
}
} else {
// 空白区域右键菜单
// 在当前目录打开终端
if (onOpenTerminal && currentPath) {
menuItems.push({
icon: <Terminal className="w-4 h-4" />,
label: "在此处打开终端",
action: () => onOpenTerminal(currentPath),
shortcut: "Ctrl+T"
});
menuItems.push({ separator: true } as MenuItem);
}
if (onUpload) {
menuItems.push({
icon: <Upload className="w-4 h-4" />,