Skip to content

Commit 2599061

Browse files
committed
Ajout du guide pour ajouter un contour blanc autour de l'icône via GIMP (annulation du contour CSS)
1 parent b90dc56 commit 2599061

File tree

9 files changed

+261
-52
lines changed

9 files changed

+261
-52
lines changed

app-icon-2.png

-163 KB
Binary file not shown.

app-icon.png

15.7 KB
Loading

app-icon2.png

79.4 KB
Loading

index.html

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,23 +103,26 @@
103103
<h2 class="panel-heading-xl" data-i18n="updates.title">Mettre à jour AM et toutes les applications installées</h2>
104104
<div class="updates-actions-row">
105105
<button id="runUpdatesBtn" class="btn btn-primary updates-run" data-i18n="updates.run">Mettre à jour</button>
106+
<button id="updatesToggleBtn" class="btn btn-outline updates-toggle-icon" type="button" aria-expanded="false" aria-controls="updatesTerminalWrap" title="Afficher/masquer le terminal" data-i18n-title="updates.toggleTerminal">
107+
<span class="updates-log-caret" aria-hidden="true"></span>
108+
</button>
106109
</div>
107110
<div id="updateSpinner" class="update-spinner" role="status" aria-live="polite" data-busy="false">
108-
<div class="spinner-info">
109-
<div class="spinner-status-row">
110-
<div class="spinner" aria-hidden="true"></div>
111-
<div class="spinner-label" data-i18n="updates.ready">Aucune mise à jour en cours.</div>
112-
</div>
113-
<button id="updatesToggleBtn" class="updates-toggle-btn" type="button" aria-controls="updatesTerminalWrap" aria-expanded="false" data-i18n="updates.toggleShow">Voir les détails ⌄</button>
111+
<div class="spinner-status-row">
112+
113+
<div class="update-timer" style="display:none;"></div>
114+
<div class="spinner-label" data-i18n="updates.ready">Aucune mise à jour en cours.</div>
114115
</div>
115116
</div>
116-
<div id="updatesTerminalWrap" class="updates-terminal" hidden>
117-
<div id="updatesTerminal" class="updates-terminal-body" role="log" aria-live="polite"></div>
118-
</div>
119117
<div id="updateResult" class="update-result" style="display:none;">
120118
<div id="updateFinalMessage" class="update-final-message"></div>
121119
<div id="updatedAppsIcons" class="icons-row updated-icons"></div>
122120
</div>
121+
<div id="updatesLogSection" class="updates-log-section" data-open="false">
122+
<div id="updatesTerminalWrap" class="updates-terminal" hidden>
123+
<div id="updatesTerminal" class="updates-terminal-body" role="log" aria-live="polite"></div>
124+
</div>
125+
</div>
123126
</section>
124127
<section id="advancedPanel" class="panel-lite advanced-panel" hidden>
125128
<h2 class="panel-heading-sm centered" data-i18n="advanced.title">Mode avancé</h2>
@@ -221,6 +224,19 @@ <h3 class="details-section-title" data-i18n="details.desc">Description</h3>
221224

222225
<div id="toast" class="toast" role="status" aria-live="polite" hidden></div>
223226

227+
<!-- Modale confirmation de fermeture avec installation en cours -->
228+
<div id="closeConfirmModal" class="modal" role="dialog" aria-modal="true" aria-label="Installation en cours" hidden>
229+
<div class="modal-dialog confirm-dialog" role="document">
230+
<div class="confirm-body">
231+
<p class="confirm-message" data-i18n="closeConfirm.message">Une installation est en cours. Si vous quittez maintenant, l'installation sera annulée.</p>
232+
<div class="confirm-actions">
233+
<button id="closeConfirmStay" class="btn btn-soft-neutral" type="button" data-i18n="closeConfirm.stay">Annuler et rester</button>
234+
<button id="closeConfirmQuit" class="btn btn-danger" type="button" data-i18n="closeConfirm.quit">Continuer et quitter</button>
235+
</div>
236+
</div>
237+
</div>
238+
</div>
239+
224240
<!-- Lightbox supprimé : plus de galerie séparée -->
225241
<!-- Modale confirmation compacte (sans titre ni croix) -->
226242
<div id="actionConfirmModal" class="modal" role="dialog" aria-modal="true" aria-label="Confirmation" hidden>

main.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,18 @@ function createWindow () {
369369
const menu = Menu.buildFromTemplate(template);
370370
menu.popup({ window: win });
371371
});
372+
373+
// Gestion de la fermeture : demander confirmation si une installation est en cours
374+
win.on('close', (event) => {
375+
// Vérifier s'il y a une installation active
376+
if (activeInstalls.size > 0) {
377+
event.preventDefault();
378+
// Envoyer un message au renderer pour afficher la modale de confirmation
379+
win.webContents.send('before-close');
380+
}
381+
});
382+
383+
return win;
372384
}
373385

374386
app.whenReady().then(() => {
@@ -997,5 +1009,11 @@ ipcMain.handle('window-control', (event, action) => {
9971009
}
9981010
});
9991011

1012+
// Handler pour fermer la fenêtre (appelé après confirmation)
1013+
ipcMain.handle('close-window', (event) => {
1014+
const win = BrowserWindow.fromWebContents(event.sender);
1015+
if (win) win.destroy(); // Force la fermeture sans redemander
1016+
});
1017+
10001018

10011019

preload.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
3939
}
4040
return ipcRenderer.invoke('sandbox-disable', { appName: payload });
4141
},
42-
onSandboxProgress: (cb) => ipcRenderer.on('sandbox-progress', (e, data) => cb && cb(data))
42+
onSandboxProgress: (cb) => ipcRenderer.on('sandbox-progress', (e, data) => cb && cb(data)),
43+
closeWindow: () => ipcRenderer.invoke('close-window'),
44+
onBeforeClose: (cb) => ipcRenderer.on('before-close', () => cb && cb())
4345
});
4446
try {
4547
const lArg = process.argv.find(a => a.startsWith('--locale='));

src/renderer/i18n/translations.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@
8787
'updates.ready': 'Aucune mise à jour en cours.',
8888
'updates.toggleShow': 'Voir les détails ⌄',
8989
'updates.toggleHide': 'Masquer les détails ⌃',
90+
'updates.logTitle': 'Terminal (logs)',
91+
'updates.toggleTerminal': 'Afficher/masquer le terminal',
9092
'updates.logHeader': 'Lancement des mises à jour',
9193
'updates.logCompleted': 'Mise à jour terminée',
9294
'updates.error': 'Une erreur est survenue pendant la mise à jour.',
@@ -177,6 +179,9 @@
177179
'modal.close': 'Fermer',
178180
'confirm.cancel': 'Annuler',
179181
'confirm.ok': 'Valider',
182+
'closeConfirm.message': 'Une installation est en cours. Si vous quittez maintenant, l\'installation sera annulée.',
183+
'closeConfirm.stay': 'Rester',
184+
'closeConfirm.quit': 'Quitter',
180185
'warning.title': 'Avertissement',
181186
'warning.checkboxText': "Je reste avec wget2, ne plus afficher cet avertissement (peut causer des bugs mineurs)",
182187
'warning.closeBtn': 'Fermer',
@@ -274,6 +279,8 @@
274279
'updates.ready': 'No update is running.',
275280
'updates.toggleShow': 'Show details ⌄',
276281
'updates.toggleHide': 'Hide details ⌃',
282+
'updates.logTitle': 'Terminal (logs)',
283+
'updates.toggleTerminal': 'Show/hide terminal',
277284
'updates.logHeader': 'Starting update process',
278285
'updates.logCompleted': 'Update finished',
279286
'updates.error': 'Something went wrong while updating.',
@@ -364,6 +371,9 @@
364371
'modal.close': 'Close',
365372
'confirm.cancel': 'Cancel',
366373
'confirm.ok': 'OK',
374+
'closeConfirm.message': 'An installation is in progress. If you quit now, the installation will be cancelled.',
375+
'closeConfirm.stay': 'Stay',
376+
'closeConfirm.quit': 'Quit',
367377
'warning.title': 'Warning',
368378
'warning.checkboxText': "I stay with wget2, do not show this warning again (may cause minor bugs)",
369379
'warning.closeBtn': 'Close',
@@ -461,6 +471,8 @@
461471
'updates.ready': 'Nessun aggiornamento in corso.',
462472
'updates.toggleShow': 'Mostra dettagli ⌄',
463473
'updates.toggleHide': 'Nascondi dettagli ⌃',
474+
'updates.logTitle': 'Terminale (log)',
475+
'updates.toggleTerminal': 'Mostra/nascondi terminale',
464476
'updates.logHeader': 'Avvio del processo di aggiornamento',
465477
'updates.logCompleted': 'Aggiornamento completato',
466478
'updates.error': 'Si è verificato un errore durante l’aggiornamento.',
@@ -551,6 +563,9 @@
551563
'modal.close': 'Chiudi',
552564
'confirm.cancel': 'Annulla',
553565
'confirm.ok': 'Conferma',
566+
'closeConfirm.message': 'Un\'installazione è in corso. Se esci ora, l\'installazione verrà annullata.',
567+
'closeConfirm.stay': 'Resta',
568+
'closeConfirm.quit': 'Esci',
554569
'warning.title': 'Avviso',
555570
'warning.checkboxText': "Resto con wget2, non mostrare più questo avviso (potrebbe causare piccoli bug)",
556571
'warning.closeBtn': 'Chiudi',

src/renderer/renderer.js

Lines changed: 137 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -460,27 +460,32 @@ if (modeMenuBtn && modeMenu) {
460460
modeMenu.hidden = true;
461461
modeMenuBtn.setAttribute('aria-expanded','false');
462462
});
463-
window.addEventListener('keydown', (ev) => {
464-
if (ev.key === 'Escape' && !modeMenu.hidden) {
465-
modeMenu.hidden = true; modeMenuBtn.setAttribute('aria-expanded','false');
463+
}
464+
465+
function startUpdateTimer() {
466+
const timer = document.querySelector('.update-timer');
467+
if (!timer) return;
468+
// Ne pas réinitialiser si déjà en cours
469+
if (updateTimerStart === null) updateTimerStart = Date.now();
470+
if (updateTimerInterval) return;
471+
const updateTimerText = () => {
472+
const elapsed = Math.max(0, Math.floor((Date.now() - updateTimerStart) / 1000));
473+
if (elapsed < 60) {
474+
timer.textContent = `${elapsed}s`;
475+
} else {
476+
const min = Math.floor(elapsed / 60);
477+
const sec = String(elapsed % 60).padStart(2, '0');
478+
timer.textContent = `${min}:${sec}`;
466479
}
467-
});
468-
modeMenu.addEventListener('click', (ev) => {
469-
const opt = ev.target.closest('.mode-option');
470-
if (!opt) return;
471-
const mode = opt.getAttribute('data-mode');
472-
if (!mode || mode === state.viewMode) { modeMenu.hidden = true; modeMenuBtn.setAttribute('aria-expanded','false'); return; }
473-
if (!['grid','list','icons','cards'].includes(mode)) return;
474-
state.viewMode = mode;
475-
localStorage.setItem('viewMode', state.viewMode);
476-
currentViewMode = state.viewMode;
477-
updateModeMenuUI();
478-
rerenderActiveCategory();
479-
// Remettre le scroll en haut à chaque changement de mode
480-
if (scrollShell) scrollShell.scrollTop = 0;
481-
modeMenu.hidden = true;
482-
modeMenuBtn.setAttribute('aria-expanded','false');
483-
});
480+
};
481+
updateTimerText();
482+
updateTimerInterval = setInterval(updateTimerText, 1000);
483+
}
484+
485+
function stopUpdateTimer() {
486+
if (updateTimerInterval) clearInterval(updateTimerInterval);
487+
updateTimerInterval = null;
488+
updateTimerStart = null;
484489
}
485490

486491
updateModeMenuUI();
@@ -2594,7 +2599,8 @@ tabs.forEach(tab => {
25942599
setUpdateSpinnerBusy(false);
25952600
}
25962601
} else {
2597-
setUpdateSpinnerBusy(false);
2602+
// Conserver l'état du spinner si une mise à jour tourne encore
2603+
setUpdateSpinnerBusy(updateInProgress);
25982604
}
25992605
// Pas de terminal dans le mode avancé désormais
26002606
if (document.body.classList.contains('details-mode')) {
@@ -2612,20 +2618,70 @@ tabs.forEach(tab => {
26122618
function hasUpdatesStreamingSupport() {
26132619
return !!(window.electronAPI?.startUpdates && window.electronAPI?.onUpdatesProgress);
26142620
}
2621+
let updateTimerInterval = null;
2622+
let updateTimerStart = null;
26152623

26162624
function setUpdateSpinnerBusy(isBusy) {
26172625
if (!updateSpinner) return;
26182626
updateSpinnerBusy = !!isBusy;
26192627
updateSpinner.setAttribute('data-busy', updateSpinnerBusy ? 'true' : 'false');
2628+
const hourglass = updateSpinner.querySelector('.update-hourglass');
2629+
const timer = updateSpinner.querySelector('.update-timer');
26202630
const label = updateSpinner.querySelector('.spinner-label');
2621-
if (label) label.textContent = t(updateSpinnerBusy ? 'updates.loading' : 'updates.ready');
2631+
if (runUpdatesBtn) runUpdatesBtn.classList.toggle('loading', updateSpinnerBusy);
2632+
if (updateSpinnerBusy) {
2633+
if (hourglass) hourglass.style.display = 'inline-block';
2634+
if (timer) timer.style.display = 'inline-block';
2635+
if (label) {
2636+
label.textContent = t('updates.loading');
2637+
label.style.display = '';
2638+
}
2639+
startUpdateTimer();
2640+
} else {
2641+
if (hourglass) hourglass.style.display = 'none';
2642+
if (timer) timer.style.display = 'none';
2643+
if (label) {
2644+
label.textContent = '';
2645+
label.style.display = 'none';
2646+
}
2647+
stopUpdateTimer();
2648+
}
2649+
}
2650+
2651+
function startUpdateTimer() {
2652+
const timer = document.querySelector('.update-timer');
2653+
if (!timer) return;
2654+
// Ne pas réinitialiser si déjà en cours
2655+
if (updateTimerStart === null) updateTimerStart = Date.now();
2656+
if (updateTimerInterval) return;
2657+
const updateTimerText = () => {
2658+
const elapsed = Math.max(0, Math.floor((Date.now() - updateTimerStart) / 1000));
2659+
if (elapsed < 60) {
2660+
timer.textContent = `${elapsed}s`;
2661+
} else {
2662+
const min = Math.floor(elapsed / 60);
2663+
const sec = String(elapsed % 60).padStart(2, '0');
2664+
timer.textContent = `${min}:${sec}`;
2665+
}
2666+
};
2667+
updateTimerText();
2668+
updateTimerInterval = setInterval(updateTimerText, 1000);
2669+
}
2670+
2671+
function stopUpdateTimer() {
2672+
if (updateTimerInterval) clearInterval(updateTimerInterval);
2673+
updateTimerInterval = null;
2674+
updateTimerStart = null;
26222675
}
26232676

26242677
function updateUpdatesToggleUi() {
26252678
if (!updatesToggleBtn) return;
2626-
const key = updatesTerminalExpanded ? 'updates.toggleHide' : 'updates.toggleShow';
2627-
updatesToggleBtn.textContent = t(key);
26282679
updatesToggleBtn.setAttribute('aria-expanded', updatesTerminalExpanded ? 'true' : 'false');
2680+
const section = document.getElementById('updatesLogSection');
2681+
if (section) section.setAttribute('data-open', updatesTerminalExpanded ? 'true' : 'false');
2682+
// Mettre à jour la flèche
2683+
const caret = updatesToggleBtn.querySelector('.updates-log-caret');
2684+
if (caret) caret.textContent = updatesTerminalExpanded ? '▾' : '▸';
26292685
}
26302686

26312687
function applyUpdatesTerminalVisibility() {
@@ -2849,10 +2905,13 @@ function handleUpdateCompletion(fullText){
28492905
}
28502906
const updated = parseUpdatedApps(sanitized);
28512907
const nothingPhrase = /Nothing to do here!?/i.test(sanitized || '');
2852-
let toShow = updated;
2908+
let toShow = new Set();
2909+
// Si on a trouvé la section officielle, on ne montre QUE ces apps
28532910
if (filteredUpdated && filteredUpdated.size > 0) {
2854-
// Ne garder que les apps détectées ET listées dans la section
2855-
toShow = new Set([...updated].filter(x => filteredUpdated.has(x)));
2911+
toShow = filteredUpdated;
2912+
} else if (!nothingPhrase && updated.size > 0) {
2913+
// Sinon fallback sur le parsing ligne par ligne, seulement si pas de message "rien à faire"
2914+
toShow = updated;
28562915
}
28572916
if (toShow.size > 0) {
28582917
if (updateFinalMessage) updateFinalMessage.textContent = t('updates.updatedApps');
@@ -2950,9 +3009,11 @@ runUpdatesBtn?.addEventListener('click', async () => {
29503009
const result = await fetchUpdatesOutput();
29513010
const raw = typeof result?.output === 'string' ? result.output : '';
29523011
handleUpdateCompletion(raw);
2953-
await refreshAfterUpdates();
29543012
const dur = Math.round((performance.now()-start)/1000);
29553013
if (updateFinalMessage && updateFinalMessage.textContent) updateFinalMessage.textContent += t('updates.duration', {dur});
3014+
// Arrêter le spinner dès que le résultat est prêt, avant le refresh lourd
3015+
setUpdateSpinnerBusy(false);
3016+
await refreshAfterUpdates();
29563017
} catch (err) {
29573018
console.error('Updates failed', err);
29583019
showToast(t('toast.updateFailed') || 'Échec des mises à jour');
@@ -3303,6 +3364,54 @@ function showPopupWarning(msg) {
33033364
modal.style.display = 'block';
33043365
}
33053366
}
3367+
3368+
// Gestion de la confirmation de fermeture avec installation en cours
3369+
const closeConfirmModal = document.getElementById('closeConfirmModal');
3370+
const closeConfirmStay = document.getElementById('closeConfirmStay');
3371+
const closeConfirmQuit = document.getElementById('closeConfirmQuit');
3372+
3373+
function showCloseConfirm() {
3374+
if (!closeConfirmModal) return;
3375+
closeConfirmModal.hidden = false;
3376+
applyTranslations(); // Mettre à jour les textes traduits
3377+
if (closeConfirmQuit) closeConfirmQuit.focus();
3378+
}
3379+
3380+
function hideCloseConfirm() {
3381+
if (!closeConfirmModal) return;
3382+
closeConfirmModal.hidden = true;
3383+
}
3384+
3385+
closeConfirmStay?.addEventListener('click', () => {
3386+
hideCloseConfirm();
3387+
});
3388+
3389+
closeConfirmQuit?.addEventListener('click', async () => {
3390+
hideCloseConfirm();
3391+
// Annuler l'installation en cours
3392+
if (activeInstallSession && !activeInstallSession.done) {
3393+
try {
3394+
await cancelActiveInstall();
3395+
} catch (err) {
3396+
console.error('Failed to cancel installation', err);
3397+
}
3398+
}
3399+
// Fermer l'application
3400+
if (window.electronAPI?.closeWindow) {
3401+
window.electronAPI.closeWindow();
3402+
}
3403+
});
3404+
3405+
// Intercepter la tentative de fermeture
3406+
if (window.electronAPI?.onBeforeClose) {
3407+
window.electronAPI.onBeforeClose(() => {
3408+
// Vérifier si une installation est en cours
3409+
if (activeInstallSession && activeInstallSession.id && !activeInstallSession.done) {
3410+
showCloseConfirm();
3411+
}
3412+
});
3413+
}
3414+
33063415
//# sourceMappingURL=app.js.map
33073416

33083417

0 commit comments

Comments
 (0)