detached f2194b1

Remove file dialog manual test page

commit
f2194b18f408d28536723ec63371bf0731dc5824
author
brice · · 2026-05-08 00:55 UTC
parent
737371e
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · 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>

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close