diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63f2baa..d51dfcc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/),
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
> **En cours de développement** : les changements à venir sont listés dans la section
-> [Unreleased](#unreleased). La dernière version livrée est **2.40.0**.
+> [Unreleased](#unreleased). La dernière version livrée est **2.41.0**.
---
@@ -14,6 +14,24 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
---
+## [2.41.0] — 2026-09-29
+
+### Ajouté
+
+- **#154 — Refonte UI/UX de la visionneuse XLSX (Lot 2) : dialogues thémés et conflits non
+ bloquants.**
+ Les `confirm()` / `prompt()` natifs sont remplacés par des **dialogues intégrés au thème**
+ (`showConfirm()` / `showPrompt()` dans `frontend/js/ui.js`, promise-based, réutilisant
+ `.obsigate-modal-*`) pour toutes les actions de structure du classeur (ajouter / renommer /
+ dupliquer / supprimer une feuille, insérer / supprimer une ligne ou une colonne) et la
+ confirmation de perte (`409 xlsx_lossy_content`). Un **conflit d'écriture** (`409 conflict`)
+ n'interrompt plus l'utilisateur : un **bandeau non bloquant** propose de réessayer en
+ conservant les modifications. Le bouton **Enregistrer** et l'onglet de la feuille concernée
+ signalent les modifications non sauvegardées. Tests JSDOM `tests/frontend/xlsx-viewer.test.mjs`
+ (42) et E2E `tests/e2e/xlsx-viewer.spec.js` (9) adaptés. Aucun changement backend.
+
+---
+
## [2.40.0] — 2026-09-29
### Ajouté
diff --git a/README.fr.md b/README.fr.md
index 59004c5..51a959c 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -4,7 +4,7 @@
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
-[]()
+[]()
[](https://opensource.org/licenses/MIT)
[](https://www.docker.com/)
[](https://www.python.org/)
@@ -976,8 +976,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
## 📝 Changelog
-Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.40.0).
+Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.41.0).
---
-*Projet : ObsiGate | Version : 2.40.0 | Dernière mise à jour : Septembre 2026*
+*Projet : ObsiGate | Version : 2.41.0 | Dernière mise à jour : Septembre 2026*
diff --git a/README.md b/README.md
index 768c4db..4f54f38 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
-[]()
+[]()
[](https://opensource.org/licenses/MIT)
[](https://www.docker.com/)
[](https://www.python.org/)
@@ -1151,8 +1151,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
## 📝 Changelog
-See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.40.0).
+See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.41.0).
---
-*Project: ObsiGate | Version: 2.40.0 | Last updated: September 2026*
+*Project: ObsiGate | Version: 2.41.0 | Last updated: September 2026*
diff --git a/VERSION b/VERSION
index 770060b..2d4c52e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.40.0
+2.41.0
diff --git a/desktop/Cargo.lock b/desktop/Cargo.lock
index dc84c8b..aecdfb9 100644
--- a/desktop/Cargo.lock
+++ b/desktop/Cargo.lock
@@ -2626,7 +2626,7 @@ dependencies = [
[[package]]
name = "obsigate-desktop"
-version = "2.40.0"
+version = "2.41.0"
dependencies = [
"chrono",
"env_logger",
diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml
index 3489b66..a51b65c 100644
--- a/desktop/Cargo.toml
+++ b/desktop/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "obsigate-desktop"
-version = "2.40.0"
+version = "2.41.0"
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
authors = ["Bruno Charest"]
edition = "2021"
diff --git a/desktop/tauri.conf.json b/desktop/tauri.conf.json
index ccfcc24..03cb799 100644
--- a/desktop/tauri.conf.json
+++ b/desktop/tauri.conf.json
@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
"productName": "ObsiGate",
- "version": "2.40.0",
+ "version": "2.41.0",
"identifier": "com.obsigate.desktop",
"build": {
"frontendDist": "../frontend",
diff --git a/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md b/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md
index aea9b23..e958122 100644
--- a/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md
+++ b/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md
@@ -159,10 +159,15 @@ réécrire : **valeurs calculées** mises en cache par Excel, segments
(slicers), chronologies, contrôles de formulaire, connexions/requêtes,
XML personnalisé, signature numérique, commentaires enrichis, macros.
L'ouverture affiche alors un bandeau qui les liste, et la première
-sauvegarde demande confirmation. Si vous refusez, rien n'est écrit.
+sauvegarde demande confirmation dans une fenêtre intégrée au thème de
+l'application. Si vous refusez, rien n'est écrit.
> Les **graphiques, images et tableaux croisés** sont, eux, bien conservés.
+Si le classeur est modifié ailleurs entre-temps (verrou concurrent), ObsiGate
+n'interrompt pas votre travail : un bandeau vous propose de **réessayer**
+l'enregistrement, vos modifications restant en place.
+
### Formules
Par sécurité, une valeur saisie commençant par `=` ou `@` est **stockée comme
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index ad915b6..bc9ea0d 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -1,6 +1,6 @@
# ObsiGate — Roadmap
-> **Version :** 2.40.0 | **Dernière mise à jour :** 2026-09-29
+> **Version :** 2.41.0 | **Dernière mise à jour :** 2026-09-29
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
> vers les fonctionnalités livrées.
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
@@ -97,10 +97,11 @@
### 154. Refonte UI/UX de la visionneuse & éditeur XLSX (ruban, grille, inspecteur)
- **Effort :** 6-9 jours (Lot 1 ✅ · Lot 2 · Lot 3 · Lot 4) | **Impact :** 🟡
-- **Statut :** 🔵 **en cours** — **Lot 1 livré le 2026-09-29** (ruban de commandes groupé, onglets
- de feuilles permanents avec bouton « + », badges d'état lecture seule / formules non
- recalculées, tokens de grille et affordances). Lots suivants : dialogues thémés, inspecteur
- droit (dashboard + IA), découpage modulaire + undo/redo.
+- **Statut :** 🔵 **en cours** — **Lots 1 & 2 livrés le 2026-09-29** (ruban de commandes groupé,
+ onglets de feuilles permanents avec bouton « + », badges d'état lecture seule / formules non
+ recalculées, tokens de grille et affordances ; dialogues thémés `showConfirm`/`showPrompt`,
+ bandeau de conflit 409 non bloquant, indicateur *dirty*). Lots suivants : inspecteur droit
+ (dashboard + IA), découpage modulaire + undo/redo.
- **Analyse, architecture cible et plan par lots :** [features/xlsx-ui-redesign.md](./features/xlsx-ui-redesign.md)
- **Description :** la visionneuse XLSX (#152/#153) est fonctionnelle mais peu conviviale :
commandes à plat sans hiérarchie, en-têtes de grille indistincts des cellules, états avancés
@@ -110,7 +111,7 @@
- **Sous-tâches :**
- [x] **A1** Coquille : barre de commandes groupée, onglets feuilles permanents + « + »,
badges d'état, tokens de grille et affordances visuelles (Lot 1)
- - [ ] **A2** Dialogues thémés (modales + toasts) et feedback non bloquant des conflits 409 (Lot 2)
+ - [x] **A2** Dialogues thémés (modales + toasts) et feedback non bloquant des conflits 409 (Lot 2)
- [ ] **A3** Inspecteur droit repliable : Tableau de bord + Assistant IA, lien dashboard ⇄ grille (Lot 3)
- [ ] **A4** Découpage `frontend/js/xlsx/*` + undo/redo + défilement via `IntersectionObserver` (Lot 4)
diff --git a/docs/features/xlsx-ui-redesign.md b/docs/features/xlsx-ui-redesign.md
index f2623c7..7a9b008 100644
--- a/docs/features/xlsx-ui-redesign.md
+++ b/docs/features/xlsx-ui-redesign.md
@@ -2,8 +2,8 @@
> **Item de roadmap :** [#154 — Refonte UI/UX tableur](../ROADMAP.md)
> **Origine :** #152 / #153 (visionneuse XLSX fonctionnelle mais peu conviviale)
-> **Statut :** 🔵 **en cours** — Lot 1 livré le 2026-09-29
-> **Effort estimé :** 6-9 jours (Lot 1 ✅ · Lot 2 · Lot 3 · Lot 4)
+> **Statut :** 🔵 **en cours** — Lots 1 & 2 livrés le 2026-09-29
+> **Effort estimé :** 6-9 jours (Lot 1 ✅ · Lot 2 ✅ · Lot 3 · Lot 4)
> **Règle de maintenance :** la Roadmap porte les cases à cocher (suivi), cette fiche porte
> l'analyse, l'architecture cible et le plan par lots. **Ne pas dupliquer le détail.**
@@ -68,12 +68,16 @@ la grille ni le backend.
`--grid-border`, `--grid-zebra`) déclinés dark/light + affordances (en-têtes clairs,
zébrage, survol, cellule active solide, cellule *dirty* prioritaire au survol).
-### Lot 2 — Dialogues thémés & feedback (à venir)
+### Lot 2 — Dialogues thémés & feedback ✅ *(2026-09-29)*
-- Remplacer `confirm()`/`prompt()` par des **modales thémées** (`.obsigate-modal-*`) et des
- **toasts** : structure (feuille/ligne/colonne), perte (409 `xlsx_lossy_content`), conflit
- (409 `conflict`) en bandeau non bloquant.
-- Indicateur *dirty* sur l'onglet de feuille et la barre.
+- **A2.1** Helpers génériques **`showConfirm()` / `showPrompt()`** (`frontend/js/ui.js`), promise-based,
+ réutilisant les classes `.obsigate-modal-*` (fini `window.confirm()` / `window.prompt()`).
+- **A2.2** La visionneuse XLSX utilise ces dialogues pour les actions de structure (ajouter /
+ renommer / dupliquer / supprimer feuille, insérer / supprimer ligne et colonne) et pour la
+ confirmation de perte (409 `xlsx_lossy_content`).
+- **A2.3** **Conflit de sauvegarde (409 `conflict`)** : bandeau **non bloquant** `.xlsx-banner-conflict`
+ avec bouton **Réessayer** — les modifications sont conservées.
+- **A2.4** Indicateur *dirty* sur le bouton **Enregistrer** et sur l'onglet de la feuille concernée.
### Lot 3 — Inspecteur droit (à venir)
@@ -116,3 +120,4 @@ la grille ni le backend.
| Date | Événement |
|---|---|
| 2026-09-29 | Audit UX (3 problèmes) + architecture cible + plan par lots ; **Lot 1** livré (ruban groupé, onglets permanents + « + », badges d'état, tokens de grille) |
+| 2026-09-29 | **Lot 2** livré : dialogues thémés (`showConfirm`/`showPrompt`) pour la structure et la confirmation de perte, bandeau de conflit 409 non bloquant avec réessai, indicateur *dirty* (bouton + onglet) |
diff --git a/frontend/js/ui.js b/frontend/js/ui.js
index 743c5c7..b70aab3 100644
--- a/frontend/js/ui.js
+++ b/frontend/js/ui.js
@@ -1143,6 +1143,88 @@ const FileOperations = {
};
+// ---------------------------------------------------------------------------
+// Generic themed dialogs (#154-A2)
+// ---------------------------------------------------------------------------
+// Promise-based replacements for window.confirm() / window.prompt() so the
+// Excel viewer's structure actions and lossy-write confirmations stay inside
+// the app theme (and are keyboard accessible) instead of native dialogs.
+
+function _closeDialog(overlay, resolve, value) {
+ overlay.classList.remove("active");
+ if (overlay._onKey) document.removeEventListener("keydown", overlay._onKey);
+ setTimeout(() => overlay.remove(), 200);
+ resolve(value);
+}
+
+function _openDialog(innerHtml) {
+ const overlay = document.createElement("div");
+ overlay.className = "obsigate-modal-overlay";
+ const modal = document.createElement("div");
+ modal.className = "obsigate-modal";
+ modal.setAttribute("role", "dialog");
+ modal.setAttribute("aria-modal", "true");
+ modal.innerHTML = innerHtml;
+ overlay.appendChild(modal);
+ document.body.appendChild(overlay);
+ setTimeout(() => overlay.classList.add("active"), 10);
+ return { overlay, modal };
+}
+
+/** Themed replacement for window.confirm(). Resolves to a boolean. */
+export function showConfirm({ title = "", message = "", confirmLabel = "", cancelLabel = "", danger = false } = {}) {
+ return new Promise((resolve) => {
+ const { overlay, modal } = _openDialog(`
+