fix(export_docs): sidebar folder tree follows parent/child paths

Render _Sidebar entries in DFS order instead of sorting by depth globally.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mihailkudravcev
2026-07-29 13:42:22 +03:00
parent cd79549c27
commit 31659d4287
3 changed files with 42 additions and 20 deletions
+6
View File
@@ -2,6 +2,12 @@
All notable changes to **export_1c_help** are documented in this file. All notable changes to **export_1c_help** are documented in this file.
## [0.3.12] - 2026-07-29
### Fixed
- `_Sidebar` folder tree: list subfolders under their real parent (DFS), not as a flat sort by depth (which made e.g. `transfer/mapping` appear under `ws`).
## [0.3.11] - 2026-07-29 ## [0.3.11] - 2026-07-29
### Fixed ### Fixed
+1 -1
View File
@@ -1 +1 @@
0.3.11 0.3.12
+35 -19
View File
@@ -344,32 +344,28 @@ def _docs_index_rel(folder: str, managed_files: set[str]) -> str | None:
def _sidebar_folder_links(folder: str, managed_files: set[str]) -> list[tuple[int, str, str]]: def _sidebar_folder_links(folder: str, managed_files: set[str]) -> list[tuple[int, str, str]]:
""" """
Return (depth, label, target) for folder index pages in wiki. Return (depth, label, target) for folder index pages in wiki, in tree order.
Supports both: Supports both:
- nested mode: docs/sub/INDEX.md - nested mode: docs/sub/INDEX.md
- flat mode: docs__sub__INDEX.md - flat mode: docs__sub__INDEX.md
""" """
folder_clean = folder.strip().strip("/").strip("\\") folder_clean = folder.strip().strip("/").strip("\\")
rows: list[tuple[int, str, str]] = []
if not folder_clean: if not folder_clean:
return rows return []
target_by_folder: dict[str, str] = {}
# nested mode
for rel in managed_files: for rel in managed_files:
if not rel.endswith("/INDEX.md"): if not rel.endswith("/INDEX.md"):
continue continue
if not rel.startswith(folder_clean + "/"): if rel.startswith(folder_clean + "/"):
continue folder_rel = rel[: -len("/INDEX.md")]
folder_rel = rel[: -len("/INDEX.md")] target_by_folder[folder_rel] = rel
depth = max(folder_rel.count("/") - folder_clean.count("/"), 0)
rows.append((depth, folder_rel, rel))
# flat mode
root_flat = f"{folder_clean}__INDEX.md" root_flat = f"{folder_clean}__INDEX.md"
if root_flat in managed_files: if root_flat in managed_files:
rows.append((0, folder_clean, root_flat)) target_by_folder[folder_clean] = root_flat
prefix = f"{folder_clean}__" prefix = f"{folder_clean}__"
suffix = "__INDEX.md" suffix = "__INDEX.md"
for rel in managed_files: for rel in managed_files:
@@ -379,14 +375,34 @@ def _sidebar_folder_links(folder: str, managed_files: set[str]) -> list[tuple[in
if not mid: if not mid:
continue continue
folder_rel = folder_clean + "/" + mid.replace("__", "/") folder_rel = folder_clean + "/" + mid.replace("__", "/")
depth = max(folder_rel.count("/") - folder_clean.count("/"), 0) target_by_folder[folder_rel] = rel
rows.append((depth, folder_rel, rel))
# dedupe by target if not target_by_folder:
uniq: dict[str, tuple[int, str, str]] = {} return []
for row in rows:
uniq[row[2]] = row children: dict[str, list[str]] = {}
return sorted(uniq.values(), key=lambda x: (x[0], x[1].casefold())) for folder_rel in target_by_folder:
if folder_rel == folder_clean:
continue
parent = Path(folder_rel).parent.as_posix()
if parent != folder_clean and not parent.startswith(folder_clean + "/"):
parent = folder_clean
children.setdefault(parent, []).append(folder_rel)
ordered: list[tuple[int, str, str]] = []
def walk(parent: str, depth: int) -> None:
for child in sorted(children.get(parent, []), key=lambda x: x.casefold()):
ordered.append((depth, child, target_by_folder[child]))
walk(child, depth + 1)
if folder_clean in target_by_folder:
ordered.append((0, folder_clean, target_by_folder[folder_clean]))
walk(folder_clean, 1)
else:
walk(folder_clean, 0)
return ordered
def ensure_root_navigation( def ensure_root_navigation(