feat(viewer): #90 toolbar groupes aussi pour les fichiers texte non markdown
CI / lint (push) Successful in 1m18s
CI / security (push) Successful in 53s
CI / test (push) Successful in 2m30s
CI / build (push) Successful in 50s
CI / e2e (push) Successful in 10m27s

[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)
This commit is contained in:
2026-09-15 08:24:22 -04:00
parent 14b99826eb
commit 140a8f6efe
4 changed files with 31 additions and 17 deletions
+4 -2
View File
@@ -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é
+7 -2
View File
@@ -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`
+8 -4
View File
@@ -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);
+12 -9
View File
@@ -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]", () => {