NathMen12 commited on
Commit
8385c0f
·
verified ·
1 Parent(s): 98e52e4

Update public/index.html

Browse files
Files changed (1) hide show
  1. 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">Pas de compte ? <a onclick="app.toggleAuth('register')">S'inscrire</a></div>
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">Déjà un compte ? <a onclick="app.toggleAuth('login')">Se connecter</a></div>
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="Génération...">
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="Génération...">
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="Génération...">
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>