From 140a8f6efe18cf85d9bc952d24f8036ee42347f2 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Tue, 15 Sep 2026 08:24:22 -0400 Subject: [PATCH] feat(viewer): #90 toolbar groupes aussi pour les fichiers texte non markdown [pop-out][Bookmark] | [Editer][Source][.ext][Pretty] | [Copier] | [Partager] - TOC ancre uniquement au markdown (plus de bouton TOC sur .sh/.py/...) - PDF/Export deja ancres au markdown: groupe export = [Copier] seul sur texte - tests toolbar-order mis a jour (conditions is_markdown sur nav/export) --- CHANGELOG.md | 6 ++++-- docs/archive/COMPLETED_v1-v2.md | 9 +++++++-- frontend/js/viewer.js | 12 ++++++++---- tests/frontend/toolbar-order.test.mjs | 21 ++++++++++++--------- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47f4c0e..9c0fd19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,8 +38,10 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). `[TOC] [pop-out] [Bookmark] | [Editer] [Source] [.md] [Forge] | [Copier] [PDF] [Export] | [Partager]`. Le bouton « Pretty » (fichiers texte) rejoint le groupe édition, les boutons non applicables à un type de fichier sont omis et les spacers d'un - groupe vide ne sont pas rendus. La fenêtre pop-out (`popout.html`) est - alignée sur le même ordre (groupes nav / édition / export / partage). + groupe vide ne sont pas rendus. Fichiers texte non markdown : + `[pop-out] [Bookmark] | [Editer] [Source] [.ext] [Pretty] | [Copier] | [Partager]` + (pas de TOC, pas de PDF/Export). La fenêtre pop-out (`popout.html`) est + alignée sur le même ordre markdown (groupes nav / édition / export / partage). Tests : `tests/frontend/toolbar-order.test.mjs` (9). ### Corrigé diff --git a/docs/archive/COMPLETED_v1-v2.md b/docs/archive/COMPLETED_v1-v2.md index 99ac1f9..55573de 100644 --- a/docs/archive/COMPLETED_v1-v2.md +++ b/docs/archive/COMPLETED_v1-v2.md @@ -369,9 +369,14 @@ séparés par des barres verticales (`span.action-sep`) : `[TOC] [pop-out] [Bookmark] | [Editer] [Source] [.md] [Forge] | [Copier] [PDF] [Export] | [Partager]` +Fichiers texte non markdown : + +`[pop-out] [Bookmark] | [Editer] [Source] [.ext] [Pretty] | [Copier] | [Partager]` + - **Viewer principal** (`frontend/js/viewer.js`, `renderFile`) : construction par - groupes `navBtns` / `editBtns` / `exportBtns` / `shareBtns` ; « Pretty » - (fichiers texte) ancré au groupe édition ; les spacers d'un groupe vide + groupes `navBtns` / `editBtns` / `exportBtns` / `shareBtns` ; TOC ancré au + markdown seul (`.sh`/`.py`/etc. sans TOC) ; « Pretty » (fichiers texte) ancré + au groupe édition après le bouton d'extension ; les spacers d'un groupe vide (ex. fichier image ou binaire) ne sont pas rendus. - **Pop-out** (`frontend/popout.html`) : même assemblage groupé, ordre aligné. - **CSS** (`frontend/style.css`) : règle `.file-actions .action-sep` diff --git a/frontend/js/viewer.js b/frontend/js/viewer.js index bae62cf..b3966e7 100644 --- a/frontend/js/viewer.js +++ b/frontend/js/viewer.js @@ -882,10 +882,14 @@ export function renderFile(data) { // Separateur visuel entre les groupes d'actions (#90) const actionSep = () => el("span", { class: "action-sep" }); // Ordre des boutons par groupes fonctionnels (#90) : - // [TOC][pop-out][Bookmark] | [Editer][Source][.md][Forge] | [Copier][PDF][Export] | [Partager] - // Boutons non affiches selon le type de fichier (Pretty, PDF/Export hors - // markdown, ...) restent ancres a leur groupe ; spacer de groupe vide retire. - const navBtns = [tocBtn, openNewWindowBtn, bookmarkBtn]; + // markdown : [TOC][pop-out][Bookmark] | [Editer][Source][.md][Forge] | [Copier][PDF][Export] | [Partager] + // texte autre : [pop-out][Bookmark] | [Editer][Source][.ext][Pretty] | [Copier] | [Partager] + // Boutons non applicables au type de fichier (TOC hors markdown, Pretty hors + // texte, PDF/Export hors markdown, ...) restent ancres a leur groupe ; + // spacer de groupe vide retire. + const navBtns = []; + if (data.is_markdown) navBtns.push(tocBtn); + navBtns.push(openNewWindowBtn, bookmarkBtn); const editBtns = []; if (!data.unsupported) editBtns.push(editBtn); editBtns.push(sourceBtn); diff --git a/tests/frontend/toolbar-order.test.mjs b/tests/frontend/toolbar-order.test.mjs index e841ec4..a50000d 100644 --- a/tests/frontend/toolbar-order.test.mjs +++ b/tests/frontend/toolbar-order.test.mjs @@ -35,14 +35,14 @@ function test(label, fn) { } // ── viewer.js : définition des groupes ─────────────────────────────────── -test("viewer.js — group nav = [toc, pop-out, bookmark]", () => { - assert.match( - viewer, - /const navBtns = \[\s*tocBtn,\s*openNewWindowBtn,\s*bookmarkBtn\s*\];/, - ); +test("viewer.js — group nav: TOC markdown-only, then pop-out, bookmark", () => { + const m = viewer.match(/const navBtns = \[\];([\s\S]*?)const editBtns/); + assert.ok(m, "navBtns block not found"); + assert.match(m[1], /if \(data\.is_markdown\) navBtns\.push\(tocBtn\);/); + assert.match(m[1], /navBtns\.push\(openNewWindowBtn,\s*bookmarkBtn\);/); }); -test("viewer.js — group edit = [edit, source, .md, forge] (pretty appended)", () => { +test("viewer.js — group edit = [edit, source, .ext, forge?, pretty?] in order", () => { const m = viewer.match( /const editBtns = \[\];([\s\S]*?)const exportBtns/, ); @@ -52,18 +52,21 @@ test("viewer.js — group edit = [edit, source, .md, forge] (pretty appended)", const re = /editBtns\.push\((\w+)\)/g; let g; while ((g = re.exec(block))) order.push(g[1]); - // editBtn conditional, then sourceBtn, then mdBtn conditional, then forge conditional + // editBtn conditional, then sourceBtn, then ext-download conditional, then forge conditional assert.deepEqual(order, ["editBtn", "sourceBtn", "mdBtn", "forgeBtn", "prettyBtn"]); assert.ok(block.includes("if (!data.unsupported) editBtns.push(editBtn)")); assert.ok(block.includes("if (!data.is_image) editBtns.push(mdBtn)")); assert.ok(block.includes("if (data.is_markdown) editBtns.push(forgeBtn)")); + // Pretty (text files) must land in the EDIT group, after the extension button + assert.ok(block.indexOf("editBtns.push(prettyBtn)") > block.indexOf("editBtns.push(mdBtn)")); }); -test("viewer.js — group export = [copier, pdf, export]", () => { +test("viewer.js — export group = [copier] + [pdf, export] markdown-only", () => { const m = viewer.match(/const exportBtns = \[([\s\S]*?)\];([\s\S]*?)const shareBtns/); assert.ok(m, "exportBtns block not found"); assert.ok(m[1].includes("copyBtn"), "copyBtn must be first in export group"); - assert.match(m[2], /exportBtns\.push\(pdfBtn,\s*exportWrap\)/); + assert.match(m[2], /if \(data\.is_markdown\) exportBtns\.push\(pdfBtn,\s*exportWrap\)/, + "PDF/Export must be markdown-only (text files: Copier alone)"); }); test("viewer.js — group share = [partager]", () => {