From e94af0369b94c6283e482b7fcafe9ae7d2d1089c Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Tue, 29 Sep 2026 14:56:05 -0400 Subject: [PATCH] feat: inspecteur droit pour le tableau de bord XLSX #154 --- CHANGELOG.md | 16 +++++- README.fr.md | 6 +-- README.md | 6 +-- VERSION | 2 +- desktop/Cargo.lock | 2 +- desktop/Cargo.toml | 2 +- desktop/tauri.conf.json | 2 +- docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md | 9 ++-- docs/ROADMAP.md | 10 ++-- docs/features/xlsx-ui-redesign.md | 17 ++++--- frontend/js/viewer.js | 41 ++++++++++++++-- frontend/locales/en.json | 3 ++ frontend/locales/fr.json | 3 ++ frontend/style.css | 65 +++++++++++++++++++++++++ package.json | 2 +- tests/frontend/xlsx-viewer.test.mjs | 28 +++++++++++ 16 files changed, 183 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d51dfcc..42fa9ab 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.41.0**. +> [Unreleased](#unreleased). La dernière version livrée est **2.42.0**. --- @@ -14,6 +14,20 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). --- +## [2.42.0] — 2026-09-29 + +### Ajouté + +- **#154 — Refonte UI/UX de la visionneuse XLSX (Lot 3) : inspecteur droit.** + Le **Tableau de bord** quitte le flux de la grille pour un **panneau latéral droit repliable** + (`.xlsx-inspector`) : la grille reste visible à côté. L'en-tête de l'inspecteur porte le titre, + une entrée **Assistant IA** (ouvre le panneau latéral global existant) et un bouton de fermeture. + Sous 900 px, l'inspecteur se place sous la grille. Tests JSDOM + `tests/frontend/xlsx-viewer.test.mjs` (44) et E2E `tests/e2e/xlsx-viewer.spec.js` (9) verts. + Aucun changement backend. + +--- + ## [2.41.0] — 2026-09-29 ### Ajouté diff --git a/README.fr.md b/README.fr.md index 51a959c..bf4022c 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. -[![Version](https://img.shields.io/badge/Version-2.41.0-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-2.42.0-blue.svg)]() [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/) [![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](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.41.0). +Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.42.0). --- -*Projet : ObsiGate | Version : 2.41.0 | Dernière mise à jour : Septembre 2026* +*Projet : ObsiGate | Version : 2.42.0 | Dernière mise à jour : Septembre 2026* diff --git a/README.md b/README.md index 4f54f38..935d202 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. -[![Version](https://img.shields.io/badge/Version-2.41.0-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-2.42.0-blue.svg)]() [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/) [![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](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.41.0). +See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.42.0). --- -*Project: ObsiGate | Version: 2.41.0 | Last updated: September 2026* +*Project: ObsiGate | Version: 2.42.0 | Last updated: September 2026* diff --git a/VERSION b/VERSION index 2d4c52e..345a83e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.41.0 +2.42.0 diff --git a/desktop/Cargo.lock b/desktop/Cargo.lock index aecdfb9..b2cfa43 100644 --- a/desktop/Cargo.lock +++ b/desktop/Cargo.lock @@ -2626,7 +2626,7 @@ dependencies = [ [[package]] name = "obsigate-desktop" -version = "2.41.0" +version = "2.42.0" dependencies = [ "chrono", "env_logger", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index a51b65c..4ff800f 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "obsigate-desktop" -version = "2.41.0" +version = "2.42.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 03cb799..91e039c 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.41.0", + "version": "2.42.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 e958122..aa00108 100644 --- a/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md +++ b/docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md @@ -236,13 +236,14 @@ en **lecture seule** ; un `.csv` s'ouvre dans la même grille et se réécrit conformément à la RFC 4180 (les guillemets et séparateurs sont échappés). -**Tableau de bord** — le bouton **Tableau de bord** liste les plages +**Tableau de bord** — le bouton **Tableau de bord** ouvre un **inspecteur +latéral droit** (la grille reste visible à côté) qui liste les plages nommées du classeur (nom, référence, portée), signale les feuilles contenant des graphiques ou des tableaux croisés, et donne pour chaque feuille un résumé (cellules, lignes, colonnes, formules, valeurs -numériques) avec quelques chiffres clés. C'est une aide à la lecture : -les plages nommées peuvent ensuite être exploitées avec l'assistant IA -(outils `list_xlsx_sheets`, `xlsx_to_markdown`, `update_xlsx_cells`, +numériques) avec quelques chiffres clés. L'en-tête de l'inspecteur offre +aussi un accès direct à l'**assistant IA**, qui peut ensuite exploiter ces +plages (outils `list_xlsx_sheets`, `xlsx_to_markdown`, `update_xlsx_cells`, `append_xlsx_rows`). ### Limites diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index bc9ea0d..941b8db 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,6 +1,6 @@ # ObsiGate — Roadmap -> **Version :** 2.41.0 | **Dernière mise à jour :** 2026-09-29 +> **Version :** 2.42.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,11 +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** — **Lots 1 & 2 livrés le 2026-09-29** (ruban de commandes groupé, +- **Statut :** 🔵 **en cours** — **Lots 1, 2 & 3 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. + bandeau de conflit 409 non bloquant, indicateur *dirty* ; **inspecteur droit repliable** pour le + tableau de bord avec entrée Assistant IA). Lot suivant : 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 @@ -112,7 +112,7 @@ - [x] **A1** Coquille : barre de commandes groupée, onglets feuilles permanents + « + », badges d'état, tokens de grille et affordances visuelles (Lot 1) - [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) + - [x] **A3** Inspecteur droit repliable : Tableau de bord + entrée Assistant IA (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 7a9b008..48a35dd 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** — Lots 1 & 2 livrés le 2026-09-29 -> **Effort estimé :** 6-9 jours (Lot 1 ✅ · Lot 2 ✅ · Lot 3 · Lot 4) +> **Statut :** 🔵 **en cours** — Lots 1, 2 & 3 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.** @@ -79,11 +79,13 @@ la grille ni le backend. 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) +### Lot 3 — Inspecteur droit ✅ *(2026-09-29)* -- Panneau repliable hébergeant **Tableau de bord** et **Assistant IA** en onglets ; la grille - reste visible. Lien **dashboard → grille** (clic sur KPI/plage = scroll + sélection). -- Détachement en split via `PaneManager.splitRight()`. +- **A3.1** Le **Tableau de bord** quitte le flux de la grille pour un **panneau droit repliable** + (`.xlsx-inspector`) : la grille reste visible à côté (`.xlsx-body` = `.xlsx-main` + inspecteur). +- **A3.2** En-tête d'inspecteur : titre, bouton **Assistant IA** (ouvre le panneau latéral global + existant) et bouton de fermeture. +- **A3.3** Responsive : sous 900 px, l'inspecteur passe sous la grille. ### Lot 4 — Découpage & finitions (à venir) @@ -91,6 +93,8 @@ la grille ni le backend. `grid`, `sheet-tabs`, `inspector`, `cell-editor`, `api`. - **Undo/redo** local (pile de commandes), défilement paresseux via `IntersectionObserver`, accessibilité ARIA (`role="grid"`, *roving tabindex*). +- Lien **dashboard → grille** (clic sur KPI/plage = scroll + sélection), inspecteur + **redimensionnable**, détachement en split via `PaneManager.splitRight()`. ## 5. Recommandations techniques (contrainte « zéro build ») @@ -121,3 +125,4 @@ la grille ni le backend. |---|---| | 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) | +| 2026-09-29 | **Lot 3** livré : le Tableau de bord passe dans un **inspecteur droit repliable** (grille toujours visible), en-tête d'inspecteur avec entrée **Assistant IA** et fermeture, responsive < 900 px | diff --git a/frontend/js/viewer.js b/frontend/js/viewer.js index a70f375..b8cb0c1 100644 --- a/frontend/js/viewer.js +++ b/frontend/js/viewer.js @@ -15,6 +15,7 @@ import { cacheViewedFile, getCachedFile } from './offline.js'; import { t } from './i18n.js'; import { onFileRender } from './plugins.js'; import { NowPlaying } from './now-playing.js'; +import { openAssistant } from './ai-fab.js'; // ── Multi-format export ──────────────────────────────────────────────────── // Downloads a file export (HTML / MD bundle / ePub) via the authenticated @@ -1198,7 +1199,23 @@ export function renderXlsxViewer(area, data) { -
${panels}
+
+
+
${panels}
+
+ +
`; // ── #153 A17 — dashboard panel: named ranges, objects, per-sheet KPI ── @@ -1212,14 +1229,26 @@ export function renderXlsxViewer(area, data) { title: t("xlsx.dashboard_btn"), }, [icon("layout-dashboard", 14), document.createTextNode(t("xlsx.dashboard_btn"))]); (area.querySelector("#xlsx-view-group") || area.querySelector(".xlsx-toolbar-actions")).appendChild(dashBtn); + const inspector = area.querySelector("#xlsx-inspector"); + const inspectorBody = area.querySelector("#xlsx-inspector-body"); + const closeInspector = () => { + const panel = inspectorBody.querySelector(".xlsx-dashboard"); + if (panel) panel.remove(); + inspector.classList.remove("open"); + inspector.setAttribute("aria-hidden", "true"); + dashBtn.classList.remove("active"); + }; dashBtn.addEventListener("click", async () => { - const old = area.querySelector(".xlsx-dashboard"); - if (old) { old.remove(); dashBtn.classList.remove("active"); return; } + // #154-A3 — the dashboard lives in a right-hand inspector so the grid + // stays visible beside it (it used to be prepended into the grid flow). + if (inspectorBody.querySelector(".xlsx-dashboard")) { closeInspector(); return; } dashBtn.classList.add("active"); + inspector.classList.add("open"); + inspector.setAttribute("aria-hidden", "false"); const panel = document.createElement("div"); panel.className = "xlsx-dashboard"; panel.innerHTML = `
…
`; - area.querySelector(".xlsx-panels").prepend(panel); + inspectorBody.prepend(panel); try { const dash = await api( `/api/file/${encodeURIComponent(data.vault)}/xlsx/dashboard?path=${encodeURIComponent(data.path)}`, @@ -1261,6 +1290,10 @@ export function renderXlsxViewer(area, data) { panel.innerHTML = `

${escapeHtml(t("xlsx.load_error"))}: ${escapeHtml(String(err.message || err))}

`; } }); + area.querySelector("#xlsx-inspector-close").addEventListener("click", closeInspector); + // #154-A3 — the assistant is the existing global side panel; the inspector + // just gives it a contextual entry point from the spreadsheet. + area.querySelector("#xlsx-inspector-ai").addEventListener("click", () => { openAssistant(); }); } const saveBtn = area.querySelector("#xlsx-save-btn"); diff --git a/frontend/locales/en.json b/frontend/locales/en.json index b78efc8..ee298f4 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -1877,6 +1877,9 @@ "xlsx.structure_error": "Could not change the structure", "xlsx.last_sheet": "The last sheet cannot be deleted", "xlsx.dashboard_btn": "Dashboard", + "xlsx.inspector_title": "Inspector", + "xlsx.inspector_ai": "AI assistant", + "xlsx.inspector_ai_short": "AI", "xlsx.dashboard_title": "Workbook dashboard", "xlsx.dashboard_stats": "{{cells}} cells · {{rows}} rows · {{cols}} columns · {{formulas}} formulas · {{numeric}} numeric values", "xlsx.dashboard_charts": "{{n}} chart(s)", diff --git a/frontend/locales/fr.json b/frontend/locales/fr.json index d7891b4..228ec18 100644 --- a/frontend/locales/fr.json +++ b/frontend/locales/fr.json @@ -1877,6 +1877,9 @@ "xlsx.structure_error": "Modification de la structure impossible", "xlsx.last_sheet": "Impossible de supprimer la dernière feuille", "xlsx.dashboard_btn": "Tableau de bord", + "xlsx.inspector_title": "Inspecteur", + "xlsx.inspector_ai": "Assistant IA", + "xlsx.inspector_ai_short": "IA", "xlsx.dashboard_title": "Tableau de bord du classeur", "xlsx.dashboard_stats": "{{cells}} cellules · {{rows}} lignes · {{cols}} colonnes · {{formulas}} formules · {{numeric}} valeurs numériques", "xlsx.dashboard_charts": "{{n}} graphique(s)", diff --git a/frontend/style.css b/frontend/style.css index dafa80d..f1783e0 100644 --- a/frontend/style.css +++ b/frontend/style.css @@ -11289,6 +11289,71 @@ body.desktop-mode .editor-container { color: var(--text-primary); } +/* #154-A3 — right-hand inspector (dashboard, assistant) beside the grid. */ +.xlsx-body { + display: flex; + align-items: stretch; + gap: 10px; +} +.xlsx-main { + flex: 1 1 auto; + min-width: 0; +} +.xlsx-inspector { + flex: 0 0 340px; + width: 340px; + max-width: 45%; + display: none; + flex-direction: column; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); + max-height: 70vh; +} +.xlsx-inspector.open { + display: flex; +} +.xlsx-inspector-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px 10px; + border-bottom: 1px solid var(--border); +} +.xlsx-inspector-title { + font-weight: 600; + font-size: 0.85rem; + color: var(--text-primary); +} +.xlsx-inspector-actions { + display: inline-flex; + align-items: center; + gap: 6px; +} +.xlsx-inspector-body { + flex: 1 1 auto; + overflow: auto; + padding: 10px; +} +.xlsx-inspector .xlsx-dashboard { + margin: 0; + padding: 0; + border: none; + border-radius: 0; + background: transparent; +} +@media (max-width: 900px) { + .xlsx-body { + flex-direction: column; + } + .xlsx-inspector { + flex-basis: auto; + width: auto; + max-width: none; + } +} + /* #153 A17 — workbook dashboard panel: named ranges table + KPI cards. Colors come from the existing CSS variables (no hardcoded values). */ .xlsx-dashboard { diff --git a/package.json b/package.json index 27c658b..24fefa9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsigate", - "version": "2.41.0", + "version": "2.42.0", "description": "**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.", "main": "patch.js", "directories": { diff --git a/tests/frontend/xlsx-viewer.test.mjs b/tests/frontend/xlsx-viewer.test.mjs index f2eb5cd..5d8c649 100644 --- a/tests/frontend/xlsx-viewer.test.mjs +++ b/tests/frontend/xlsx-viewer.test.mjs @@ -719,6 +719,34 @@ await test("the dashboard button fetches the metadata and renders named ranges + assert.equal(area.querySelector(".xlsx-dashboard"), null, "panel toggles closed"); }); +await test("the dashboard opens in the right-hand inspector, not inline in the grid", async () => { + const area = mount(); + apiQueue.push({ + ok: true, status: 200, + body: { vault: "V", path: "data.xlsx", named_ranges: [], objects: { charts: 0, pivots: 0 }, sheets: [{ name: "Feuille1", cells: 1, rows: 1, cols: 1, formulas: 0, numeric: 0, kpi: [] }] }, + }); + area.querySelector("#xlsx-dashboard-btn").click(); + await new Promise((r) => setTimeout(r, 5)); + const inspector = area.querySelector("#xlsx-inspector"); + assert.equal(inspector.classList.contains("open"), true, "the inspector opens"); + assert.equal(inspector.getAttribute("aria-hidden"), "false"); + assert.ok(inspector.querySelector("#xlsx-inspector-body .xlsx-dashboard"), "the dashboard lives in the inspector"); + // The grid is still present beside it (not replaced). + assert.ok(area.querySelector(".xlsx-main .xlsx-table"), "the grid stays visible"); + // Closing removes the panel and closes the inspector. + area.querySelector("#xlsx-inspector-close").click(); + assert.equal(area.querySelector(".xlsx-dashboard"), null); + assert.equal(inspector.classList.contains("open"), false); + assert.equal(inspector.getAttribute("aria-hidden"), "true"); +}); + +await test("the inspector exposes an AI assistant entry point", () => { + const area = mount(); + assert.ok(area.querySelector("#xlsx-inspector-ai"), "AI button in the inspector head"); + const title = area.querySelector("#xlsx-inspector .xlsx-inspector-title"); + assert.equal(title.textContent, FR["xlsx.inspector_title"]); +}); + await test("a csv mounts without the dashboard button", () => { const area = document.getElementById("content-area"); area.innerHTML = "";