Spaces:
Paused
Paused
Update public/index.html
Browse files- public/index.html +195 -48
public/index.html
CHANGED
|
@@ -6,6 +6,18 @@
|
|
| 6 |
<title>PixelDrive</title>
|
| 7 |
<link rel="manifest" href="/manifest.json">
|
| 8 |
<meta name="theme-color" content="#4f46e5">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
<style>
|
| 10 |
:root { --bg-dark:#0f0f13; --bg-panel:#18181b; --bg-hover:#27272a; --primary:#6366f1; --primary-hover:#4f46e5; --text-main:#f4f4f5; --text-muted:#a1a1aa; --border:#27272a; --danger:#ef4444; --success:#22c55e; --radius:8px; --font:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; --sidebar-width:260px; }
|
| 11 |
* { box-sizing:border-box; margin:0; padding:0; outline:none; -webkit-tap-highlight-color:transparent; }
|
|
@@ -39,6 +51,21 @@
|
|
| 39 |
.nav-item { display:flex; align-items:center; gap:0.75rem; padding:0.875rem; border-radius:var(--radius); color:var(--text-muted); cursor:pointer; margin-bottom:0.25rem; font-weight:500; }
|
| 40 |
.nav-item.active { background:var(--primary); color:white; }
|
| 41 |
.nav-section { font-size:0.75rem; text-transform:uppercase; letter-spacing:0.05em; color:var(--text-muted); margin:1.5rem 0 0.5rem 0.75rem; font-weight:600; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
main { flex:1; display:flex; flex-direction:column; min-width:0; }
|
| 43 |
.toolbar { padding:0.75rem 1rem; display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--border); background:var(--bg-panel); gap:0.5rem; flex-shrink:0; }
|
| 44 |
.breadcrumb { display:flex; align-items:center; gap:0.5rem; font-size:1rem; font-weight:600; flex:1; min-width:0; overflow:hidden; }
|
|
@@ -128,6 +155,11 @@
|
|
| 128 |
.sidebar-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:90; }
|
| 129 |
.sidebar-overlay.active { display:block; }
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
@media (max-width:768px) {
|
| 132 |
.menu-btn { display:flex; }
|
| 133 |
.user-badge { display:none; }
|
|
@@ -166,27 +198,28 @@
|
|
| 166 |
<symbol id="icon-link" viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></symbol>
|
| 167 |
<symbol id="icon-user-plus" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="23" y1="11" x2="17" y2="11"></line></symbol>
|
| 168 |
<symbol id="icon-menu" viewBox="0 0 24 24"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></symbol>
|
|
|
|
| 169 |
</svg>
|
| 170 |
|
| 171 |
<div id="auth-screen">
|
| 172 |
<div class="auth-card">
|
| 173 |
<div class="auth-header"><h1><svg class="icon"><use href="#icon-drive"></use></svg> PixelDrive</h1></div>
|
| 174 |
<div id="login-form">
|
| 175 |
-
<div class="form-group"><label>Utilisateur</label><input type="text" id="login-user" class="form-input" autocomplete="username"></div>
|
| 176 |
-
<div class="form-group"><label>Mot de passe</label><input type="password" id="login-pass" class="form-input" autocomplete="current-password"></div>
|
| 177 |
-
<button class="btn" onclick="app.login()">Se connecter</button>
|
| 178 |
-
<div class="auth-switch"
|
| 179 |
</div>
|
| 180 |
<div id="register-form" style="display:none;">
|
| 181 |
-
<div class="form-group"><label>Utilisateur</label><input type="text" id="reg-user" class="form-input"></div>
|
| 182 |
-
<div class="form-group"><label>Mot de passe</label><input type="password" id="reg-pass" class="form-input"></div>
|
| 183 |
-
<div class="form-group"><label>Confirmer</label><input type="password" id="reg-pass2" class="form-input"></div>
|
| 184 |
<div class="form-group" style="display:flex;align-items:center;gap:0.75rem;">
|
| 185 |
<input type="checkbox" id="reg-tos" style="width:20px;height:20px;accent-color:var(--primary);">
|
| 186 |
-
<label for="reg-tos" style="margin:0;font-size:0.875rem;">J'accepte les <a href="#" onclick="app.showTos(false); return false;">CGU</a></label>
|
| 187 |
</div>
|
| 188 |
-
<button class="btn" onclick="app.register()">Créer compte</button>
|
| 189 |
-
<div class="auth-switch"
|
| 190 |
</div>
|
| 191 |
</div>
|
| 192 |
</div>
|
|
@@ -205,25 +238,38 @@
|
|
| 205 |
<div class="sidebar-overlay" onclick="app.toggleSidebar()"></div>
|
| 206 |
<div class="main-container">
|
| 207 |
<aside id="sidebar">
|
| 208 |
-
<div class="nav-section">Navigation</div>
|
| 209 |
-
<div class="nav-item active" onclick="app.navTo('files')" id="nav-files"><svg class="icon"><use href="#icon-folder"></use></svg> Mes Fichiers</div>
|
| 210 |
-
<div class="nav-item" onclick="app.navTo('shared')" id="nav-shared"><svg class="icon"><use href="#icon-share"></use></svg> Partagés</div>
|
| 211 |
-
<div class="nav-item" onclick="app.navTo('recent')" id="nav-recent"><svg class="icon"><use href="#icon-clock"></use></svg> Récents</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
</aside>
|
| 213 |
<main>
|
| 214 |
<div class="toolbar">
|
| 215 |
<div class="breadcrumb" id="breadcrumb"><span onclick="app.goRoot()">Mes Fichiers</span></div>
|
| 216 |
<div class="actions" id="toolbar-actions">
|
| 217 |
-
<button class="btn-primary" onclick="document.getElementById('file-input').click()"><svg class="icon icon-sm"><use href="#icon-upload"></use></svg> <span class="text">Uploader</span></button>
|
| 218 |
<input type="file" id="file-input" style="display:none;" multiple onchange="app.handleUpload(this)">
|
| 219 |
-
<button class="btn-icon" onclick="app.createFolder()"><svg class="icon"><use href="#icon-plus"></use></svg></button>
|
| 220 |
<select id="sort-select" class="sort-select" onchange="app.setSort(this.value)">
|
| 221 |
-
<option value="name-asc">Nom (A-Z)</option>
|
| 222 |
-
<option value="name-desc">Nom (Z-A)</option>
|
| 223 |
-
<option value="size-desc">Taille</option>
|
| 224 |
-
<option value="date-desc">Date</option>
|
| 225 |
</select>
|
| 226 |
-
<button class="btn-icon" onclick="app.refresh()"><svg class="icon"><use href="#icon-refresh"></use></svg></button>
|
| 227 |
</div>
|
| 228 |
</div>
|
| 229 |
<div class="file-area" id="file-area"></div>
|
|
@@ -231,10 +277,9 @@
|
|
| 231 |
</div>
|
| 232 |
</div>
|
| 233 |
|
| 234 |
-
<!-- ✅ OVERLAY UPLOAD MULTI avec vitesse -->
|
| 235 |
<div id="upload-overlay">
|
| 236 |
<div style="display:flex;justify-content:space-between;align-items:center;gap:1rem;">
|
| 237 |
-
<strong>Transfert <span id="upload-count"></span></strong>
|
| 238 |
<button class="btn-icon" style="width:32px;height:32px;" onclick="app.cancelUpload()"><svg class="icon icon-sm"><use href="#icon-x"></use></svg></button>
|
| 239 |
</div>
|
| 240 |
<div class="progress-bar-bg"><div class="progress-bar-fill" id="upload-progress"></div></div>
|
|
@@ -248,8 +293,8 @@
|
|
| 248 |
<div class="modal">
|
| 249 |
<div id="preview-container"></div>
|
| 250 |
<div class="preview-controls">
|
| 251 |
-
<button class="btn btn-secondary" onclick="app.closePreview()">Fermer</button>
|
| 252 |
-
<button class="btn btn-primary" id="preview-download-btn"><svg class="icon icon-sm"><use href="#icon-download"></use></svg> Télécharger</button>
|
| 253 |
</div>
|
| 254 |
</div>
|
| 255 |
</div>
|
|
@@ -257,53 +302,51 @@
|
|
| 257 |
<div class="modal-overlay" id="share-modal" onclick="if(event.target === this) app.closeShare()">
|
| 258 |
<div class="modal">
|
| 259 |
<div class="modal-header">
|
| 260 |
-
<div class="modal-title"><svg class="icon"><use href="#icon-share"></use></svg> Partager le fichier</div>
|
| 261 |
<button class="close-modal" onclick="app.closeShare()"><svg class="icon"><use href="#icon-x"></use></svg></button>
|
| 262 |
</div>
|
| 263 |
<div class="share-section">
|
| 264 |
-
<label class="share-label"><svg class="icon icon-sm"><use href="#icon-eye"></use></svg> Lien de prévisualisation</label>
|
| 265 |
-
<p class="share-hint">Page web avec lecteur intégré</p>
|
| 266 |
<div class="share-input-group">
|
| 267 |
-
<input type="text" id="share-preview-url" readonly class="form-input share-input" value="
|
| 268 |
<button class="btn-icon share-copy-btn" data-type="preview"><svg class="icon"><use href="#icon-link"></use></svg></button>
|
| 269 |
</div>
|
| 270 |
</div>
|
| 271 |
<div class="share-section">
|
| 272 |
-
<label class="share-label"><svg class="icon icon-sm"><use href="#icon-download"></use></svg> Lien de téléchargement direct</label>
|
| 273 |
-
<p class="share-hint">Télécharge le fichier immédiatement</p>
|
| 274 |
<div class="share-input-group">
|
| 275 |
-
<input type="text" id="share-download-url" readonly class="form-input share-input" value="
|
| 276 |
<button class="btn-icon share-copy-btn" data-type="download"><svg class="icon"><use href="#icon-link"></use></svg></button>
|
| 277 |
</div>
|
| 278 |
</div>
|
| 279 |
<div class="share-section">
|
| 280 |
-
<label class="share-label"><svg class="icon icon-sm"><use href="#icon-link"></use></svg> Code d'intégration HTML</label>
|
| 281 |
-
<p class="share-hint">À coller sur un site web ou un forum</p>
|
| 282 |
<div class="share-input-group">
|
| 283 |
-
<input type="text" id="share-embed-code" readonly class="form-input share-input" value="
|
| 284 |
<button class="btn-icon share-copy-btn" data-type="embed"><svg class="icon"><use href="#icon-link"></use></svg></button>
|
| 285 |
</div>
|
| 286 |
</div>
|
| 287 |
-
<div class="share-footer">Ces liens expirent dans 7 jours</div>
|
| 288 |
</div>
|
| 289 |
</div>
|
| 290 |
|
| 291 |
-
<!-- ✅ CGU EMBEDDÉE (iframe tos.html) -->
|
| 292 |
<div class="modal-overlay" id="tos-modal">
|
| 293 |
<div class="modal">
|
| 294 |
<div class="modal-header">
|
| 295 |
-
<div class="modal-title">Conditions Générales d'Utilisation</div>
|
| 296 |
<button class="close-modal" onclick="app.hideTos()"><svg class="icon"><use href="#icon-x"></use></svg></button>
|
| 297 |
</div>
|
| 298 |
<iframe id="tos-frame" src="/tos.html" class="tos-frame" title="CGU PixelDrive"></iframe>
|
| 299 |
<div style="display:flex;gap:1rem;">
|
| 300 |
-
<button class="btn btn-secondary" style="flex:1;" onclick="app.hideTos()">Annuler</button>
|
| 301 |
-
<button class="btn btn-primary" id="tos-accept-btn" style="display:none;flex:1;" onclick="app.acceptTos()">J'accepte</button>
|
| 302 |
</div>
|
| 303 |
</div>
|
| 304 |
</div>
|
| 305 |
|
| 306 |
-
<!-- ✅ DIALOGUE CUSTOM (remplace confirm/prompt natifs) -->
|
| 307 |
<div class="modal-overlay" id="dialog-modal">
|
| 308 |
<div class="modal" style="max-width:420px;">
|
| 309 |
<div class="modal-header"><div class="modal-title" id="dialog-title"></div></div>
|
|
@@ -312,24 +355,128 @@
|
|
| 312 |
<input type="text" id="dialog-input" class="form-input">
|
| 313 |
</div>
|
| 314 |
<div class="dialog-actions">
|
| 315 |
-
<button class="btn btn-secondary" id="dialog-cancel" onclick="app._dialogClose(false)">Annuler</button>
|
| 316 |
<button class="btn" id="dialog-ok" onclick="app._dialogClose(true)">Confirmer</button>
|
| 317 |
</div>
|
| 318 |
</div>
|
| 319 |
</div>
|
| 320 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
<div id="context-menu">
|
| 322 |
-
<div class="ctx-item" id="ctx-preview"><svg class="icon icon-sm"><use href="#icon-eye"></use></svg> Aperçu</div>
|
| 323 |
-
<div class="ctx-item" id="ctx-download"><svg class="icon icon-sm"><use href="#icon-download"></use></svg> Télécharger</div>
|
| 324 |
-
<div class="ctx-item" id="ctx-share"><svg class="icon icon-sm"><use href="#icon-link"></use></svg> Lien public</div>
|
| 325 |
-
<div class="ctx-item" id="ctx-share-user"><svg class="icon icon-sm"><use href="#icon-user-plus"></use></svg> Partager à un utilisateur</div>
|
| 326 |
<div class="ctx-sep"></div>
|
| 327 |
-
<div class="ctx-item" id="ctx-remove" style="color:var(--danger);"><svg class="icon icon-sm"><use href="#icon-x"></use></svg> Retirer de ma liste</div>
|
| 328 |
-
<div class="ctx-item" id="ctx-delete" style="color:var(--danger);"><svg class="icon icon-sm"><use href="#icon-trash"></use></svg> Supprimer</div>
|
| 329 |
</div>
|
| 330 |
|
| 331 |
<div id="toast-container"></div>
|
| 332 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
<script src="/app.js"></script>
|
| 334 |
</body>
|
| 335 |
</html>
|
|
|
|
| 6 |
<title>PixelDrive</title>
|
| 7 |
<link rel="manifest" href="/manifest.json">
|
| 8 |
<meta name="theme-color" content="#4f46e5">
|
| 9 |
+
<!-- ✅ OPEN GRAPH : bannière visible dans WhatsApp/Messages/Discord, PAS sur le site -->
|
| 10 |
+
<meta property="og:type" content="website">
|
| 11 |
+
<meta property="og:site_name" content="PixelDrive">
|
| 12 |
+
<meta property="og:title" content="PixelDrive — Stockage cloud chiffré Zero-Knowledge">
|
| 13 |
+
<meta property="og:description" content="Importez, chiffrez (AES-256-GCM) et partagez vos fichiers en toute confidentialité.">
|
| 14 |
+
<meta property="og:image" content="https://nathmen12-pixel-drive.hf.space/banner.png">
|
| 15 |
+
<meta property="og:url" content="https://nathmen12-pixel-drive.hf.space/">
|
| 16 |
+
<meta name="twitter:card" content="summary_large_image">
|
| 17 |
+
<meta name="twitter:title" content="PixelDrive — Stockage cloud chiffré">
|
| 18 |
+
<meta name="twitter:description" content="Partage sécurisé de fichiers chiffrés.">
|
| 19 |
+
<meta name="twitter:image" content="https://nathmen12-pixel-drive.hf.space/banner.png">
|
| 20 |
+
<link rel="icon" type="image/png" href="/banner.png">
|
| 21 |
<style>
|
| 22 |
:root { --bg-dark:#0f0f13; --bg-panel:#18181b; --bg-hover:#27272a; --primary:#6366f1; --primary-hover:#4f46e5; --text-main:#f4f4f5; --text-muted:#a1a1aa; --border:#27272a; --danger:#ef4444; --success:#22c55e; --radius:8px; --font:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; --sidebar-width:260px; }
|
| 23 |
* { box-sizing:border-box; margin:0; padding:0; outline:none; -webkit-tap-highlight-color:transparent; }
|
|
|
|
| 51 |
.nav-item { display:flex; align-items:center; gap:0.75rem; padding:0.875rem; border-radius:var(--radius); color:var(--text-muted); cursor:pointer; margin-bottom:0.25rem; font-weight:500; }
|
| 52 |
.nav-item.active { background:var(--primary); color:white; }
|
| 53 |
.nav-section { font-size:0.75rem; text-transform:uppercase; letter-spacing:0.05em; color:var(--text-muted); margin:1.5rem 0 0.5rem 0.75rem; font-weight:600; }
|
| 54 |
+
|
| 55 |
+
.sidebar-footer { margin-top:auto; padding-top:1rem; border-top:1px solid var(--border); display:flex; align-items:center; gap:0.75rem; }
|
| 56 |
+
.avatar-btn { width:40px; height:40px; border-radius:50%; overflow:hidden; background:var(--primary); border:none; cursor:pointer; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
| 57 |
+
.avatar-btn img { width:100%; height:100%; object-fit:cover; }
|
| 58 |
+
.avatar-initial { color:white; font-weight:700; font-size:1rem; display:flex; align-items:center; justify-content:center; width:100%; height:100%; }
|
| 59 |
+
.avatar-initial.big { font-size:1.5rem; }
|
| 60 |
+
.footer-info { flex:1; min-width:0; display:flex; flex-direction:column; }
|
| 61 |
+
.footer-username { font-size:0.875rem; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
| 62 |
+
.footer-sub { font-size:0.7rem; color:var(--text-muted); }
|
| 63 |
+
.avatar-preview { width:72px; height:72px; border-radius:50%; overflow:hidden; background:var(--primary); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
|
| 64 |
+
.avatar-preview img { width:100%; height:100%; object-fit:cover; }
|
| 65 |
+
.settings-avatar-row { display:flex; gap:1rem; align-items:center; margin-bottom:1.25rem; }
|
| 66 |
+
.settings-section { margin-bottom:1.25rem; }
|
| 67 |
+
.owner-badge img { width:14px; height:14px; border-radius:50%; object-fit:cover; vertical-align:-2px; margin-right:4px; }
|
| 68 |
+
|
| 69 |
main { flex:1; display:flex; flex-direction:column; min-width:0; }
|
| 70 |
.toolbar { padding:0.75rem 1rem; display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--border); background:var(--bg-panel); gap:0.5rem; flex-shrink:0; }
|
| 71 |
.breadcrumb { display:flex; align-items:center; gap:0.5rem; font-size:1rem; font-weight:600; flex:1; min-width:0; overflow:hidden; }
|
|
|
|
| 155 |
.sidebar-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:90; }
|
| 156 |
.sidebar-overlay.active { display:block; }
|
| 157 |
|
| 158 |
+
body.light { --bg-dark:#f5f5f6; --bg-panel:#ffffff; --bg-hover:#ececee; --text-main:#17171b; --text-muted:#5b5b66; --border:#d8d8de; }
|
| 159 |
+
body.light .card-thumb { background:#e8e8ec; }
|
| 160 |
+
body.light .card-thumb.folder-thumb { background:#e0e0e6; }
|
| 161 |
+
body.light .form-input, body.light .share-input, body.light .share-link-input, body.light .sort-select { background:#f0f0f2; }
|
| 162 |
+
|
| 163 |
@media (max-width:768px) {
|
| 164 |
.menu-btn { display:flex; }
|
| 165 |
.user-badge { display:none; }
|
|
|
|
| 198 |
<symbol id="icon-link" viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></symbol>
|
| 199 |
<symbol id="icon-user-plus" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="23" y1="11" x2="17" y2="11"></line></symbol>
|
| 200 |
<symbol id="icon-menu" viewBox="0 0 24 24"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></symbol>
|
| 201 |
+
<symbol id="icon-settings" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 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 1 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 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h.01a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h.01a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v.01a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path></symbol>
|
| 202 |
</svg>
|
| 203 |
|
| 204 |
<div id="auth-screen">
|
| 205 |
<div class="auth-card">
|
| 206 |
<div class="auth-header"><h1><svg class="icon"><use href="#icon-drive"></use></svg> PixelDrive</h1></div>
|
| 207 |
<div id="login-form">
|
| 208 |
+
<div class="form-group"><label data-i18n="username">Utilisateur</label><input type="text" id="login-user" class="form-input" autocomplete="username"></div>
|
| 209 |
+
<div class="form-group"><label data-i18n="password">Mot de passe</label><input type="password" id="login-pass" class="form-input" autocomplete="current-password"></div>
|
| 210 |
+
<button class="btn" onclick="app.login()"><span data-i18n="login_btn">Se connecter</span></button>
|
| 211 |
+
<div class="auth-switch" id="switch-login"></div>
|
| 212 |
</div>
|
| 213 |
<div id="register-form" style="display:none;">
|
| 214 |
+
<div class="form-group"><label data-i18n="username">Utilisateur</label><input type="text" id="reg-user" class="form-input"></div>
|
| 215 |
+
<div class="form-group"><label data-i18n="password">Mot de passe</label><input type="password" id="reg-pass" class="form-input"></div>
|
| 216 |
+
<div class="form-group"><label data-i18n="confirm_pass">Confirmer</label><input type="password" id="reg-pass2" class="form-input"></div>
|
| 217 |
<div class="form-group" style="display:flex;align-items:center;gap:0.75rem;">
|
| 218 |
<input type="checkbox" id="reg-tos" style="width:20px;height:20px;accent-color:var(--primary);">
|
| 219 |
+
<label for="reg-tos" style="margin:0;font-size:0.875rem;"><span data-i18n="tos_accept">J'accepte les CGU</span> · <a href="#" onclick="app.showTos(false); return false;">CGU</a></label>
|
| 220 |
</div>
|
| 221 |
+
<button class="btn" onclick="app.register()"><span data-i18n="register_btn">Créer compte</span></button>
|
| 222 |
+
<div class="auth-switch" id="switch-register"></div>
|
| 223 |
</div>
|
| 224 |
</div>
|
| 225 |
</div>
|
|
|
|
| 238 |
<div class="sidebar-overlay" onclick="app.toggleSidebar()"></div>
|
| 239 |
<div class="main-container">
|
| 240 |
<aside id="sidebar">
|
| 241 |
+
<div class="nav-section" data-i18n="nav_section">Navigation</div>
|
| 242 |
+
<div class="nav-item active" onclick="app.navTo('files')" id="nav-files"><svg class="icon"><use href="#icon-folder"></use></svg> <span data-i18n="nav_files">Mes Fichiers</span></div>
|
| 243 |
+
<div class="nav-item" onclick="app.navTo('shared')" id="nav-shared"><svg class="icon"><use href="#icon-share"></use></svg> <span data-i18n="nav_shared">Partagés</span></div>
|
| 244 |
+
<div class="nav-item" onclick="app.navTo('recent')" id="nav-recent"><svg class="icon"><use href="#icon-clock"></use></svg> <span data-i18n="nav_recent">Récents</span></div>
|
| 245 |
+
<div class="sidebar-footer">
|
| 246 |
+
<button class="avatar-btn" onclick="app.openSettings()" aria-label="Profil">
|
| 247 |
+
<img id="avatar-img" alt="" style="display:none;">
|
| 248 |
+
<span id="avatar-initial" class="avatar-initial">?</span>
|
| 249 |
+
</button>
|
| 250 |
+
<div class="footer-info">
|
| 251 |
+
<span class="footer-username" id="footer-username">User</span>
|
| 252 |
+
<span class="footer-sub" data-i18n="settings">Paramètres</span>
|
| 253 |
+
</div>
|
| 254 |
+
<button class="btn-icon" onclick="app.openSettings()" title="Paramètres" data-i18n-title="settings">
|
| 255 |
+
<svg class="icon"><use href="#icon-settings"></use></svg>
|
| 256 |
+
</button>
|
| 257 |
+
</div>
|
| 258 |
</aside>
|
| 259 |
<main>
|
| 260 |
<div class="toolbar">
|
| 261 |
<div class="breadcrumb" id="breadcrumb"><span onclick="app.goRoot()">Mes Fichiers</span></div>
|
| 262 |
<div class="actions" id="toolbar-actions">
|
| 263 |
+
<button class="btn-primary" onclick="document.getElementById('file-input').click()"><svg class="icon icon-sm"><use href="#icon-upload"></use></svg> <span class="text" data-i18n="upload">Uploader</span></button>
|
| 264 |
<input type="file" id="file-input" style="display:none;" multiple onchange="app.handleUpload(this)">
|
| 265 |
+
<button class="btn-icon" onclick="app.createFolder()" title="Nouveau Dossier" data-i18n-title="new_folder"><svg class="icon"><use href="#icon-plus"></use></svg></button>
|
| 266 |
<select id="sort-select" class="sort-select" onchange="app.setSort(this.value)">
|
| 267 |
+
<option value="name-asc" data-i18n="sort_name_asc">Nom (A-Z)</option>
|
| 268 |
+
<option value="name-desc" data-i18n="sort_name_desc">Nom (Z-A)</option>
|
| 269 |
+
<option value="size-desc" data-i18n="sort_size">Taille</option>
|
| 270 |
+
<option value="date-desc" data-i18n="sort_date">Date</option>
|
| 271 |
</select>
|
| 272 |
+
<button class="btn-icon" onclick="app.refresh()" title="Rafraîchir" data-i18n-title="refresh"><svg class="icon"><use href="#icon-refresh"></use></svg></button>
|
| 273 |
</div>
|
| 274 |
</div>
|
| 275 |
<div class="file-area" id="file-area"></div>
|
|
|
|
| 277 |
</div>
|
| 278 |
</div>
|
| 279 |
|
|
|
|
| 280 |
<div id="upload-overlay">
|
| 281 |
<div style="display:flex;justify-content:space-between;align-items:center;gap:1rem;">
|
| 282 |
+
<strong><span data-i18n="transfer">Transfert</span> <span id="upload-count"></span></strong>
|
| 283 |
<button class="btn-icon" style="width:32px;height:32px;" onclick="app.cancelUpload()"><svg class="icon icon-sm"><use href="#icon-x"></use></svg></button>
|
| 284 |
</div>
|
| 285 |
<div class="progress-bar-bg"><div class="progress-bar-fill" id="upload-progress"></div></div>
|
|
|
|
| 293 |
<div class="modal">
|
| 294 |
<div id="preview-container"></div>
|
| 295 |
<div class="preview-controls">
|
| 296 |
+
<button class="btn btn-secondary" onclick="app.closePreview()"><span data-i18n="close">Fermer</span></button>
|
| 297 |
+
<button class="btn btn-primary" id="preview-download-btn"><svg class="icon icon-sm"><use href="#icon-download"></use></svg> <span data-i18n="download">Télécharger</span></button>
|
| 298 |
</div>
|
| 299 |
</div>
|
| 300 |
</div>
|
|
|
|
| 302 |
<div class="modal-overlay" id="share-modal" onclick="if(event.target === this) app.closeShare()">
|
| 303 |
<div class="modal">
|
| 304 |
<div class="modal-header">
|
| 305 |
+
<div class="modal-title"><svg class="icon"><use href="#icon-share"></use></svg> <span data-i18n="share_title">Partager le fichier</span></div>
|
| 306 |
<button class="close-modal" onclick="app.closeShare()"><svg class="icon"><use href="#icon-x"></use></svg></button>
|
| 307 |
</div>
|
| 308 |
<div class="share-section">
|
| 309 |
+
<label class="share-label"><svg class="icon icon-sm"><use href="#icon-eye"></use></svg> <span data-i18n="share_preview">Lien de prévisualisation</span></label>
|
| 310 |
+
<p class="share-hint" data-i18n="share_preview_hint">Page web avec lecteur intégré</p>
|
| 311 |
<div class="share-input-group">
|
| 312 |
+
<input type="text" id="share-preview-url" readonly class="form-input share-input" value="...">
|
| 313 |
<button class="btn-icon share-copy-btn" data-type="preview"><svg class="icon"><use href="#icon-link"></use></svg></button>
|
| 314 |
</div>
|
| 315 |
</div>
|
| 316 |
<div class="share-section">
|
| 317 |
+
<label class="share-label"><svg class="icon icon-sm"><use href="#icon-download"></use></svg> <span data-i18n="share_dl">Lien de téléchargement direct</span></label>
|
| 318 |
+
<p class="share-hint" data-i18n="share_dl_hint">Télécharge le fichier immédiatement</p>
|
| 319 |
<div class="share-input-group">
|
| 320 |
+
<input type="text" id="share-download-url" readonly class="form-input share-input" value="...">
|
| 321 |
<button class="btn-icon share-copy-btn" data-type="download"><svg class="icon"><use href="#icon-link"></use></svg></button>
|
| 322 |
</div>
|
| 323 |
</div>
|
| 324 |
<div class="share-section">
|
| 325 |
+
<label class="share-label"><svg class="icon icon-sm"><use href="#icon-link"></use></svg> <span data-i18n="share_embed">Code d'intégration HTML</span></label>
|
| 326 |
+
<p class="share-hint" data-i18n="share_embed_hint">À coller sur un site web ou un forum</p>
|
| 327 |
<div class="share-input-group">
|
| 328 |
+
<input type="text" id="share-embed-code" readonly class="form-input share-input" value="...">
|
| 329 |
<button class="btn-icon share-copy-btn" data-type="embed"><svg class="icon"><use href="#icon-link"></use></svg></button>
|
| 330 |
</div>
|
| 331 |
</div>
|
| 332 |
+
<div class="share-footer" data-i18n="share_expiry">Ces liens expirent dans 7 jours</div>
|
| 333 |
</div>
|
| 334 |
</div>
|
| 335 |
|
|
|
|
| 336 |
<div class="modal-overlay" id="tos-modal">
|
| 337 |
<div class="modal">
|
| 338 |
<div class="modal-header">
|
| 339 |
+
<div class="modal-title" data-i18n="tos_title">Conditions Générales d'Utilisation</div>
|
| 340 |
<button class="close-modal" onclick="app.hideTos()"><svg class="icon"><use href="#icon-x"></use></svg></button>
|
| 341 |
</div>
|
| 342 |
<iframe id="tos-frame" src="/tos.html" class="tos-frame" title="CGU PixelDrive"></iframe>
|
| 343 |
<div style="display:flex;gap:1rem;">
|
| 344 |
+
<button class="btn btn-secondary" style="flex:1;" onclick="app.hideTos()"><span data-i18n="cancel">Annuler</span></button>
|
| 345 |
+
<button class="btn btn-primary" id="tos-accept-btn" style="display:none;flex:1;" onclick="app.acceptTos()"><span data-i18n="tos_accept_btn">J'accepte</span></button>
|
| 346 |
</div>
|
| 347 |
</div>
|
| 348 |
</div>
|
| 349 |
|
|
|
|
| 350 |
<div class="modal-overlay" id="dialog-modal">
|
| 351 |
<div class="modal" style="max-width:420px;">
|
| 352 |
<div class="modal-header"><div class="modal-title" id="dialog-title"></div></div>
|
|
|
|
| 355 |
<input type="text" id="dialog-input" class="form-input">
|
| 356 |
</div>
|
| 357 |
<div class="dialog-actions">
|
| 358 |
+
<button class="btn btn-secondary" id="dialog-cancel" onclick="app._dialogClose(false)"><span data-i18n="cancel">Annuler</span></button>
|
| 359 |
<button class="btn" id="dialog-ok" onclick="app._dialogClose(true)">Confirmer</button>
|
| 360 |
</div>
|
| 361 |
</div>
|
| 362 |
</div>
|
| 363 |
|
| 364 |
+
<div class="modal-overlay" id="settings-modal" onclick="if(event.target === this) app.closeSettings()">
|
| 365 |
+
<div class="modal" style="max-width:460px;">
|
| 366 |
+
<div class="modal-header">
|
| 367 |
+
<div class="modal-title"><svg class="icon"><use href="#icon-settings"></use></svg> <span data-i18n="settings">Paramètres</span></div>
|
| 368 |
+
<button class="close-modal" onclick="app.closeSettings()"><svg class="icon"><use href="#icon-x"></use></svg></button>
|
| 369 |
+
</div>
|
| 370 |
+
<div class="settings-section">
|
| 371 |
+
<label class="share-label" data-i18n="profile">Profil</label>
|
| 372 |
+
<div class="settings-avatar-row">
|
| 373 |
+
<div class="avatar-preview"><img id="settings-avatar-img" alt="" style="display:none;"><span id="settings-avatar-initial" class="avatar-initial big">?</span></div>
|
| 374 |
+
<div style="flex:1;display:flex;flex-direction:column;gap:0.5rem;">
|
| 375 |
+
<button class="btn" style="padding:0.6rem;" onclick="document.getElementById('avatar-input').click()"><span data-i18n="change_avatar">Changer l'avatar</span></button>
|
| 376 |
+
<input type="file" id="avatar-input" accept="image/*" style="display:none;" onchange="app.uploadAvatar(this)">
|
| 377 |
+
<button class="btn btn-secondary" style="padding:0.6rem;" onclick="app.removeAvatar()"><span data-i18n="remove_avatar">Retirer</span></button>
|
| 378 |
+
</div>
|
| 379 |
+
</div>
|
| 380 |
+
</div>
|
| 381 |
+
<div class="settings-section">
|
| 382 |
+
<label class="share-label" data-i18n="language">Langue</label>
|
| 383 |
+
<select id="set-lang" class="form-input" onchange="app.setLang(this.value)"><option value="fr">Français</option><option value="en">English</option></select>
|
| 384 |
+
</div>
|
| 385 |
+
<div class="settings-section">
|
| 386 |
+
<label class="share-label" data-i18n="theme">Thème</label>
|
| 387 |
+
<select id="set-theme" class="form-input" onchange="app.setTheme(this.value)"><option value="dark" data-i18n="theme_dark">Sombre</option><option value="light" data-i18n="theme_light">Clair</option></select>
|
| 388 |
+
</div>
|
| 389 |
+
<div class="settings-section">
|
| 390 |
+
<label class="share-label" data-i18n="default_sort">Tri par défaut</label>
|
| 391 |
+
<select id="set-sort" class="form-input" onchange="app.setDefSort(this.value)">
|
| 392 |
+
<option value="name-asc" data-i18n="sort_name_asc">Nom (A-Z)</option>
|
| 393 |
+
<option value="name-desc" data-i18n="sort_name_desc">Nom (Z-A)</option>
|
| 394 |
+
<option value="size-desc" data-i18n="sort_size">Taille</option>
|
| 395 |
+
<option value="date-desc" data-i18n="sort_date">Date</option>
|
| 396 |
+
</select>
|
| 397 |
+
</div>
|
| 398 |
+
</div>
|
| 399 |
+
</div>
|
| 400 |
+
|
| 401 |
<div id="context-menu">
|
| 402 |
+
<div class="ctx-item" id="ctx-preview"><svg class="icon icon-sm"><use href="#icon-eye"></use></svg> <span data-i18n="ctx_preview">Aperçu</span></div>
|
| 403 |
+
<div class="ctx-item" id="ctx-download"><svg class="icon icon-sm"><use href="#icon-download"></use></svg> <span data-i18n="ctx_download">Télécharger</span></div>
|
| 404 |
+
<div class="ctx-item" id="ctx-share"><svg class="icon icon-sm"><use href="#icon-link"></use></svg> <span data-i18n="ctx_link">Lien public</span></div>
|
| 405 |
+
<div class="ctx-item" id="ctx-share-user"><svg class="icon icon-sm"><use href="#icon-user-plus"></use></svg> <span data-i18n="ctx_share_user">Partager à un utilisateur</span></div>
|
| 406 |
<div class="ctx-sep"></div>
|
| 407 |
+
<div class="ctx-item" id="ctx-remove" style="color:var(--danger);"><svg class="icon icon-sm"><use href="#icon-x"></use></svg> <span data-i18n="ctx_remove">Retirer de ma liste</span></div>
|
| 408 |
+
<div class="ctx-item" id="ctx-delete" style="color:var(--danger);"><svg class="icon icon-sm"><use href="#icon-trash"></use></svg> <span data-i18n="ctx_delete">Supprimer</span></div>
|
| 409 |
</div>
|
| 410 |
|
| 411 |
<div id="toast-container"></div>
|
| 412 |
|
| 413 |
+
<!-- ✅ TRADUCTIONS INTÉGRÉES (FR / EN) — plus de fichier externe -->
|
| 414 |
+
<script>
|
| 415 |
+
window.I18N = {
|
| 416 |
+
fr: {
|
| 417 |
+
nav_section: 'Navigation', nav_files: 'Mes Fichiers', nav_shared: 'Partagés', nav_recent: 'Récents',
|
| 418 |
+
upload: 'Uploader', new_folder: 'Nouveau Dossier', refresh: 'Rafraîchir',
|
| 419 |
+
sort_name_asc: 'Nom (A-Z)', sort_name_desc: 'Nom (Z-A)', sort_size: 'Taille', sort_date: 'Date',
|
| 420 |
+
username: 'Utilisateur', password: 'Mot de passe', confirm_pass: 'Confirmer',
|
| 421 |
+
login_btn: 'Se connecter', register_btn: 'Créer compte',
|
| 422 |
+
no_account: "Pas de compte ? S'inscrire", have_account: 'Déjà un compte ? Se connecter',
|
| 423 |
+
tos_accept: "J'accepte les CGU",
|
| 424 |
+
sec_shared_me: 'Partagés avec moi', sec_my_shares: 'Mes partages utilisateurs (gestion)', sec_my_links: 'Mes liens publics',
|
| 425 |
+
empty_folder: 'Dossier vide', empty_shared: 'Rien ne vous a été partagé', empty_my_shares: 'Aucun partage actif', empty_links: 'Aucun lien public', loading: 'Chargement...',
|
| 426 |
+
ctx_preview: 'Aperçu', ctx_download: 'Télécharger', ctx_link: 'Lien public', ctx_share_user: 'Partager à un utilisateur', ctx_remove: 'Retirer de ma liste', ctx_delete: 'Supprimer',
|
| 427 |
+
share_title: 'Partager le fichier', share_preview: 'Lien de prévisualisation', share_preview_hint: 'Page web avec lecteur intégré',
|
| 428 |
+
share_dl: 'Lien de téléchargement direct', share_dl_hint: 'Télécharge le fichier immédiatement',
|
| 429 |
+
share_embed: "Code d'intégration HTML", share_embed_hint: 'À coller sur un site web ou un forum', share_expiry: 'Ces liens expirent dans 7 jours',
|
| 430 |
+
close: 'Fermer', cancel: 'Annuler', download: 'Télécharger',
|
| 431 |
+
tos_title: "Conditions Générales d'Utilisation", tos_accept_btn: "J'accepte",
|
| 432 |
+
settings: 'Paramètres', profile: 'Profil', change_avatar: "Changer l'avatar", remove_avatar: 'Retirer',
|
| 433 |
+
language: 'Langue', theme: 'Thème', theme_dark: 'Sombre', theme_light: 'Clair', default_sort: 'Tri par défaut',
|
| 434 |
+
transfer: 'Transfert', folder: 'Dossier',
|
| 435 |
+
st_pending: 'En attente', st_uploading: 'Envoi...', st_processing: 'Traitement...', st_ready: 'Prêt', st_error: 'Erreur',
|
| 436 |
+
t_copied: 'Lien copié !', t_deleted: 'Supprimé', t_removed: 'Retiré', t_revoked: 'Partage révoqué', t_folder_created: 'Dossier créé',
|
| 437 |
+
t_avatar_saved: 'Avatar mis à jour', t_avatar_removed: 'Avatar retiré', t_lang_changed: 'Langue modifiée',
|
| 438 |
+
dlg_logout_title: 'Déconnexion', dlg_logout_msg: 'Voulez-vous vraiment vous déconnecter ?',
|
| 439 |
+
dlg_remove_msg: "Vous perdrez l'accès à cet élément partagé.", dlg_links_msg: 'Tous les liens publics de ce fichier seront révoqués.',
|
| 440 |
+
dlg_delete_msg: 'Supprimer définitivement', revoke: 'Révoquer',
|
| 441 |
+
expires: 'Expire le', shared_on: 'Partagé le', shared_with: 'partagé avec'
|
| 442 |
+
},
|
| 443 |
+
en: {
|
| 444 |
+
nav_section: 'Navigation', nav_files: 'My Files', nav_shared: 'Shared', nav_recent: 'Recent',
|
| 445 |
+
upload: 'Upload', new_folder: 'New Folder', refresh: 'Refresh',
|
| 446 |
+
sort_name_asc: 'Name (A-Z)', sort_name_desc: 'Name (Z-A)', sort_size: 'Size', sort_date: 'Date',
|
| 447 |
+
username: 'Username', password: 'Password', confirm_pass: 'Confirm',
|
| 448 |
+
login_btn: 'Log in', register_btn: 'Create account',
|
| 449 |
+
no_account: 'No account? Sign up', have_account: 'Already have an account? Log in',
|
| 450 |
+
tos_accept: 'I accept the Terms',
|
| 451 |
+
sec_shared_me: 'Shared with me', sec_my_shares: 'My user shares (management)', sec_my_links: 'My public links',
|
| 452 |
+
empty_folder: 'Empty folder', empty_shared: 'Nothing shared with you', empty_my_shares: 'No active shares', empty_links: 'No public links', loading: 'Loading...',
|
| 453 |
+
ctx_preview: 'Preview', ctx_download: 'Download', ctx_link: 'Public link', ctx_share_user: 'Share with a user', ctx_remove: 'Remove from my list', ctx_delete: 'Delete',
|
| 454 |
+
share_title: 'Share file', share_preview: 'Preview link', share_preview_hint: 'Web page with embedded player',
|
| 455 |
+
share_dl: 'Direct download link', share_dl_hint: 'Downloads the file immediately',
|
| 456 |
+
share_embed: 'HTML embed code', share_embed_hint: 'Paste on a website or forum', share_expiry: 'These links expire in 7 days',
|
| 457 |
+
close: 'Close', cancel: 'Cancel', download: 'Download',
|
| 458 |
+
tos_title: 'Terms of Service', tos_accept_btn: 'I accept',
|
| 459 |
+
settings: 'Settings', profile: 'Profile', change_avatar: 'Change avatar', remove_avatar: 'Remove',
|
| 460 |
+
language: 'Language', theme: 'Theme', theme_dark: 'Dark', theme_light: 'Light', default_sort: 'Default sort',
|
| 461 |
+
transfer: 'Transfer', folder: 'Folder',
|
| 462 |
+
st_pending: 'Pending', st_uploading: 'Uploading...', st_processing: 'Processing...', st_ready: 'Ready', st_error: 'Error',
|
| 463 |
+
t_copied: 'Link copied!', t_deleted: 'Deleted', t_removed: 'Removed', t_revoked: 'Share revoked', t_folder_created: 'Folder created',
|
| 464 |
+
t_avatar_saved: 'Avatar updated', t_avatar_removed: 'Avatar removed', t_lang_changed: 'Language changed',
|
| 465 |
+
dlg_logout_title: 'Log out', dlg_logout_msg: 'Do you really want to log out?',
|
| 466 |
+
dlg_remove_msg: 'You will lose access to this shared item.', dlg_links_msg: 'All public links for this file will be revoked.',
|
| 467 |
+
dlg_delete_msg: 'Permanently delete', revoke: 'Revoke',
|
| 468 |
+
expires: 'Expires', shared_on: 'Shared on', shared_with: 'shared with'
|
| 469 |
+
}
|
| 470 |
+
};
|
| 471 |
+
window.getLang = () => localStorage.getItem('pd_lang') || 'fr';
|
| 472 |
+
window.t = (k) => { const L = window.I18N[getLang()] || {}; return L[k] !== undefined ? L[k] : (window.I18N.fr[k] !== undefined ? window.I18N.fr[k] : k); };
|
| 473 |
+
window.applyI18n = () => {
|
| 474 |
+
document.querySelectorAll('[data-i18n]').forEach(el => { el.textContent = t(el.getAttribute('data-i18n')); });
|
| 475 |
+
document.querySelectorAll('[data-i18n-ph]').forEach(el => { el.placeholder = t(el.getAttribute('data-i18n-ph')); });
|
| 476 |
+
document.querySelectorAll('[data-i18n-title]').forEach(el => { el.title = t(el.getAttribute('data-i18n-title')); });
|
| 477 |
+
document.documentElement.lang = getLang();
|
| 478 |
+
};
|
| 479 |
+
</script>
|
| 480 |
<script src="/app.js"></script>
|
| 481 |
</body>
|
| 482 |
</html>
|