Zero-copy FTP/HTTP Daemon compatible with all POSIX systems
| 1 | <!DOCTYPE html> |
| 2 | <html lang="en" data-theme="ps5"> |
| 3 | |
| 4 | <head> |
| 5 | <meta charset="UTF-8"> |
| 6 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 7 | <title>zftpd | Console File Manager</title> |
| 8 | <!-- CSRF_TOKEN --> |
| 9 | |
| 10 | <!-- ══ Modular CSS ══ --> |
| 11 | <link rel="stylesheet" href="css/variables.css?v=3"> |
| 12 | <link rel="stylesheet" href="css/base.css?v=3"> |
| 13 | <link rel="stylesheet" href="css/layout.css?v=3"> |
| 14 | <link rel="stylesheet" href="css/components.css?v=3"> |
| 15 | <link rel="stylesheet" href="css/file-explorer.css?v=3"> |
| 16 | <link rel="stylesheet" href="css/file-manager.css?v=3"> |
| 17 | <link rel="stylesheet" href="css/download-manager.css?v=3"> |
| 18 | <link rel="stylesheet" href="css/dashboard.css?v=3"> |
| 19 | <link rel="stylesheet" href="css/games.css?v=3"> |
| 20 | <link rel="stylesheet" href="css/settings.css?v=3"> |
| 21 | <link rel="stylesheet" href="css/context-menu.css?v=3"> |
| 22 | <link rel="stylesheet" href="css/transfer-tray.css?v=3"> |
| 23 | <link rel="stylesheet" href="css/stats-monitor.css?v=3"> |
| 24 | <link rel="stylesheet" href="css/responsive.css?v=3"> |
| 25 | |
| 26 | <!-- Theme dropdown inline styles (small, keep in HTML) --> |
| 27 | <style> |
| 28 | .theme-switcher { position: relative; display: flex; align-items: center; } |
| 29 | .theme-btn { display:flex;align-items:center;gap:7px;border:1px solid var(--bd2);background:var(--sf2);color:var(--tx2);border-radius:8px;padding:6px 11px;font-family:inherit;font-size:11px;font-weight:600;cursor:pointer;transition:all .15s;white-space:nowrap; } |
| 30 | .theme-btn:hover { border-color:var(--ac);color:var(--tx); } |
| 31 | .t-swatch { width:10px;height:10px;border-radius:50%;flex-shrink:0;border:1.5px solid rgba(255,255,255,.2); } |
| 32 | .t-arr { transition:transform .15s;color:var(--tx3); } |
| 33 | .theme-btn.open .t-arr { transform:rotate(180deg); } |
| 34 | .theme-dd { position:absolute;top:calc(100% + 8px);right:0;min-width:210px;background:var(--sf);border:1px solid var(--bd2);border-radius:14px;padding:8px;box-shadow:0 24px 64px rgba(0,0,0,.7);display:none;z-index:600;animation:popIn .15s ease; } |
| 35 | .theme-dd.show { display:block; } |
| 36 | .td-hd { font-size:9px;color:var(--tx3);text-transform:uppercase;letter-spacing:.1em;padding:4px 8px 8px;border-bottom:1px solid var(--bd);margin-bottom:6px; } |
| 37 | .td-item { display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;cursor:pointer;color:var(--tx2);transition:all .1s; } |
| 38 | .td-item:hover { background:var(--sf2);color:var(--tx); } |
| 39 | .td-item.active { background:var(--gw);color:var(--ac); } |
| 40 | .td-sw { width:26px;height:26px;border-radius:7px;flex-shrink:0;border:1px solid rgba(255,255,255,.1);display:flex;align-items:center;justify-content:center; } |
| 41 | .td-info { display:flex;flex-direction:column;gap:1px;flex:1;min-width:0; } |
| 42 | .td-nm { font-weight:600;font-size:12px; } |
| 43 | .td-ds { font-size:10px;color:var(--tx3); } |
| 44 | .td-ck { color:var(--ac);opacity:0;transition:opacity .1s;flex-shrink:0; } |
| 45 | .td-item.active .td-ck { opacity:1; } |
| 46 | </style> |
| 47 | </head> |
| 48 | |
| 49 | <body> |
| 50 | |
| 51 | <!-- ══ TOPBAR ═══════════════════════════════════════════════════════════ --> |
| 52 | <header class="topbar"> |
| 53 | <div class="topbar-left"> |
| 54 | <div class="brand"> |
| 55 | <img class="brand-logo" src="assets/zftpd-logo.png" alt="zftpd"> |
| 56 | <div class="brand-text"> |
| 57 | <div class="brand-title">zftpd</div> |
| 58 | <div class="brand-sub">Console File Manager</div> |
| 59 | </div> |
| 60 | </div> |
| 61 | </div> |
| 62 | <div class="topbar-right"> |
| 63 | <div id="status" class="status-pill status-ok"> |
| 64 | <span class="sdot"></span> |
| 65 | <span class="stxt">Connected</span> |
| 66 | </div> |
| 67 | |
| 68 | <!-- Download Progress Pill --> |
| 69 | <div id="tb-dl-pill" class="tb-dl-pill hidden" title="Active downloads"> |
| 70 | <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> |
| 71 | <span id="tb-dl-count">0</span> |
| 72 | <div class="tb-dl-bar"><div id="tb-dl-bar-fill" class="tb-dl-bar-fill"></div></div> |
| 73 | </div> |
| 74 | |
| 75 | <!-- Notifications Bell --> |
| 76 | <button id="tb-notif-btn" class="tb-notif-btn" title="Notifications"> |
| 77 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg> |
| 78 | <span id="tb-notif-badge" class="tb-notif-badge hidden">0</span> |
| 79 | </button> |
| 80 | <div id="tb-notif-dd" class="tb-notif-dd"> |
| 81 | <div class="td-hd">Notifications</div> |
| 82 | <div id="tb-notif-list" class="tb-notif-list"> |
| 83 | <div class="tb-notif-empty">No notifications</div> |
| 84 | </div> |
| 85 | </div> |
| 86 | |
| 87 | <div class="theme-switcher"> |
| 88 | <button id="theme-btn" class="theme-btn"> |
| 89 | <span id="t-sw" class="t-swatch" style="background:#2b8cff"></span> |
| 90 | <span id="t-nm">PS5</span> |
| 91 | <span class="t-arr">▾</span> |
| 92 | </button> |
| 93 | <div id="theme-dd" class="theme-dd"></div> |
| 94 | <select id="theme-select" class="theme-select-mobile"></select> |
| 95 | </div> |
| 96 | </div> |
| 97 | </header> |
| 98 | |
| 99 | <!-- ══ NAV TABS ═════════════════════════════════════════════════════════ --> |
| 100 | <nav class="nav-tabs"> |
| 101 | <button class="nav-tab active" data-view="dashboard"> |
| 102 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg> |
| 103 | Dashboard |
| 104 | </button> |
| 105 | <button class="nav-tab" data-view="explorer"> |
| 106 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg> |
| 107 | Explorer |
| 108 | </button> |
| 109 | <button class="nav-tab" data-view="filemanager"> |
| 110 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><line x1="12" y1="3" x2="12" y2="21"/></svg> |
| 111 | File Manager |
| 112 | </button> |
| 113 | <button class="nav-tab" data-view="downloads"> |
| 114 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> |
| 115 | Downloads |
| 116 | </button> |
| 117 | <button class="nav-tab" data-view="games"> |
| 118 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 21a9 9 0 0 0 9-9c0-3.3-1-5.3-2.5-6.6-1.5-1.3-4-2.4-6.5-2.4S7.5 4.1 6 5.4C4.5 6.7 3.5 8.7 3.5 12a9 9 0 0 0 9 9z"/><path d="M12 12h.01M8 12h.01M16 12h.01M12 8h.01M12 16h.01"/></svg> |
| 119 | Games |
| 120 | </button> |
| 121 | <button class="nav-tab" data-view="settings"> |
| 122 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg> |
| 123 | Settings |
| 124 | </button> |
| 125 | </nav> |
| 126 | |
| 127 | <!-- ══ VIEW: DASHBOARD ══════════════════════════════════════════════════ --> |
| 128 | <div id="view-dashboard" class="view view-active"> |
| 129 | <div class="content"> |
| 130 | <div class="dash"> |
| 131 | |
| 132 | <!-- Games Section: Hero + Scroll --> |
| 133 | <div class="dash-section" style="padding-bottom: 16px;"> |
| 134 | <div class="dash-section-title"> |
| 135 | <svg viewBox="0 0 24 24" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 21a9 9 0 0 0 9-9c0-3.3-1-5.3-2.5-6.6-1.5-1.3-4-2.4-6.5-2.4S7.5 4.1 6 5.4C4.5 6.7 3.5 8.7 3.5 12a9 9 0 0 0 9 9z"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 12h.01M8 12h.01M16 12h.01M12 8h.01M12 16h.01"/></svg> |
| 136 | My Library |
| 137 | <span class="dash-see-all" onclick="ZFTPD.dashboard.showGamesList()">See all →</span> |
| 138 | </div> |
| 139 | |
| 140 | <div id="dash-hero-container" class="dash-hero-container" style="display:none;"> |
| 141 | <!-- Hero banner dynamically injected here --> |
| 142 | </div> |
| 143 | |
| 144 | <div id="dash-games" class="dash-games-row"> |
| 145 | <!-- Game cards inserted here --> |
| 146 | </div> |
| 147 | </div> |
| 148 | |
| 149 | <div class="dash-grid-2col"> |
| 150 | <!-- Quick Actions & Stats --> |
| 151 | <div class="dash-section"> |
| 152 | <div class="dash-section-title"> |
| 153 | <svg viewBox="0 0 24 24" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg> |
| 154 | Quick Actions & System |
| 155 | </div> |
| 156 | |
| 157 | <div class="dash-actions-grid"> |
| 158 | <div class="dash-action-pill" onclick="ZFTPD.dashboard.goExplorer()"> |
| 159 | <div class="dash-action-pill-ico blue"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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"/></svg></div> |
| 160 | <div class="dash-action-pill-text">File Explorer</div> |
| 161 | </div> |
| 162 | <div class="dash-action-pill" onclick="ZFTPD.dashboard.goFileManager()"> |
| 163 | <div class="dash-action-pill-ico green"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><line x1="12" y1="3" x2="12" y2="21"/></svg></div> |
| 164 | <div class="dash-action-pill-text">File Manager</div> |
| 165 | </div> |
| 166 | <div class="dash-action-pill" onclick="ZFTPD.dashboard.doUpload()"> |
| 167 | <div class="dash-action-pill-ico orange"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg></div> |
| 168 | <div class="dash-action-pill-text">Upload File</div> |
| 169 | </div> |
| 170 | <div class="dash-action-pill" onclick="ZFTPD.dashboard.goDownloads()"> |
| 171 | <div class="dash-action-pill-ico purple"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 13v8l-4-4"/><path d="m12 21 4-4"/><path d="M4.393 15.269A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.436 8.284"/></svg></div> |
| 172 | <div class="dash-action-pill-text">Downloader</div> |
| 173 | </div> |
| 174 | </div> |
| 175 | |
| 176 | <!-- Stats Rings --> |
| 177 | <div class="dash-stats-flex"> |
| 178 | <div class="dash-stat-ring-container"> |
| 179 | <div class="dash-stat-ring"> |
| 180 | <svg><circle class="dash-stat-ring-bg" cx="40" cy="40" r="35"/><circle id="dash-disk-ring" class="dash-stat-ring-fg" cx="40" cy="40" r="35"/></svg> |
| 181 | <div id="dash-disk-txt" class="dash-stat-ring-val">—</div> |
| 182 | </div> |
| 183 | <div class="dash-stat-label">Storage</div> |
| 184 | <div id="dash-disk-sub" class="dash-stat-sub">Loading…</div> |
| 185 | </div> |
| 186 | |
| 187 | <div class="dash-stat-ring-container"> |
| 188 | <div class="dash-stat-ring"> |
| 189 | <svg><circle class="dash-stat-ring-bg" cx="40" cy="40" r="35"/><circle id="dash-temp-ring" class="dash-stat-ring-fg" cx="40" cy="40" r="35"/></svg> |
| 190 | <div id="dash-temp-txt" class="dash-stat-ring-val">—</div> |
| 191 | </div> |
| 192 | <div class="dash-stat-label">APU Temp</div> |
| 193 | <div id="dash-temp-sub" class="dash-stat-sub">Loading…</div> |
| 194 | </div> |
| 195 | </div> |
| 196 | </div> |
| 197 | |
| 198 | <!-- Recent Files --> |
| 199 | <div class="dash-section"> |
| 200 | <div class="dash-section-title"> |
| 201 | <svg viewBox="0 0 24 24" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg> |
| 202 | Recent Activity |
| 203 | </div> |
| 204 | <div id="dash-recent-list" class="dash-recent-list"></div> |
| 205 | </div> |
| 206 | </div> |
| 207 | |
| 208 | </div> |
| 209 | </div> |
| 210 | </div> |
| 211 | |
| 212 | <!-- ══ VIEW: EXPLORER ═══════════════════════════════════════════════════ --> |
| 213 | <div id="view-explorer" class="view"> |
| 214 | <div class="toolbar"> |
| 215 | <button id="btn-up" class="btn" title="Up"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m18 15-6-6-6 6"/></svg></button> |
| 216 | <button id="btn-ref" class="btn" title="Refresh"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg></button> |
| 217 | <div class="tb-sep"></div> |
| 218 | <button class="btn" onclick="document.getElementById('file-input').click()"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg> Upload</button> |
| 219 | <button class="btn" onclick="ZFTPD.explorer && ZFTPD.modal && ZFTPD.modal.prompt('New Folder','').then(function(n){if(n)ZFTPD.api.mkdir(ZFTPD.state.path,n).then(function(){ZFTPD.explorer.nav(ZFTPD.state.path)})})"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 10v6m-3-3h6"/><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"/></svg> New Folder</button> |
| 220 | <div class="tb-sep"></div> |
| 221 | <div class="vg"> |
| 222 | <button id="vb-grid" class="vb active" title="Grid"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/></svg></button> |
| 223 | <button id="vb-list" class="vb" title="List"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg></button> |
| 224 | <button id="vb-details" class="vb" title="Details"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 3h18v18H3z"/><path d="M3 9h18"/><path d="M3 15h18"/><path d="M9 3v18"/></svg></button> |
| 225 | </div> |
| 226 | <div class="spacer"></div> |
| 227 | <div class="search-wrap"> |
| 228 | <span class="search-ico"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg></span> |
| 229 | <input id="search" class="search" type="text" placeholder="Search files…"> |
| 230 | </div> |
| 231 | <input id="file-input" type="file" multiple style="display:none"> |
| 232 | </div> |
| 233 | <div id="breadcrumb" class="breadcrumb"></div> |
| 234 | <div class="content"> |
| 235 | <div class="fl-header"><span id="fl-count" class="fl-count"></span></div> |
| 236 | <div id="file-list" class="fl vg-grid"></div> |
| 237 | </div> |
| 238 | </div> |
| 239 | |
| 240 | <!-- ══ VIEW: FILE MANAGER ═══════════════════════════════════════════════ --> |
| 241 | <div id="view-filemanager" class="view"> |
| 242 | <div class="content" style="padding-top:8px;"> |
| 243 | <div class="fm-container"> |
| 244 | <!-- Left Panel --> |
| 245 | <div class="fm-panel"> |
| 246 | <div class="fm-panel-header"><span class="fm-label">Source</span><button id="fm-refresh-left" class="btn" style="margin-left:auto;padding:3px 8px;font-size:10px;"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg></button></div> |
| 247 | <div id="fm-left-path" class="fm-panel-path"></div> |
| 248 | <div id="fm-left-body" class="fm-panel-body"></div> |
| 249 | <div id="fm-left-footer" class="fm-panel-footer"></div> |
| 250 | </div> |
| 251 | |
| 252 | <!-- Center Actions --> |
| 253 | <div class="fm-center"> |
| 254 | <button id="fm-copy-right" class="fm-action-btn" title="Copy to right"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m9 18 6-6-6-6"/></svg></button> |
| 255 | <button id="fm-copy-left" class="fm-action-btn" title="Copy to left"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m15 18-6-6 6-6"/></svg></button> |
| 256 | <button id="fm-delete" class="fm-action-btn" title="Delete selected" style="color:var(--er);"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><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"/></svg></button> |
| 257 | </div> |
| 258 | |
| 259 | <!-- Right Panel --> |
| 260 | <div class="fm-panel"> |
| 261 | <div class="fm-panel-header"><span class="fm-label">Destination</span><button id="fm-refresh-right" class="btn" style="margin-left:auto;padding:3px 8px;font-size:10px;"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg></button></div> |
| 262 | <div id="fm-right-path" class="fm-panel-path"></div> |
| 263 | <div id="fm-right-body" class="fm-panel-body"></div> |
| 264 | <div id="fm-right-footer" class="fm-panel-footer"></div> |
| 265 | </div> |
| 266 | </div> |
| 267 | </div> |
| 268 | </div> |
| 269 | |
| 270 | <!-- ══ VIEW: DOWNLOADS ══════════════════════════════════════════════════ --> |
| 271 | <div id="view-downloads" class="view"> |
| 272 | <div class="content"> |
| 273 | <div class="dl-container"> |
| 274 | |
| 275 | <!-- URL Input --> |
| 276 | <div class="dl-input-bar"> |
| 277 | <input id="dl-url" class="dl-url-input" type="text" placeholder="Paste URL, magnet link, Google Drive link…"> |
| 278 | <button class="dl-dest-select" onclick="ZFTPD.downloadMgr.selectDest()"> |
| 279 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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"/></svg> |
| 280 | <span id="dl-dest-path">/</span> |
| 281 | </button> |
| 282 | <button class="dl-start-btn" onclick="ZFTPD.downloadMgr.start()"> |
| 283 | <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> |
| 284 | Download |
| 285 | </button> |
| 286 | </div> |
| 287 | |
| 288 | <!-- Active Downloads --> |
| 289 | <div class="dl-section-header">Active <span id="dl-active-count" class="dl-count">0</span></div> |
| 290 | <div id="dl-active"></div> |
| 291 | |
| 292 | <!-- History --> |
| 293 | <div class="dl-section-header" style="margin-top:20px;">History</div> |
| 294 | <div id="dl-history" class="dl-history"></div> |
| 295 | |
| 296 | </div> |
| 297 | </div> |
| 298 | </div> |
| 299 | |
| 300 | <!-- ══ VIEW: GAMES ══════════════════════════════════════════════════════ --> |
| 301 | <div id="view-games" class="view"> |
| 302 | <div class="content"> |
| 303 | <div class="games-xmb"> |
| 304 | <div class="games-xmb-header"> |
| 305 | <div> |
| 306 | <div class="games-xmb-title">Games</div> |
| 307 | <div class="games-xmb-sub">PS4-style XMB library with install and launch controls</div> |
| 308 | </div> |
| 309 | <div class="games-xmb-tools"> |
| 310 | <button id="games-repair-btn" class="btn">Repair Visibility</button> |
| 311 | <button id="games-refresh-btn" class="btn">Refresh</button> |
| 312 | </div> |
| 313 | </div> |
| 314 | |
| 315 | <div class="games-actions"> |
| 316 | <div class="games-actions-row"> |
| 317 | <input id="games-install-path" class="games-input" type="text" placeholder="/mnt/usb0/MyGame.pkg"> |
| 318 | <button id="games-install-btn" class="btn">Install</button> |
| 319 | <button id="games-reinstall-btn" class="btn">Reinstall</button> |
| 320 | <button id="games-scan-btn" class="btn">Scan PKG/exFAT</button> |
| 321 | </div> |
| 322 | <div id="games-install-status" class="games-install-status">No active install task</div> |
| 323 | </div> |
| 324 | |
| 325 | <section class="games-shelf"> |
| 326 | <div class="games-shelf-title">Installed Apps</div> |
| 327 | <div id="games-installed-list" class="games-xmb-row"></div> |
| 328 | </section> |
| 329 | |
| 330 | <section class="games-shelf"> |
| 331 | <div class="games-shelf-title">Detected Game Images</div> |
| 332 | <div id="games-images-list" class="games-xmb-row"></div> |
| 333 | </section> |
| 334 | </div> |
| 335 | </div> |
| 336 | </div> |
| 337 | |
| 338 | <!-- ══ VIEW: SETTINGS ═══════════════════════════════════════════════════ --> |
| 339 | <div id="view-settings" class="view"> |
| 340 | <div class="content"> |
| 341 | <div id="settings-content"></div> |
| 342 | </div> |
| 343 | </div> |
| 344 | |
| 345 | <!-- ══ OVERLAYS ═════════════════════════════════════════════════════════ --> |
| 346 | |
| 347 | <!-- Context Menu --> |
| 348 | <div id="ctx-menu" class="ctx"></div> |
| 349 | |
| 350 | <!-- Drop Overlay --> |
| 351 | <div id="drop-overlay" class="drop"> |
| 352 | <div class="drop-card"> |
| 353 | <button id="drop-close" class="drop-close">×</button> |
| 354 | <div class="d-ico"><svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg></div> |
| 355 | <div class="d-title">Drop files to upload</div> |
| 356 | <div id="drop-sub" class="d-sub">Release to start upload</div> |
| 357 | <div class="d-bar"><div id="drop-bar" class="d-bf"></div></div> |
| 358 | </div> |
| 359 | </div> |
| 360 | |
| 361 | <!-- Transfer Tray --> |
| 362 | <div id="xfer-tray" class="xfer-tray"></div> |
| 363 | |
| 364 | <!-- Toast Notifications --> |
| 365 | <div id="toast-wrap" class="toast-wrap"></div> |
| 366 | |
| 367 | <!-- ══ MODULAR JS ═══════════════════════════════════════════════════════ --> |
| 368 | <script src="js/utils.js?v=3"></script> |
| 369 | <script src="js/icons.js?v=3"></script> |
| 370 | <script src="js/api.js?v=3"></script> |
| 371 | <script src="js/views/dashboard.js?v=3"></script> |
| 372 | <script src="js/views/explorer.js?v=3"></script> |
| 373 | <script src="js/views/file-manager.js?v=3"></script> |
| 374 | <script src="js/views/download-mgr.js?v=3"></script> |
| 375 | <script src="js/views/games.js?v=3"></script> |
| 376 | <script src="js/views/settings.js?v=3"></script> |
| 377 | <script src="js/app.js?v=3"></script> |
| 378 | |
| 379 | </body> |
| 380 | </html> |
| 381 |