<?php

/**
 * @id 7
 * @name 文件编辑器
 * @version 1.0.0
 * @description 提供文件浏览、编辑、保存、删除、重命名等多种功能，支持多种文件类型在线编辑，操作简单直观
 * @price 0
 * @type 工具
 * @website 
 * @width 800
 * @height 600
 * @thumbnail /img/id7.png
 */

// 插件配置参数
$plugin_config = <<<EOT
[config_params]
root_directory =
[/config_params]
EOT;
// 强制设置mbstring默认编码
mb_internal_encoding('UTF-8');
mb_http_output('UTF-8');

// 只在非AJAX请求时设置HTML Content-Type头
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {
    header('Content-Type: text/html; charset=utf-8');
}

// 定义根目录（使用实际的网站根目录）
$rootDir = realpath(__DIR__ . '/../..'); // 从admin/Plugin目录向上两级到网站根目录
// 确保路径使用正确的分隔符
// realpath已经会返回正确的路径分隔符，所以不需要额外替换
// $rootDir = str_replace(['/', '\'], DIRECTORY_SEPARATOR, $rootDir);
$currentDir = $rootDir;

// ========== 核心修复：兼容Windows中文路径的编码转换 ==========
/**
 * 转换UTF-8文件名为Windows系统可识别的编码（解决mb_convert_encoding失效问题）
 */
function winUtf8ToGb2312($str)
{
    if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') return $str;
    // 方案1：用iconv（优先）
    if (function_exists('iconv')) {
        return @iconv('UTF-8', 'GB2312//IGNORE', $str);
    }
    // 方案2：mb_convert_encoding兜底
    return mb_convert_encoding($str, 'GB2312', 'UTF-8');
}

/**
 * 转换Windows系统编码为UTF-8（用于显示）
 */
function winGb2312ToUtf8($str)
{
    if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') return $str;
    if (function_exists('iconv')) {
        return @iconv('GB2312', 'UTF-8//IGNORE', $str);
    }
    return mb_convert_encoding($str, 'UTF-8', 'GB2312');
}

/**
 * 安全拼接路径（统一用反斜杠，修复一级文件路径问题）
 * @param string $base 根目录
 * @param string $subPath 子路径（空=一级目录）
 * @param string $fileName 文件名（可选，空表示目录）
 * @return string 完整路径
 */
function buildWinPath($base, $subPath, $fileName = '')
{
    // 去掉路径前后的分隔符
    $base = rtrim($base, '\\/');
    $subPath = ltrim($subPath, '\\/');
    $fileName = ltrim($fileName, '\\/');

    // 构建基本路径
    $result = $base;
    if (!empty($subPath)) {
        $result .= '\\' . $subPath;
    }
    if (!empty($fileName)) {
        $result .= '\\' . $fileName;
    }
    return $result;
}

// 处理AJAX请求
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
    header('Content-Type: application/json; charset=utf-8');

    // 处理AJAX请求

    // 检查是否是JSON数据
    $request = $_POST;
    if (isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false) {
        $jsonData = json_decode(file_get_contents('php://input'), true);
        if ($jsonData !== null) {
            $request = $jsonData;
        }
    }

    // 安全检查：确保请求数据有效
    if (!is_array($request)) {
        echo json_encode(['success' => false, 'error' => '请求格式错误：无法解析请求数据']);
        exit;
    }

    switch ($request['action'] ?? '') {
        case 'open_file':
            $filePath = $request['path'] ?? ''; // UTF-8编码的路径
            $fileName = $request['name'] ?? ''; // UTF-8编码的文件名

            // 准备处理文件路径

            // 1. 转换为Windows系统编码
            $sysFilePath = winUtf8ToGb2312($filePath);
            $sysFileName = winUtf8ToGb2312($fileName);

            // 2. 拼接绝对路径（强制反斜杠）
            // 修复路径重复问题：检查$sysFilePath是否已经是完整的文件路径
            if (stripos($sysFilePath, $rootDir) === 0) {
                // 如果$sysFilePath已经包含$rootDir，检查它是否已经是完整的文件路径
                // 即检查路径是否指向一个实际存在的文件
                if (file_exists($sysFilePath) && !is_dir($sysFilePath)) {
                    // 已经是完整的文件路径，直接使用
                    $targetFile = $sysFilePath;
                } else {
                    // 是目录路径，需要拼接文件名
                    $targetFile = $sysFilePath . '\\' . ltrim($sysFileName, '\\/');
                }
            } else {
                // 检查$sysFilePath是否包含文件扩展名（简单判断）
                if (strpos($sysFilePath, '.') !== false) {
                    // 看起来已经是文件路径，直接拼接根目录
                    $targetFile = $rootDir . '\\' . ltrim($sysFilePath, '\\/');
                } else {
                    // 是目录路径，需要拼接文件名
                    $targetFile = buildWinPath($rootDir, $sysFilePath, $sysFileName);
                }
            }

            // 验证文件是否存在

            // 3. 宽松的文件存在性检查（绕过realpath对中文的解析问题）
            if (!file_exists($targetFile) || !is_file($targetFile)) {
                // 返回UTF-8编码的错误信息
                echo json_encode([
                    'success' => false,
                    'error' => '文件不存在：' . $fileName . '（系统路径：' . winGb2312ToUtf8($targetFile) . '）'
                ]);
                exit;
            }

            // 4. 读取文件内容（自动识别编码）
            $content = file_get_contents($targetFile);
            $encoding = mb_detect_encoding($content, ['UTF-8', 'GB2312', 'GBK', 'ASCII'], true);
            if ($encoding && $encoding !== 'UTF-8') {
                $content = mb_convert_encoding($content, 'UTF-8', $encoding);
            }

            echo json_encode([
                'success' => true,
                'content' => $content,
                'fileName' => $fileName,
                'filePath' => $filePath
            ]);
            exit;

        case 'save_file':
            $filePath = $request['path'] ?? '';
            $content = $request['content'] ?? '';

            $sysFilePath = winUtf8ToGb2312($filePath);

            // 修复路径重复问题：如果$sysFilePath已经包含$rootDir，则直接使用$sysFilePath
            if (stripos($sysFilePath, $rootDir) === 0) {
                $targetFile = $sysFilePath;
            } else {
                $targetFile = buildWinPath($rootDir, $sysFilePath, '');
            }

            if (!file_exists($targetFile) || !is_file($targetFile) || !is_writable($targetFile)) {
                echo json_encode([
                    'success' => false,
                    'error' => '文件不可写或不存在：' . $fileName
                ]);
                exit;
            }

            if (file_put_contents($targetFile, $content) !== false) {
                echo json_encode(['success' => true, 'message' => '保存成功！']);
            } else {
                echo json_encode(['success' => false, 'error' => '保存失败']);
            }
            exit;

        case 'load_directory':
            $dirPath = $request['dir'] ?? '';
            $sysDirPath = winUtf8ToGb2312($dirPath);

            // 修复目录处理逻辑：检查是否已经是绝对路径
            if (empty($sysDirPath)) {
                $targetDir = $rootDir;
            } elseif (strpos($sysDirPath, ':') !== false) {
                // 已经是绝对路径（包含盘符），直接使用
                $targetDir = $sysDirPath;
            } else {
                // 相对路径，拼接根目录
                $targetDir = buildWinPath($rootDir, $sysDirPath, '');
            }

            if (!is_dir($targetDir)) {
                echo json_encode(['success' => false, 'error' => '目录不存在：' . $dirPath]);
                exit;
            }

            // 获取目录内容并排序（限制只获取一级子目录）
            $files = getFiles($targetDir, 0, 1);

            // 直接输出调试信息
            if (empty($files)) {
                $globPath = rtrim($targetDir, '\\/') . '\\*';
                $rawFiles = glob($globPath, GLOB_NOSORT);
                $debugInfo = [
                    'targetDir' => $targetDir,
                    'globPath' => $globPath,
                    'rawFiles' => $rawFiles,
                    'rootDir' => $rootDir,
                    'isDir' => is_dir($targetDir),
                    'globResult' => $rawFiles ? count($rawFiles) : 0
                ];
                echo json_encode([
                    'success' => true,
                    'files' => [],
                    'dirPath' => winGb2312ToUtf8(str_replace($rootDir . '\\', '', $targetDir)),
                    'debug' => $debugInfo
                ]);
                exit;
            }

            usort($files, function ($a, $b) {
                if ($a['isDir'] && !$b['isDir']) return -1;
                if (!$a['isDir'] && $b['isDir']) return 1;
                return strnatcasecmp($a['name'], $b['name']);
            });

            // 确保所有文件路径和文件名都转换为UTF-8编码
            array_walk_recursive($files, function (&$value, $key) {
                if ($key === 'path' || $key === 'original_name') {
                    $value = winGb2312ToUtf8($value);
                }
            });

            // 返回响应
            $response = [
                'success' => true,
                'files' => $files,
                'dirPath' => winGb2312ToUtf8(str_replace($rootDir . '\\', '', $targetDir))
            ];

            // 确保JSON编码成功
            $jsonResult = json_encode($response);
            if ($jsonResult === false) {
                echo json_encode([
                    'success' => false,
                    'error' => 'JSON编码失败: ' . json_last_error_msg()
                ]);
                exit;
            }
            echo $jsonResult;
            exit;

        case 'new_file':
            $dirPath = $request['dir'] ?? '';
            $fileName = $request['name'] ?? '';

            if (empty($fileName)) {
                echo json_encode(['success' => false, 'error' => '文件名不能为空']);
                exit;
            }

            $sysDirPath = winUtf8ToGb2312($dirPath);
            $sysFileName = winUtf8ToGb2312($fileName);

            $targetDir = empty($sysDirPath) ? $rootDir : buildWinPath($rootDir, $sysDirPath, '');
            $targetFile = $targetDir . '\\' . ltrim($sysFileName, '\\/');

            if (file_exists($targetFile)) {
                echo json_encode(['success' => false, 'error' => '文件已存在']);
                exit;
            }

            // 创建空文件
            if (file_put_contents($targetFile, '') !== false) {
                echo json_encode(['success' => true, 'message' => '文件创建成功']);
            } else {
                echo json_encode(['success' => false, 'error' => '文件创建失败']);
            }
            exit;

        case 'delete':
            $filePath = $request['path'] ?? '';
            $isDir = $request['is_dir'] ?? false;

            $sysFilePath = winUtf8ToGb2312($filePath);

            // 修复路径重复问题：如果$sysFilePath已经包含$rootDir，则直接使用$sysFilePath
            if (stripos($sysFilePath, $rootDir) === 0) {
                $targetFile = $sysFilePath;
            } else {
                $targetFile = buildWinPath($rootDir, $sysFilePath, '');
            }

            if (!file_exists($targetFile)) {
                echo json_encode(['success' => false, 'error' => '文件或目录不存在']);
                exit;
            }

            // 删除文件或目录
            if ($isDir) {
                // 删除目录（递归删除）
                function rmdirRecursive($dir)
                {
                    $files = scandir($dir);
                    foreach ($files as $file) {
                        if ($file !== '.' && $file !== '..') {
                            $path = $dir . '\\' . $file;
                            if (is_dir($path)) {
                                rmdirRecursive($path);
                            } else {
                                unlink($path);
                            }
                        }
                    }
                    return rmdir($dir);
                }

                if (rmdirRecursive($targetFile)) {
                    echo json_encode(['success' => true, 'message' => '目录删除成功']);
                } else {
                    echo json_encode(['success' => false, 'error' => '目录删除失败']);
                }
            } else {
                // 删除文件
                if (unlink($targetFile)) {
                    echo json_encode(['success' => true, 'message' => '文件删除成功']);
                } else {
                    echo json_encode(['success' => false, 'error' => '文件删除失败']);
                }
            }
            exit;

        case 'rename':
            $filePath = $request['path'] ?? '';
            $newName = $request['new_name'] ?? '';
            $isDir = $request['is_dir'] ?? false;

            if (empty($newName)) {
                echo json_encode(['success' => false, 'error' => '新文件名不能为空']);
                exit;
            }

            $sysFilePath = winUtf8ToGb2312($filePath);
            $sysNewName = winUtf8ToGb2312($newName);

            // 修复路径重复问题：如果$sysFilePath已经包含$rootDir，则直接使用$sysFilePath
            if (stripos($sysFilePath, $rootDir) === 0) {
                $oldPath = $sysFilePath;
                // 新路径是原路径的目录部分加上新文件名
                $newPath = dirname($sysFilePath) . '\\' . ltrim($sysNewName, '\\/');
            } else {
                $oldPath = buildWinPath($rootDir, $sysFilePath, '');
                // 新路径是原路径的目录部分加上新文件名
                $newPath = dirname($oldPath) . '\\' . ltrim($sysNewName, '\\/');
            }

            if (!file_exists($oldPath)) {
                echo json_encode(['success' => false, 'error' => '文件或目录不存在']);
                exit;
            }

            if (file_exists($newPath)) {
                echo json_encode(['success' => false, 'error' => '新文件名已存在']);
                exit;
            }

            // 重命名
            if (rename($oldPath, $newPath)) {
                echo json_encode(['success' => true, 'message' => '重命名成功']);
            } else {
                echo json_encode(['success' => false, 'error' => '重命名失败']);
            }
            exit;

        default:
            echo json_encode(['success' => false, 'error' => '未知请求']);
            die(); // 终止脚本，防止输出HTML内容
    }
}



// ========== 修复遍历文件时的中文显示 ==========
/**
 * 获取目录文件列表，支持限制递归深度
 * @param string $dir 目录路径
 * @param int $depth 当前深度
 * @param int $maxDepth 最大递归深度（0表示只显示当前目录，1表示显示一级子目录，以此类推）
 * @return array 文件列表
 */
function getFiles($dir, $depth = 0, $maxDepth = 1)
{
    global $rootDir; // 引入网站根目录
    $items = [];
    if (!is_dir($dir)) return $items;

    // 用glob替代scandir（更好兼容中文路径）
    $globPath = rtrim($dir, '\\/') . '\\*';
    $files = glob($globPath, GLOB_NOSORT); // 添加GLOB_NOSORT参数，提高性能

    foreach ($files as $fullPath) {
        // 跳过.和..
        if (basename($fullPath) === '.' || basename($fullPath) === '..') continue;

        // 转换为UTF-8用于显示
        $displayName = winGb2312ToUtf8(basename($fullPath));
        $isDir = is_dir($fullPath);

        // 计算相对于网站根目录的路径
        $relativePath = str_replace($rootDir . '\\', '', $fullPath);

        $item = [
            'name' => $displayName, // UTF-8显示名
            'original_name' => basename($fullPath), // 系统原始名
            'path' => $relativePath, // 相对于网站根目录的路径
            'isDir' => $isDir,
            'size' => $isDir ? 0 : filesize($fullPath),
            'mtime' => filemtime($fullPath),
            'depth' => $depth,
            'children' => []
        ];

        // 只有在未达到最大深度时才递归获取子目录
        if ($isDir && $depth < $maxDepth) {
            $item['children'] = getFiles($fullPath, $depth + 1, $maxDepth);
        }
        $items[] = $item;
    }
    return $items;
}

// 不再需要PHP端初始化文件列表，所有文件列表都通过AJAX动态获取

// 格式化文件大小
function formatSize($bytes)
{
    if ($bytes >= 1073741824) return number_format($bytes / 1073741824, 1) . ' GB';
    if ($bytes >= 1048576) return number_format($bytes / 1048576, 1) . ' MB';
    if ($bytes >= 1024) return number_format($bytes / 1024, 1) . ' KB';
    return $bytes . ' 字节';
}
?>
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>文件管理器（修复一级文件）</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; font-size: 14px; line-height: 1.5; color: #374151; background-color: #f9fafb; }
        .container { display: flex; gap: 0; min-height: calc(100vh - 100px); max-width: 900px; width: 100%; margin: 0 auto; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); overflow: hidden; background-color: #fff; }
        .file-list { width: 280px; border-right: 1px solid #e5e7eb; display: flex; flex-direction: column; height: calc(100vh - 100px); }
        .file-list-header { padding: 12px 16px; background: #f9fafb; border-bottom: 1px solid #e5e7eb; font-weight: 600; font-size: 14px; color: #4b5563; display: flex; justify-content: space-between; align-items: center; }
        #refresh-btn { padding: 8px 16px; font-size: 13px; cursor: pointer; background: #f59e0b; border: none; border-radius: 6px; color: white; font-weight: 600; transition: background-color 0.15s ease; }
        #refresh-btn:hover { background: #d97706; }
        .file-list-content { flex: 1; overflow-y: auto; background-color: #f9fafb; }
        .file-content { flex: 1; display: flex; flex-direction: column; height: calc(100vh - 100px); }
        .file-item { padding: 8px 16px; cursor: pointer; display: flex; align-items: center; border-bottom: 1px solid #e5e7eb; transition: background-color 0.15s ease; position: relative; font-size: 13px; color: #374151; }
        .file-item:hover { background-color: #f3f4f6; }
        .file-item.selected { background-color: #eff6ff; color: #3b82f6; }
        .file-icon { margin-right: 8px; width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center; color: #6b7280; }
        .file-item.selected .file-icon { color: #3b82f6; }
        .toggle-icon { width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center; margin-right: 4px; color: #6b7280; transition: transform 0.2s ease; }
        .file-item.selected .toggle-icon { color: #3b82f6; }
        .file-name { flex: 1; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .context-menu { position: fixed; background-color: white; border: 1px solid #e5e7eb; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; display: none; min-width: 140px; padding: 4px 0; }
        .context-menu-item { padding: 8px 16px; cursor: pointer; font-size: 13px; white-space: nowrap; transition: background-color 0.15s ease; display: flex; align-items: center; gap: 8px; color: #374151; }
        .context-menu-item:hover { background-color: #f3f4f6; color: #3b82f6; }
        .context-menu-item svg { color: #6b7280; }
        .context-menu-item:hover svg { color: #3b82f6; }
        .editor-header { padding: 12px 16px; background: #f9fafb; border-bottom: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center; }
        .editor-title { font-size: 14px; font-weight: 600; color: #4b5563; }
        .save-btn { background: #3b82f6; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600; transition: background-color 0.15s ease; display: flex; align-items: center; gap: 6px; }
        .save-btn:hover:not(:disabled) { background: #2563eb; }
        .save-btn:disabled { background: #e5e7eb; color: #9ca3af; cursor: not-allowed; }
        .save-btn svg { width: 14px; height: 14px; }
        .editor-body { flex: 1; padding: 0; overflow: hidden; }
        .code-editor-container { display: flex; position: relative; height: 100%; overflow: hidden; background-color: #fff; }
        .line-numbers { width: 48px; padding: 16px 8px; font-family: "Consolas", "Monaco", "Courier New", monospace; font-size: 13px; line-height: 1.6; text-align: right; color: #9ca3af; user-select: none; overflow: hidden; border-right: 1px solid #e5e7eb; background-color: #f9fafb; }
        .editor-content { flex: 1; display: flex; flex-direction: column; height: 100%; }
        .editor-wrapper { position: relative; flex: 1; overflow-y: auto; overflow-x: hidden; }
        #file_content { width: 100%; height: 100%; padding: 16px; font-family: "Consolas", "Monaco", "Courier New", monospace; font-size: 13px; border: none; background-color: transparent; color: transparent; caret-color: #374151; line-height: 1.6; resize: none; outline: none; overflow: auto; position: absolute; top: 0; left: 0; z-index: 1; white-space: pre-wrap; word-wrap: break-word; }
        .highlighted-content { width: 100%; height: 100%; padding: 16px; font-family: "Consolas", "Monaco", "Courier New", monospace; font-size: 13px; border: none; background-color: #fff; color: #374151; line-height: 1.6; overflow: hidden; position: absolute; top: 0; left: 0; z-index: 0; white-space: pre-wrap; word-wrap: break-word; }
        .css-selector { color: #3b82f6; font-weight: 500; }
        .css-property { color: #10b981; }
        .css-value { color: #f59e0b; }
        .css-comment { color: #9ca3af; font-style: italic; }
        .css-atrule { color: #8b5cf6; }
        .css-punctuation { color: #374151; }
        .editor-status { display: flex; justify-content: space-between; padding: 8px 16px; font-size: 12px; color: #6b7280; height: 32px; box-sizing: border-box; line-height: 16px; background-color: #f9fafb; border-top: 1px solid #e5e7eb; }
        .status-item { margin-right: 20px; }
        .status-item:last-child { margin-right: 0; margin-left: auto; }
        .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #6b7280; text-align: center; background-color: #f9fafb; }
        .empty-state .icon { width: 64px; height: 64px; margin-bottom: 16px; color: #d1d5db; }
        .empty-state h3 { font-size: 14px; font-weight: 500; margin-bottom: 8px; color: #6b7280; }
        .tree-item { position: relative; border-bottom: none; }
        .children-container { margin-left: 16px; display: none; background-color: #f9fafb; }
        .children-container.open { display: block; }
        ::-webkit-scrollbar { width: 6px; height: 6px; }
        ::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 3px; }
        ::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 3px; transition: background 0.15s ease; }
        ::-webkit-scrollbar-thumb:hover { background: #9ca3af; }
        @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
        @keyframes fadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-10px); } }
    </style>
</head>

<body>
    <div class="container">
        <div class="file-list">
            <div class="file-list-header">文件列表 <button id="refresh-btn" style="float:right; padding:2px 8px; font-size:12px; cursor:pointer;">刷新</button></div>
            <div class="file-list-content">
                <div class="tree-container" id="tree-container" data-rootpath="<?php echo htmlspecialchars($rootDir); ?>">
                    <!-- 文件列表将通过AJAX动态加载 -->
                </div>
            </div>
        </div>

        <div class="file-content">
            <div class="editor-header">
                <div class="editor-title" id="editor-title">选择一个文件进行编辑</div>
                <button class="save-btn" id="save-btn" disabled>
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
                        <polyline points="17 21 17 13 7 13 7 21"></polyline>
                        <polyline points="7 3 7 8 15 8"></polyline>
                    </svg>
                    保存
                </button>
            </div>
            <div class="editor-body">
                <div class="empty-state" id="empty-state">
                    <svg class="icon" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    <h3>选择一个文件</h3>
                </div>
                <div class="code-editor-container" id="code-editor-container" style="display: none;">
                    <div class="line-numbers" id="line-numbers"></div>
                    <div class="editor-content">
                        <div class="editor-wrapper">
                            <textarea name="content" id="file_content"></textarea>
                            <div id="highlighted-content" class="highlighted-content"></div>
                        </div>
                        <div class="editor-status">
                            <span class="status-item" id="status-encoding">编码: UTF-8</span>
                            <span class="status-item" id="status-lines">总行数: 0</span>
                            <span class="status-item" id="status-filename">文件名: -</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div id="contextMenu" class="context-menu">
            <div class="context-menu-item" id="delete">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <polyline points="3 6 5 6 21 6"></polyline>
                    <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                </svg>
                删除
            </div>
            <div class="context-menu-item" id="rename">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
                    <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
                </svg>
                重命名
            </div>
        </div>

        <script>
            let currentFile = null;

            // 使用系统提供的黑色透明提示框
            function showMessage(message, type = 'success') {
                // 转换为系统提示框的类型参数
                const options = {
                    type: type
                };
                callParentToast(message, options);
            }

            // 调用父页面的系统提示框
            function callParentToast(message, options = {}) {
                window.parent.postMessage({
                    type: 'toast',
                    func: 'showToast',
                    message: message,
                    options: options
                }, window.parent.location.origin);
            }

            // 调用父页面的系统确认框
            function callParentConfirm(message) {
                const id = Math.random().toString(36).substring(2, 15);

                return new Promise((resolve) => {
                    // 监听确认结果
                    window.addEventListener('message', function listener(event) {
                        if (event.data.type === 'confirm_result' && event.data.id === id) {
                            resolve(event.data.result);
                            window.removeEventListener('message', listener);
                        }
                    });

                    // 发送确认请求
                    window.parent.postMessage({
                        type: 'confirm',
                        id: id,
                        func: 'confirmDialog',
                        message: message
                    }, window.parent.location.origin);
                });
            }

            function toggleDirectory(toggleIcon) {
                // 确保toggleIcon元素存在
                if (!toggleIcon) {

                    return;
                }

                // 切换目录展开/折叠状态
                const treeItem = toggleIcon.parentElement.parentElement;
                if (!treeItem) {

                    return;
                }

                const children = treeItem.querySelector('.children-container');
                if (!children) return;

                if (children.style.display === 'none') {
                    // 展开目录时旋转小三角
                    toggleIcon.style.transform = 'rotate(90deg)';
                    children.style.display = 'block';
                } else {
                    // 折叠目录时恢复小三角方向
                    toggleIcon.style.transform = 'rotate(0deg)';
                    children.style.display = 'none';
                }
            }

            function openFile(fileItem) {
                if (fileItem.dataset.isdir === 'true') return;
                document.querySelectorAll('.file-item.selected').forEach(el => el.classList.remove('selected'));
                fileItem.classList.add('selected');

                const path = fileItem.dataset.path;
                const name = fileItem.dataset.name;
                const originalName = fileItem.dataset.originalName;

                document.getElementById('editor-title').textContent = `正在编辑: ${name}`;
                const textarea = document.getElementById('file_content');
                const emptyState = document.getElementById('empty-state');
                const codeEditor = document.getElementById('code-editor-container');
                textarea.style.display = 'block';
                emptyState.style.display = 'none';
                codeEditor.style.display = 'flex';
                document.getElementById('save-btn').disabled = false;

                currentFile = {
                    path,
                    name,
                    originalName
                };
                textarea.value = '加载中...';

                // 关键：前端传递UTF-8编码的文件名（不再传递originalName）
                fetch('/admin/Plugin.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-Requested-With': 'XMLHttpRequest'
                    },
                    body: JSON.stringify({
                        plugin: '<?php echo $plugin_identifier; ?>',
                        action: 'open_file',
                        path: path,
                        name: name // 直接传UTF-8显示名，后端统一转换
                    })
                }).then(res => res.json()).then(data => {
                    if (data.success) {
                        textarea.value = data.content;
                        updateHighlight(); // 更新高亮
                        updateStatusBar(textarea.value, name); // 更新状态栏
                        updateLineNumbers(); // 更新行号
                    } else {
                        textarea.value = `加载失败: ${data.error}`;
                        updateStatusBar(textarea.value, name); // 更新错误信息的状态栏
                        updateLineNumbers(); // 更新行号
                        showMessage(data.error, 'error');
                    }
                }).catch(err => {
                    textarea.value = '加载失败';
                    updateStatusBar(textarea.value, name); // 更新错误信息的状态栏
                    updateLineNumbers(); // 更新行号
                    showMessage('网络错误', 'error');
                });
            }

            function saveFile() {
                if (!currentFile) return;
                const content = document.getElementById('file_content').value;
                fetch('/admin/Plugin.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-Requested-With': 'XMLHttpRequest'
                    },
                    body: JSON.stringify({
                        plugin: '<?php echo $plugin_identifier; ?>',
                        action: 'save_file',
                        path: currentFile.path,
                        name: currentFile.name,
                        content: content
                    })
                }).then(res => res.json()).then(data => {
                    if (data.success) {
                        showMessage('保存成功');
                    } else {
                        showMessage(data.error, 'error');
                    }
                });
            }

            /**
             * CSS语法高亮函数
             * @param {string} code - 要高亮的CSS代码
             * @returns {string} - 带有HTML高亮标记的代码
             */
            function highlightCSS(code) {
                // 转义HTML特殊字符
                code = code
                    .replace(/&/g, '&amp;')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;');

                // 高亮注释
                code = code.replace(/\/\*[\s\S]*?\*\//g, '<span class="css-comment">$&</span>');

                // 高亮at规则
                code = code.replace(/@[a-zA-Z-]+\s*/g, '<span class="css-atrule">$&</span>');

                // 高亮选择器
                code = code.replace(/([a-zA-Z0-9_-]+)(?=\s*\{)/g, '<span class="css-selector">$1</span>');
                code = code.replace(/([.#][a-zA-Z0-9_-]+)(?=\s*\{)/g, '<span class="css-selector">$1</span>');
                code = code.replace(/(\[[^\]]+\])(?=\s*\{)/g, '<span class="css-selector">$1</span>');

                // 高亮属性
                code = code.replace(/([a-zA-Z-]+)(?=\s*:)/g, '<span class="css-property">$1</span>');

                // 高亮值
                code = code.replace(/:\s*([^;}]+)/g, ': <span class="css-value">$1</span>');

                // 高亮标点符号
                code = code.replace(/([{};:(),.+*~>\-=|])/g, '<span class="css-punctuation">$1</span>');

                return code;
            }

            /**
             * 更新高亮显示
             */
            function updateHighlight() {
                const textarea = document.getElementById('file_content');
                const highlightedDiv = document.getElementById('highlighted-content');
                if (textarea && highlightedDiv) {
                    const content = textarea.value;
                    highlightedDiv.innerHTML = highlightCSS(content);
                }
            }

            /**
             * 更新编辑框底部状态栏信息
             * @param {string} content - 文件内容
             * @param {string} filename - 文件名
             */
            function updateStatusBar(content, filename) {
                // 更新文件名
                document.getElementById('status-filename').textContent = `文件名: ${filename}`;

                // 计算总行数
                const totalLines = content.trim() ? content.split('\n').length : 0;
                document.getElementById('status-lines').textContent = `总行数: ${totalLines}`;

                // 编码信息（默认为UTF-8）
                document.getElementById('status-encoding').textContent = `编码: UTF-8`;
            }

            /**
             * 更新行号显示
             */
            function updateLineNumbers() {
                const textarea = document.getElementById('file_content');
                const lineNumbers = document.getElementById('line-numbers');

                // 计算行数
                const lines = textarea.value.split('\n');
                const lineCount = lines.length;

                // 生成行号HTML
                let lineNumbersHTML = '';
                for (let i = 1; i <= lineCount; i++) {
                    lineNumbersHTML += `${i}<br>`;
                }

                // 更新行号区域
                lineNumbers.innerHTML = lineNumbersHTML;

                // 同步滚动位置
                lineNumbers.scrollTop = textarea.scrollTop;
            }

            // 同步滚动和高亮更新
            document.addEventListener('DOMContentLoaded', function() {
                const textarea = document.getElementById('file_content');
                const highlightedDiv = document.getElementById('highlighted-content');
                const lineNumbers = document.getElementById('line-numbers');

                if (textarea && highlightedDiv) {
                    // 同步滚动
                    textarea.addEventListener('scroll', function() {
                        highlightedDiv.scrollTop = textarea.scrollTop;
                        highlightedDiv.scrollLeft = textarea.scrollLeft;
                        lineNumbers.scrollTop = textarea.scrollTop;
                    });

                    // 输入时更新高亮
                    textarea.addEventListener('input', function() {
                        updateHighlight();
                        updateLineNumbers();
                        if (currentFile) {
                            updateStatusBar(textarea.value, currentFile.name);
                        }
                    });
                }
            });

            /**
             * 同步行号区域与文本区域的滚动位置
             */
            function syncLineNumbersScroll() {
                const textarea = document.getElementById('file_content');
                const lineNumbers = document.getElementById('line-numbers');
                lineNumbers.scrollTop = textarea.scrollTop;
            }

            // 保存当前右键点击的文件项
            let rightClickedFileItem = null;

            /**
             * 重新加载文件列表 - 通过系统的AJAX请求获取数据并更新DOM
             */
            function loadFileList() {
                // 显示加载状态
                const treeContainer = document.getElementById('tree-container');
                const loadingHTML = '<div style="text-align: center; padding: 20px;">加载中...</div>';
                treeContainer.innerHTML = loadingHTML;

                // 使用系统正确的AJAX请求格式
                fetch('/admin/Plugin.php', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'X-Requested-With': 'XMLHttpRequest'
                        },
                        body: JSON.stringify({
                            plugin: '<?php echo $plugin_identifier; ?>',
                            action: 'load_directory',
                            dir: ''
                        })
                    })
                    .then(response => response.json())
                    .then(data => {
                        if (data.success) {
                            // 清空现有的文件列表
                            treeContainer.innerHTML = '';

                            // 渲染新的文件列表
                            renderFileList(data.files);

                            // 重新绑定事件
                            addFileItemEvents();
                        } else {
                            alert('加载文件列表失败：' + (data.error || data.message || '未知错误'));
                        }
                    })
                    .catch(error => {

                        alert('加载文件列表时发生错误：' + error.message);
                    });
            }

            /**
             * 工具函数：获取目录名
             */
            function dirname(path) {
                if (!path) return '';
                // 处理Windows和Unix风格路径
                const dir = path.replace(/[\/][^\/]+$/, '');
                return dir || '';
            }

            /**
             * 渲染单个文件项
             */
            function renderItem(file, depth = 0) {
                const isDir = file.isDir ? 'true' : 'false';
                const itemPath = file.path || '';
                const displayName = file.name;
                const indentStyle = depth > 0 ? 'padding-left: ' + (depth * 20) + 'px;' : '';

                const treeItem = document.createElement('div');
                treeItem.className = 'tree-item';

                const fileItem = document.createElement('div');
                fileItem.className = 'file-item';
                fileItem.dataset.isdir = isDir;
                fileItem.dataset.depth = depth;
                fileItem.dataset.path = itemPath;
                fileItem.dataset.name = displayName;
                if (file.original_name) {
                    fileItem.dataset.originalName = file.original_name;
                }
                fileItem.style = indentStyle;

                const toggleIcon = document.createElement('span');
                toggleIcon.className = 'toggle-icon';
                if (file.isDir) {
                    toggleIcon.innerHTML = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>';
                } else {
                    toggleIcon.style.visibility = 'hidden';
                }

                const fileIconSpan = document.createElement('span');
                fileIconSpan.className = 'file-icon';
                if (file.isDir) {
                    fileIconSpan.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>';
                } else {
                    fileIconSpan.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>';
                }

                const fileName = document.createElement('span');
                fileName.className = 'file-name';
                fileName.textContent = displayName;
                fileName.title = displayName;

                fileItem.appendChild(toggleIcon);
                fileItem.appendChild(fileIconSpan);
                fileItem.appendChild(fileName);

                treeItem.appendChild(fileItem);

                if (file.isDir) {
                    const childrenContainer = document.createElement('div');
                    childrenContainer.className = 'children-container';
                    childrenContainer.style.display = 'none';

                    if (file.children && file.children.length > 0) {
                        file.children.forEach(child => {
                            childrenContainer.appendChild(renderItem(child, depth + 1));
                        });
                    }

                    treeItem.appendChild(childrenContainer);
                }

                return treeItem;
            }

            /**
             * 渲染文件列表
             */
            function renderFileList(files) {
                const treeContainer = document.getElementById('tree-container');
                // 清空现有内容
                treeContainer.innerHTML = '';

                // 检查files参数是否存在且为数组
                if (!files || !Array.isArray(files)) {
                    treeContainer.innerHTML = '<div style="text-align: center; padding: 20px; color: #999;">暂无文件</div>';
                    return;
                }

                if (files.length === 0) {
                    treeContainer.innerHTML = '<div style="text-align: center; padding: 20px; color: #999;">目录为空</div>';
                    return;
                }

                files.forEach(file => {
                    treeContainer.appendChild(renderItem(file));
                });
            }

            /**
             * 刷新文件列表
             */
            function refreshFileList() {
                fetch('/admin/Plugin.php', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'X-Requested-With': 'XMLHttpRequest'
                        },
                        body: JSON.stringify({
                            plugin: '<?php echo $plugin_identifier; ?>',
                            action: 'load_directory',
                            dir: '' // 空字符串表示根目录
                        })
                    })
                    .then(response => response.json())
                    .then(data => {
                        if (data.success) {
                            // 显示调试信息
                            if (data.debug) {
                                console.log('调试信息:', data.debug);
                                alert('调试信息:\n' + JSON.stringify(data.debug, null, 2));
                            }
                            // 直接更新整个文件列表
                            renderFileList(data.files || []);
                            addFileItemEvents();
                        } else {
                            alert('刷新文件列表失败：' + (data.error || data.message || '未知错误'));
                        }
                    })
                    .catch(error => {

                        alert('刷新文件列表时发生错误：' + error.message);
                    });
            }

            // 为刷新按钮添加事件监听
            document.addEventListener('DOMContentLoaded', () => {
                const refreshBtn = document.getElementById('refresh-btn');
                if (refreshBtn) {
                    refreshBtn.addEventListener('click', refreshFileList);
                }

                // 页面加载完成后自动加载文件列表
                refreshFileList();
            });

            /**
             * 为文件项添加事件监听
             */
            function addFileItemEvents() {
                // 为所有文件项添加点击事件
                const fileItems = document.querySelectorAll('.file-item');

                fileItems.forEach(item => {
                    // 移除旧的事件监听器（如果有）
                    item.onclick = null;
                    item.oncontextmenu = null;

                    // 添加点击事件
                    item.onclick = function(e) {
                        // 如果是目录，则处理展开/折叠
                        if (this.dataset.isdir === 'true') {
                            // 获取toggle-icon元素
                            const toggleIcon = this.querySelector('.toggle-icon');

                            // 确保toggleIcon元素存在
                            if (!toggleIcon) {

                                return;
                            }

                            // 检查目录是否已经展开
                            const treeItem = this.parentElement;
                            const childrenContainer = treeItem.querySelector('.children-container');
                            const isExpanded = childrenContainer && childrenContainer.style.display !== 'none';

                            // 如果已经展开，则只切换折叠状态，不重新加载内容
                            if (isExpanded) {
                                toggleDirectory(toggleIcon);
                                return;
                            }

                            // 如果是www目录，只切换展开/折叠状态，不重新加载内容
                            if (this.dataset.name === 'www') {
                                // 切换目录展开/折叠状态
                                toggleDirectory(toggleIcon);
                                return;
                            }

                            // 发送AJAX请求加载目录内容
                            // data-path已经是相对路径，直接使用
                            const dir = this.dataset.path;

                            fetch('/admin/Plugin.php', {
                                    method: 'POST',
                                    headers: {
                                        'Content-Type': 'application/json',
                                        'X-Requested-With': 'XMLHttpRequest'
                                    },
                                    body: JSON.stringify({
                                        plugin: '<?php echo $plugin_identifier; ?>',
                                        action: 'load_directory',
                                        dir: dir
                                    })
                                })
                                .then(response => response.json())
                                .then(data => {
                                    if (data.success) {
                                        // 清空当前目录的子内容
                                        const treeItem = this.parentElement;
                                        const childrenContainer = treeItem.querySelector('.children-container');
                                        if (childrenContainer) {
                                            childrenContainer.innerHTML = '';

                                            // 添加新内容
                                            data.files.forEach(childFile => {
                                                const childElement = renderItem(childFile, parseInt(this.dataset.depth) + 1);
                                                childrenContainer.appendChild(childElement);
                                            });

                                            // 重新绑定事件
                                            addFileItemEvents();
                                        }

                                        // 切换目录展开/折叠状态
                                        toggleDirectory(toggleIcon);
                                    } else {
                                        alert('加载目录内容失败：' + (data.error || data.message || '未知错误'));
                                    }
                                })
                                .catch(error => {

                                    alert('加载目录内容时发生错误：' + error.message);
                                });
                        } else {
                            // 如果是文件，打开文件
                            openFile(this);
                        }
                    };

                    // 添加右键菜单事件
                    item.oncontextmenu = function(e) {
                        e.preventDefault();
                        rightClickedFileItem = this;
                        const menu = document.getElementById('contextMenu');
                        menu.style.left = e.clientX + 'px';
                        menu.style.top = e.clientY + 'px';
                        menu.style.display = 'block';
                    };
                });
            }

            // 初始化事件
            document.addEventListener('DOMContentLoaded', () => {

                // 文件内容变化监听
                const textarea = document.getElementById('file_content');
                textarea.addEventListener('input', () => {
                    if (currentFile) {
                        updateStatusBar(textarea.value, currentFile.name);
                        updateLineNumbers();
                    }
                });

                // 滚动同步监听
                textarea.addEventListener('scroll', syncLineNumbersScroll);

                // 调用统一的事件绑定函数 - 避免重复绑定事件导致的抖动
                addFileItemEvents();

                // 保存按钮
                document.getElementById('save-btn').addEventListener('click', saveFile);
                // Ctrl+S保存
                document.addEventListener('keydown', e => {
                    if ((e.ctrlKey || e.metaKey) && e.key === 's') {
                        e.preventDefault();
                        if (!document.getElementById('save-btn').disabled) saveFile();
                    }
                });

                // 隐藏右键菜单
                document.addEventListener('click', () => {
                    document.getElementById('contextMenu').style.display = 'none';
                });

                // 右键菜单项 - 删除
                document.getElementById('delete').addEventListener('click', () => {
                    if (!rightClickedFileItem) return;

                    const name = rightClickedFileItem.dataset.name;
                    const isDir = rightClickedFileItem.dataset.isdir === 'true';
                    const path = rightClickedFileItem.dataset.path;

                    callParentConfirm(`确定要删除${isDir ? '目录' : '文件'} "${name}"吗？`).then(function(result) {
                        if (result) {
                            fetch('/admin/Plugin.php', {
                                method: 'POST',
                                headers: {
                                    'Content-Type': 'application/json',
                                    'X-Requested-With': 'XMLHttpRequest'
                                },
                                body: JSON.stringify({
                                    plugin: '<?php echo $plugin_identifier; ?>',
                                    action: 'delete',
                                    path: path,
                                    name: name,
                                    is_dir: isDir
                                })
                            }).then(res => res.json()).then(data => {
                                if (data.success) {
                                    showMessage(isDir ? '目录删除成功' : '文件删除成功', 'success');
                                    refreshFileList();
                                } else {
                                    showMessage(data.error, 'error');
                                }
                            });
                        }
                    });
                    document.getElementById('contextMenu').style.display = 'none';
                });

                // 右键菜单项 - 重命名
                document.getElementById('rename').addEventListener('click', () => {
                    if (!rightClickedFileItem) return;

                    const oldName = rightClickedFileItem.dataset.name;
                    const isDir = rightClickedFileItem.dataset.isdir === 'true';
                    const path = rightClickedFileItem.dataset.path;

                    const newName = prompt('新名称:', oldName);
                    if (newName && newName !== oldName) {
                        fetch('/admin/Plugin.php', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                                'X-Requested-With': 'XMLHttpRequest'
                            },
                            body: JSON.stringify({
                                plugin: '<?php echo $plugin_identifier; ?>',
                                action: 'rename',
                                path: path,
                                old_name: oldName,
                                new_name: newName,
                                is_dir: isDir
                            })
                        }).then(res => res.json()).then(data => {
                            if (data.success) {
                                showMessage('重命名成功', 'success');
                                refreshFileList();
                            } else {
                                showMessage(data.error, 'error');
                            }
                        });
                    }
                    document.getElementById('contextMenu').style.display = 'none';
                });
            });
        </script>
</body>

</html>