fix: tri naturel de la navigation (numerique au lieu de lexicographique)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-04 07:02:12 +02:00
parent 5a8e5f4cc9
commit 65dcdd86e0
+15 -6
View File
@@ -333,6 +333,15 @@ function escapeHtml(str) {
.replace(/"/g, '&quot;'); .replace(/"/g, '&quot;');
} }
/**
* Natural sort comparator: orders numeric chunks by value, not lexically,
* so "2" comes before "11" (and "2.1" before "2.10"). Case-insensitive.
*/
const collator = new Intl.Collator('fr', { numeric: true, sensitivity: 'base' });
function naturalCompare(a, b) {
return collator.compare(a, b);
}
/** Build a nested tree object from a flat list of relative paths. */ /** Build a nested tree object from a flat list of relative paths. */
function buildTree(relPaths) { function buildTree(relPaths) {
const tree = { _files: [], _dirs: {} }; const tree = { _files: [], _dirs: {} };
@@ -354,8 +363,8 @@ function renderTree(node, baseDir, currentRel, depth) {
let html = ''; let html = '';
const depthClass = depth > 0 ? ` class="nav-depth-${Math.min(depth, 3)}"` : ''; const depthClass = depth > 0 ? ` class="nav-depth-${Math.min(depth, 3)}"` : '';
// Files first (sorted) // Files first (natural sort)
node._files.slice().sort().forEach((rel) => { node._files.slice().sort(naturalCompare).forEach((rel) => {
const pagePath = rel.replace(/\.md$/i, ''); const pagePath = rel.replace(/\.md$/i, '');
const label = path.basename(pagePath).replace(/-/g, ' '); const label = path.basename(pagePath).replace(/-/g, ' ');
const href = '/' + pagePath.split('/').map(encodeURIComponent).join('/'); const href = '/' + pagePath.split('/').map(encodeURIComponent).join('/');
@@ -368,8 +377,8 @@ function renderTree(node, baseDir, currentRel, depth) {
} }
}); });
// Sub-directories (sorted) // Sub-directories (natural sort)
Object.keys(node._dirs).sort().forEach((dirName) => { Object.keys(node._dirs).sort(naturalCompare).forEach((dirName) => {
const child = node._dirs[dirName]; const child = node._dirs[dirName];
const isOpen = currentRel && currentRel.split(path.sep).includes(dirName); const isOpen = currentRel && currentRel.split(path.sep).includes(dirName);
const openAttr = isOpen ? ' open' : ''; const openAttr = isOpen ? ' open' : '';
@@ -498,8 +507,8 @@ function buildIndex(baseDir, files, watchMode = false) {
groups.get(group).push(rel); groups.get(group).push(rel);
}); });
const listHtml = [...groups.keys()].sort().map((group) => { const listHtml = [...groups.keys()].sort(naturalCompare).map((group) => {
const items = groups.get(group).sort().map((rel) => { const items = groups.get(group).sort(naturalCompare).map((rel) => {
const pagePath = rel.replace(/\.md$/i, ''); const pagePath = rel.replace(/\.md$/i, '');
const label = path.basename(pagePath).replace(/-/g, ' '); const label = path.basename(pagePath).replace(/-/g, ' ');
const href = '/' + pagePath.split('/').map(encodeURIComponent).join('/'); const href = '/' + pagePath.split('/').map(encodeURIComponent).join('/');