Seregon/zftpd

Zero-copy FTP/HTTP Daemon compatible with all POSIX systems

C/11.0 KB/No license
web/index.html
zftpd / web / index.html
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">&#x25BE;</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 &rarr;</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">&mdash;</div>
182 </div>
183 <div class="dash-stat-label">Storage</div>
184 <div id="dash-disk-sub" class="dash-stat-sub">Loading&hellip;</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">&mdash;</div>
191 </div>
192 <div class="dash-stat-label">APU Temp</div>
193 <div id="dash-temp-sub" class="dash-stat-sub">Loading&hellip;</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&hellip;">
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&hellip;">
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">&times;</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