Remove file dialog manual test page
- commit
- f2194b18f408d28536723ec63371bf0731dc5824
- author
- brice · · 2026-05-08 00:55 UTC
- parent
- 737371e
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +0 -233
D assets/manual-tests/file-dialogs.html +0 -233
| @@ -1,233 +0,0 @@ | |||
| 1 | - | <!DOCTYPE html> | |
| 2 | - | <html lang="fr"> | |
| 3 | - | <head> | |
| 4 | - | <meta charset="UTF-8"> | |
| 5 | - | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| 6 | - | <title>Test des dialogues de fichiers — Dumber</title> | |
| 7 | - | <style> | |
| 8 | - | body { | |
| 9 | - | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| 10 | - | max-width: 800px; | |
| 11 | - | margin: 2rem auto; | |
| 12 | - | padding: 0 1rem; | |
| 13 | - | background: #fafafa; | |
| 14 | - | color: #222; | |
| 15 | - | line-height: 1.5; | |
| 16 | - | } | |
| 17 | - | h1 { font-size: 1.4rem; margin-bottom: 0.25rem; } | |
| 18 | - | h2 { font-size: 1.1rem; margin: 1.5rem 0 0.75rem; } | |
| 19 | - | section { | |
| 20 | - | background: #fff; | |
| 21 | - | border: 1px solid #ddd; | |
| 22 | - | border-radius: 6px; | |
| 23 | - | padding: 1rem 1.25rem; | |
| 24 | - | margin-bottom: 1rem; | |
| 25 | - | } | |
| 26 | - | label { display: block; margin-bottom: 0.5rem; font-weight: 600; } | |
| 27 | - | input[type="file"] { display: block; margin-bottom: 0.75rem; } | |
| 28 | - | button { | |
| 29 | - | font-size: 0.95rem; | |
| 30 | - | padding: 0.4rem 1rem; | |
| 31 | - | border: 1px solid #888; | |
| 32 | - | border-radius: 4px; | |
| 33 | - | background: #eee; | |
| 34 | - | cursor: pointer; | |
| 35 | - | } | |
| 36 | - | button:active { background: #ddd; } | |
| 37 | - | #log { | |
| 38 | - | background: #1e1e1e; | |
| 39 | - | color: #d4d4d4; | |
| 40 | - | font-family: "Cascadia Code", "Fira Code", "JetBrains Mono", Consolas, monospace; | |
| 41 | - | font-size: 0.85rem; | |
| 42 | - | padding: 0.75rem 1rem; | |
| 43 | - | border-radius: 6px; | |
| 44 | - | max-height: 20em; | |
| 45 | - | overflow-y: auto; | |
| 46 | - | white-space: pre-wrap; | |
| 47 | - | overflow-wrap: anywhere; | |
| 48 | - | } | |
| 49 | - | #log .info { color: #6bc; } | |
| 50 | - | #log .ok { color: #6c6; } | |
| 51 | - | #log .warn { color: #da5; } | |
| 52 | - | #log .error { color: #e55; } | |
| 53 | - | .instructions { | |
| 54 | - | background: #f0f6ff; | |
| 55 | - | border: 1px solid #b8d4fe; | |
| 56 | - | font-size: 0.9rem; | |
| 57 | - | padding: 0.75rem 1rem; | |
| 58 | - | border-radius: 6px; | |
| 59 | - | margin-bottom: 1.5rem; | |
| 60 | - | } | |
| 61 | - | .instructions code { | |
| 62 | - | background: #e3edf7; | |
| 63 | - | padding: 0.1em 0.4em; | |
| 64 | - | border-radius: 3px; | |
| 65 | - | font-size: 0.85rem; | |
| 66 | - | } | |
| 67 | - | hr { border: none; border-top: 1px solid #ddd; margin: 1.5rem 0; } | |
| 68 | - | </style> | |
| 69 | - | </head> | |
| 70 | - | <body> | |
| 71 | - | ||
| 72 | - | <h1>🧪 Test des dialogues de fichiers</h1> | |
| 73 | - | <p>Page HTML simple pour tester les sélecteurs de fichiers et l'emplacement de sauvegarde dans Dumber.</p> | |
| 74 | - | ||
| 75 | - | <div class="instructions"> | |
| 76 | - | <strong>Ouvrir cette page dans Dumber :</strong><br> | |
| 77 | - | <code>ENV=dev DUMBER_LOG_LEVEL=debug ./dist/dumber browse /home/brice/dev/projects/dumber/assets/manual-tests/file-dialogs.html</code> | |
| 78 | - | <p style="margin:0.6rem 0 0; color:#555;"> | |
| 79 | - | Note : le choix de dossier via <code>webkitdirectory</code> et la boîte de dialogue de sauvegarde via <code>showSaveFilePicker()</code> | |
| 80 | - | dépendent du moteur navigateur embarqué. | |
| 81 | - | </p> | |
| 82 | - | </div> | |
| 83 | - | ||
| 84 | - | <!-- ───── Upload section ───── --> | |
| 85 | - | <section> | |
| 86 | - | <h2>📤 Upload / Ouverture</h2> | |
| 87 | - | ||
| 88 | - | <label for="single-file">Un seul fichier</label> | |
| 89 | - | <input type="file" id="single-file" onclick="this.value = null"> | |
| 90 | - | ||
| 91 | - | <label for="multi-file">Plusieurs fichiers</label> | |
| 92 | - | <input type="file" id="multi-file" multiple onclick="this.value = null"> | |
| 93 | - | ||
| 94 | - | <label for="dir-picker">Dossier (webkitdirectory)</label> | |
| 95 | - | <input type="file" id="dir-picker" webkitdirectory multiple onclick="this.value = null"> | |
| 96 | - | </section> | |
| 97 | - | ||
| 98 | - | <!-- ───── Save section ───── --> | |
| 99 | - | <section> | |
| 100 | - | <h2>💾 Emplacement de sauvegarde</h2> | |
| 101 | - | <p style="margin:0 0 0.75rem; font-size:0.9rem; color:#555;"> | |
| 102 | - | Utilise <code>window.showSaveFilePicker()</code> — disponible dans les navigateurs basés sur Chromium. | |
| 103 | - | </p> | |
| 104 | - | <button type="button" id="save-btn">Choisir un emplacement et écrire le fichier</button> | |
| 105 | - | </section> | |
| 106 | - | ||
| 107 | - | <!-- ───── Status log ───── --> | |
| 108 | - | <section> | |
| 109 | - | <div style="display:flex; align-items:center; justify-content:space-between; gap:1rem; margin-bottom:0.75rem;"> | |
| 110 | - | <h2 style="margin:0;">📋 Journal des événements</h2> | |
| 111 | - | <button type="button" id="clear-log-btn">Vider le journal</button> | |
| 112 | - | </div> | |
| 113 | - | <div id="log">[journal prêt]</div> | |
| 114 | - | </section> | |
| 115 | - | ||
| 116 | - | <script> | |
| 117 | - | const logger = (() => { | |
| 118 | - | const el = document.getElementById('log'); | |
| 119 | - | return (level, msg) => { | |
| 120 | - | const line = document.createElement('div'); | |
| 121 | - | line.className = level; | |
| 122 | - | line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; | |
| 123 | - | el.appendChild(line); | |
| 124 | - | el.scrollTop = el.scrollHeight; | |
| 125 | - | }; | |
| 126 | - | })(); | |
| 127 | - | ||
| 128 | - | function info(msg) { logger('info', msg); } | |
| 129 | - | function ok(msg) { logger('ok', msg); } | |
| 130 | - | function warn(msg) { logger('warn', msg); } | |
| 131 | - | function error(msg) { logger('error', msg); } | |
| 132 | - | ||
| 133 | - | document.getElementById('clear-log-btn').addEventListener('click', function () { | |
| 134 | - | document.getElementById('log').textContent = '[journal vidé]'; | |
| 135 | - | }); | |
| 136 | - | ||
| 137 | - | if (!('webkitdirectory' in document.createElement('input'))) { | |
| 138 | - | warn('Le support de webkitdirectory ne semble pas disponible dans ce navigateur.'); | |
| 139 | - | } | |
| 140 | - | ||
| 141 | - | if (!window.showSaveFilePicker) { | |
| 142 | - | warn('showSaveFilePicker() ne semble pas disponible dans ce navigateur.'); | |
| 143 | - | } | |
| 144 | - | ||
| 145 | - | // ── Single file ── | |
| 146 | - | document.getElementById('single-file').addEventListener('change', function (e) { | |
| 147 | - | const file = this.files[0]; | |
| 148 | - | if (!file) { info('Single file: selection cleared'); return; } | |
| 149 | - | ok(`Single file: "${file.name}" (${(file.size / 1024).toFixed(1)} KB, type: ${file.type || 'unknown'})`); | |
| 150 | - | }); | |
| 151 | - | ||
| 152 | - | // ── Multiple files ── | |
| 153 | - | document.getElementById('multi-file').addEventListener('change', function () { | |
| 154 | - | const files = this.files; | |
| 155 | - | if (!files.length) { info('Multiple files: selection cleared'); return; } | |
| 156 | - | ok(`Multiple files: ${files.length} selected`); | |
| 157 | - | for (let i = 0; i < files.length; i++) { | |
| 158 | - | const f = files[i]; | |
| 159 | - | ok(` [${i}] "${f.name}" (${(f.size / 1024).toFixed(1)} KB)`); | |
| 160 | - | } | |
| 161 | - | }); | |
| 162 | - | ||
| 163 | - | // ── Directory ── | |
| 164 | - | document.getElementById('dir-picker').addEventListener('change', function () { | |
| 165 | - | const files = this.files; | |
| 166 | - | if (!files.length) { info('Folder: selection cleared'); return; } | |
| 167 | - | ok(`Folder: ${files.length} entries selected`); | |
| 168 | - | ||
| 169 | - | // Collect folder names | |
| 170 | - | const dirs = new Set(); | |
| 171 | - | for (let i = 0; i < files.length; i++) { | |
| 172 | - | const path = files[i].webkitRelativePath; | |
| 173 | - | const parts = path.split('/'); | |
| 174 | - | if (parts.length > 1) dirs.add(parts[0]); | |
| 175 | - | } | |
| 176 | - | for (const d of dirs) { | |
| 177 | - | ok(` folder: "${d}"`); | |
| 178 | - | } | |
| 179 | - | // Show up to 5 filenames | |
| 180 | - | const maxShow = 5; | |
| 181 | - | for (let i = 0; i < Math.min(files.length, maxShow); i++) { | |
| 182 | - | const f = files[i]; | |
| 183 | - | ok(` file: "${f.webkitRelativePath}" (${(f.size / 1024).toFixed(1)} KB)`); | |
| 184 | - | } | |
| 185 | - | if (files.length > maxShow) { | |
| 186 | - | ok(` … and ${files.length - maxShow} more entries`); | |
| 187 | - | } | |
| 188 | - | }); | |
| 189 | - | ||
| 190 | - | // ── Save file picker ── | |
| 191 | - | const saveButton = document.getElementById('save-btn'); | |
| 192 | - | saveButton.addEventListener('click', async function () { | |
| 193 | - | if (saveButton.disabled) return; | |
| 194 | - | ||
| 195 | - | saveButton.disabled = true; | |
| 196 | - | info('Ouverture de la boîte de dialogue de sauvegarde…'); | |
| 197 | - | ||
| 198 | - | if (!window.showSaveFilePicker) { | |
| 199 | - | warn('showSaveFilePicker() n\'est pas disponible dans ce navigateur.'); | |
| 200 | - | warn('Essayez avec un navigateur basé sur Chromium.'); | |
| 201 | - | saveButton.disabled = false; | |
| 202 | - | return; | |
| 203 | - | } | |
| 204 | - | ||
| 205 | - | try { | |
| 206 | - | const handle = await window.showSaveFilePicker({ | |
| 207 | - | suggestedName: 'dumber-save-test.txt', | |
| 208 | - | types: [{ | |
| 209 | - | description: 'Text files', | |
| 210 | - | accept: { 'text/plain': ['.txt'] } | |
| 211 | - | }] | |
| 212 | - | }); | |
| 213 | - | ||
| 214 | - | const writable = await handle.createWritable(); | |
| 215 | - | const payload = 'Dumber save test — ' + new Date().toISOString() + '\n'; | |
| 216 | - | await writable.write(payload); | |
| 217 | - | await writable.close(); | |
| 218 | - | ||
| 219 | - | ok(`Fichier sauvegardé : "${handle.name}"`); | |
| 220 | - | } catch (err) { | |
| 221 | - | if (err?.name === 'AbortError' || err?.name === 'SecurityError') { | |
| 222 | - | warn('Sauvegarde annulée ou permission refusée.'); | |
| 223 | - | } else { | |
| 224 | - | error(`Échec de la sauvegarde : ${err?.message ?? err}`); | |
| 225 | - | } | |
| 226 | - | } finally { | |
| 227 | - | saveButton.disabled = false; | |
| 228 | - | } | |
| 229 | - | }); | |
| 230 | - | </script> | |
| 231 | - | ||
| 232 | - | </body> | |
| 233 | - | </html> | |
| @@ -1,233 +0,0 @@ | |||||
| 1 | - | <!DOCTYPE html> | |||
| 2 | - | <html lang="fr"> | |||
| 3 | - | <head> | |||
| 4 | - | <meta charset="UTF-8"> | |||
| 5 | - | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| 6 | - | <title>Test des dialogues de fichiers — Dumber</title> | |||
| 7 | - | <style> | |||
| 8 | - | body { | |||
| 9 | - | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |||
| 10 | - | max-width: 800px; | |||
| 11 | - | margin: 2rem auto; | |||
| 12 | - | padding: 0 1rem; | |||
| 13 | - | background: #fafafa; | |||
| 14 | - | color: #222; | |||
| 15 | - | line-height: 1.5; | |||
| 16 | - | } | |||
| 17 | - | h1 { font-size: 1.4rem; margin-bottom: 0.25rem; } | |||
| 18 | - | h2 { font-size: 1.1rem; margin: 1.5rem 0 0.75rem; } | |||
| 19 | - | section { | |||
| 20 | - | background: #fff; | |||
| 21 | - | border: 1px solid #ddd; | |||
| 22 | - | border-radius: 6px; | |||
| 23 | - | padding: 1rem 1.25rem; | |||
| 24 | - | margin-bottom: 1rem; | |||
| 25 | - | } | |||
| 26 | - | label { display: block; margin-bottom: 0.5rem; font-weight: 600; } | |||
| 27 | - | input[type="file"] { display: block; margin-bottom: 0.75rem; } | |||
| 28 | - | button { | |||
| 29 | - | font-size: 0.95rem; | |||
| 30 | - | padding: 0.4rem 1rem; | |||
| 31 | - | border: 1px solid #888; | |||
| 32 | - | border-radius: 4px; | |||
| 33 | - | background: #eee; | |||
| 34 | - | cursor: pointer; | |||
| 35 | - | } | |||
| 36 | - | button:active { background: #ddd; } | |||
| 37 | - | #log { | |||
| 38 | - | background: #1e1e1e; | |||
| 39 | - | color: #d4d4d4; | |||
| 40 | - | font-family: "Cascadia Code", "Fira Code", "JetBrains Mono", Consolas, monospace; | |||
| 41 | - | font-size: 0.85rem; | |||
| 42 | - | padding: 0.75rem 1rem; | |||
| 43 | - | border-radius: 6px; | |||
| 44 | - | max-height: 20em; | |||
| 45 | - | overflow-y: auto; | |||
| 46 | - | white-space: pre-wrap; | |||
| 47 | - | overflow-wrap: anywhere; | |||
| 48 | - | } | |||
| 49 | - | #log .info { color: #6bc; } | |||
| 50 | - | #log .ok { color: #6c6; } | |||
| 51 | - | #log .warn { color: #da5; } | |||
| 52 | - | #log .error { color: #e55; } | |||
| 53 | - | .instructions { | |||
| 54 | - | background: #f0f6ff; | |||
| 55 | - | border: 1px solid #b8d4fe; | |||
| 56 | - | font-size: 0.9rem; | |||
| 57 | - | padding: 0.75rem 1rem; | |||
| 58 | - | border-radius: 6px; | |||
| 59 | - | margin-bottom: 1.5rem; | |||
| 60 | - | } | |||
| 61 | - | .instructions code { | |||
| 62 | - | background: #e3edf7; | |||
| 63 | - | padding: 0.1em 0.4em; | |||
| 64 | - | border-radius: 3px; | |||
| 65 | - | font-size: 0.85rem; | |||
| 66 | - | } | |||
| 67 | - | hr { border: none; border-top: 1px solid #ddd; margin: 1.5rem 0; } | |||
| 68 | - | </style> | |||
| 69 | - | </head> | |||
| 70 | - | <body> | |||
| 71 | - | ||||
| 72 | - | <h1>🧪 Test des dialogues de fichiers</h1> | |||
| 73 | - | <p>Page HTML simple pour tester les sélecteurs de fichiers et l'emplacement de sauvegarde dans Dumber.</p> | |||
| 74 | - | ||||
| 75 | - | <div class="instructions"> | |||
| 76 | - | <strong>Ouvrir cette page dans Dumber :</strong><br> | |||
| 77 | - | <code>ENV=dev DUMBER_LOG_LEVEL=debug ./dist/dumber browse /home/brice/dev/projects/dumber/assets/manual-tests/file-dialogs.html</code> | |||
| 78 | - | <p style="margin:0.6rem 0 0; color:#555;"> | |||
| 79 | - | Note : le choix de dossier via <code>webkitdirectory</code> et la boîte de dialogue de sauvegarde via <code>showSaveFilePicker()</code> | |||
| 80 | - | dépendent du moteur navigateur embarqué. | |||
| 81 | - | </p> | |||
| 82 | - | </div> | |||
| 83 | - | ||||
| 84 | - | <!-- ───── Upload section ───── --> | |||
| 85 | - | <section> | |||
| 86 | - | <h2>📤 Upload / Ouverture</h2> | |||
| 87 | - | ||||
| 88 | - | <label for="single-file">Un seul fichier</label> | |||
| 89 | - | <input type="file" id="single-file" onclick="this.value = null"> | |||
| 90 | - | ||||
| 91 | - | <label for="multi-file">Plusieurs fichiers</label> | |||
| 92 | - | <input type="file" id="multi-file" multiple onclick="this.value = null"> | |||
| 93 | - | ||||
| 94 | - | <label for="dir-picker">Dossier (webkitdirectory)</label> | |||
| 95 | - | <input type="file" id="dir-picker" webkitdirectory multiple onclick="this.value = null"> | |||
| 96 | - | </section> | |||
| 97 | - | ||||
| 98 | - | <!-- ───── Save section ───── --> | |||
| 99 | - | <section> | |||
| 100 | - | <h2>💾 Emplacement de sauvegarde</h2> | |||
| 101 | - | <p style="margin:0 0 0.75rem; font-size:0.9rem; color:#555;"> | |||
| 102 | - | Utilise <code>window.showSaveFilePicker()</code> — disponible dans les navigateurs basés sur Chromium. | |||
| 103 | - | </p> | |||
| 104 | - | <button type="button" id="save-btn">Choisir un emplacement et écrire le fichier</button> | |||
| 105 | - | </section> | |||
| 106 | - | ||||
| 107 | - | <!-- ───── Status log ───── --> | |||
| 108 | - | <section> | |||
| 109 | - | <div style="display:flex; align-items:center; justify-content:space-between; gap:1rem; margin-bottom:0.75rem;"> | |||
| 110 | - | <h2 style="margin:0;">📋 Journal des événements</h2> | |||
| 111 | - | <button type="button" id="clear-log-btn">Vider le journal</button> | |||
| 112 | - | </div> | |||
| 113 | - | <div id="log">[journal prêt]</div> | |||
| 114 | - | </section> | |||
| 115 | - | ||||
| 116 | - | <script> | |||
| 117 | - | const logger = (() => { | |||
| 118 | - | const el = document.getElementById('log'); | |||
| 119 | - | return (level, msg) => { | |||
| 120 | - | const line = document.createElement('div'); | |||
| 121 | - | line.className = level; | |||
| 122 | - | line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; | |||
| 123 | - | el.appendChild(line); | |||
| 124 | - | el.scrollTop = el.scrollHeight; | |||
| 125 | - | }; | |||
| 126 | - | })(); | |||
| 127 | - | ||||
| 128 | - | function info(msg) { logger('info', msg); } | |||
| 129 | - | function ok(msg) { logger('ok', msg); } | |||
| 130 | - | function warn(msg) { logger('warn', msg); } | |||
| 131 | - | function error(msg) { logger('error', msg); } | |||
| 132 | - | ||||
| 133 | - | document.getElementById('clear-log-btn').addEventListener('click', function () { | |||
| 134 | - | document.getElementById('log').textContent = '[journal vidé]'; | |||
| 135 | - | }); | |||
| 136 | - | ||||
| 137 | - | if (!('webkitdirectory' in document.createElement('input'))) { | |||
| 138 | - | warn('Le support de webkitdirectory ne semble pas disponible dans ce navigateur.'); | |||
| 139 | - | } | |||
| 140 | - | ||||
| 141 | - | if (!window.showSaveFilePicker) { | |||
| 142 | - | warn('showSaveFilePicker() ne semble pas disponible dans ce navigateur.'); | |||
| 143 | - | } | |||
| 144 | - | ||||
| 145 | - | // ── Single file ── | |||
| 146 | - | document.getElementById('single-file').addEventListener('change', function (e) { | |||
| 147 | - | const file = this.files[0]; | |||
| 148 | - | if (!file) { info('Single file: selection cleared'); return; } | |||
| 149 | - | ok(`Single file: "${file.name}" (${(file.size / 1024).toFixed(1)} KB, type: ${file.type || 'unknown'})`); | |||
| 150 | - | }); | |||
| 151 | - | ||||
| 152 | - | // ── Multiple files ── | |||
| 153 | - | document.getElementById('multi-file').addEventListener('change', function () { | |||
| 154 | - | const files = this.files; | |||
| 155 | - | if (!files.length) { info('Multiple files: selection cleared'); return; } | |||
| 156 | - | ok(`Multiple files: ${files.length} selected`); | |||
| 157 | - | for (let i = 0; i < files.length; i++) { | |||
| 158 | - | const f = files[i]; | |||
| 159 | - | ok(` [${i}] "${f.name}" (${(f.size / 1024).toFixed(1)} KB)`); | |||
| 160 | - | } | |||
| 161 | - | }); | |||
| 162 | - | ||||
| 163 | - | // ── Directory ── | |||
| 164 | - | document.getElementById('dir-picker').addEventListener('change', function () { | |||
| 165 | - | const files = this.files; | |||
| 166 | - | if (!files.length) { info('Folder: selection cleared'); return; } | |||
| 167 | - | ok(`Folder: ${files.length} entries selected`); | |||
| 168 | - | ||||
| 169 | - | // Collect folder names | |||
| 170 | - | const dirs = new Set(); | |||
| 171 | - | for (let i = 0; i < files.length; i++) { | |||
| 172 | - | const path = files[i].webkitRelativePath; | |||
| 173 | - | const parts = path.split('/'); | |||
| 174 | - | if (parts.length > 1) dirs.add(parts[0]); | |||
| 175 | - | } | |||
| 176 | - | for (const d of dirs) { | |||
| 177 | - | ok(` folder: "${d}"`); | |||
| 178 | - | } | |||
| 179 | - | // Show up to 5 filenames | |||
| 180 | - | const maxShow = 5; | |||
| 181 | - | for (let i = 0; i < Math.min(files.length, maxShow); i++) { | |||
| 182 | - | const f = files[i]; | |||
| 183 | - | ok(` file: "${f.webkitRelativePath}" (${(f.size / 1024).toFixed(1)} KB)`); | |||
| 184 | - | } | |||
| 185 | - | if (files.length > maxShow) { | |||
| 186 | - | ok(` … and ${files.length - maxShow} more entries`); | |||
| 187 | - | } | |||
| 188 | - | }); | |||
| 189 | - | ||||
| 190 | - | // ── Save file picker ── | |||
| 191 | - | const saveButton = document.getElementById('save-btn'); | |||
| 192 | - | saveButton.addEventListener('click', async function () { | |||
| 193 | - | if (saveButton.disabled) return; | |||
| 194 | - | ||||
| 195 | - | saveButton.disabled = true; | |||
| 196 | - | info('Ouverture de la boîte de dialogue de sauvegarde…'); | |||
| 197 | - | ||||
| 198 | - | if (!window.showSaveFilePicker) { | |||
| 199 | - | warn('showSaveFilePicker() n\'est pas disponible dans ce navigateur.'); | |||
| 200 | - | warn('Essayez avec un navigateur basé sur Chromium.'); | |||
| 201 | - | saveButton.disabled = false; | |||
| 202 | - | return; | |||
| 203 | - | } | |||
| 204 | - | ||||
| 205 | - | try { | |||
| 206 | - | const handle = await window.showSaveFilePicker({ | |||
| 207 | - | suggestedName: 'dumber-save-test.txt', | |||
| 208 | - | types: [{ | |||
| 209 | - | description: 'Text files', | |||
| 210 | - | accept: { 'text/plain': ['.txt'] } | |||
| 211 | - | }] | |||
| 212 | - | }); | |||
| 213 | - | ||||
| 214 | - | const writable = await handle.createWritable(); | |||
| 215 | - | const payload = 'Dumber save test — ' + new Date().toISOString() + '\n'; | |||
| 216 | - | await writable.write(payload); | |||
| 217 | - | await writable.close(); | |||
| 218 | - | ||||
| 219 | - | ok(`Fichier sauvegardé : "${handle.name}"`); | |||
| 220 | - | } catch (err) { | |||
| 221 | - | if (err?.name === 'AbortError' || err?.name === 'SecurityError') { | |||
| 222 | - | warn('Sauvegarde annulée ou permission refusée.'); | |||
| 223 | - | } else { | |||
| 224 | - | error(`Échec de la sauvegarde : ${err?.message ?? err}`); | |||
| 225 | - | } | |||
| 226 | - | } finally { | |||
| 227 | - | saveButton.disabled = false; | |||
| 228 | - | } | |||
| 229 | - | }); | |||
| 230 | - | </script> | |||
| 231 | - | ||||
| 232 | - | </body> | |||
| 233 | - | </html> | |||