diff --git a/tools/build_wiki.py b/tools/build_wiki.py index 13d64f1..69fb4b7 100644 --- a/tools/build_wiki.py +++ b/tools/build_wiki.py @@ -8,7 +8,8 @@ the `--host` URL flavor and the CI wrapper differ. The wiki is GENERATED BUILD OUTPUT. Never hand-edit it; edit modules/ and let CI re-render. Labs are NOT copied into the wiki — lesson pages link to the runnable -files back in the main repo. +files back in the main repo. Each page gets prev/next navigation so the wiki reads +like a textbook. Usage: build_wiki.py --repo-root . --out \ @@ -105,6 +106,19 @@ def home_intro(repo_root: Path) -> str: return "\n".join(out).strip() +def top_nav(prev) -> str: + """prev is (page, label) or None.""" + if not prev: + return "" + return f"⬅ **Previous: [{prev[1]}]({prev[0]})**\n" + + +def bottom_nav(nxt) -> str: + if not nxt: + return "" + return f"**Continue to: [{nxt[1]}]({nxt[0]})** ➡\n" + + def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--repo-root", default=".") @@ -123,43 +137,47 @@ def main() -> int: print("no modules found", flush=True) return 1 + # --- pass 1: read everything, build the ordered reading sequence ------ + # each entry: {page, label, body, slug|None} + seq = [] titles: dict[int, str] = {} slugs: dict[int, str] = {} - - # --- per-module pages ------------------------------------------------- for num, slug, path in mods: body = (path / "README.md").read_text(encoding="utf-8") h1 = first_h1(body, f"Module {num}") titles[num] = h1 slugs[num] = slug - source_path = f"modules/{slug}/README.md" - body = rewrite_lab_links(body, args.web_base, args.branch, args.host, slug) - body = rewrite_repo_file_links(body, args.web_base, args.branch, args.host) - page = ( - banner(source_path, src_url(args.web_base, args.branch, args.host, source_path)) - + "\n" - + body - + "\n" - ) - (out / f"{slug}.md").write_text(page, encoding="utf-8") + seq.append({"page": slug, "label": h1, "body": body, "slug": slug}) - # --- capstone --------------------------------------------------------- cap = repo_root / "capstone" / "README.md" cap_title = None if cap.exists(): body = cap.read_text(encoding="utf-8") cap_title = first_h1(body, "Capstone — The Full Loop") - source_path = "capstone/README.md" - # capstone labs may reference modules/.. ; only lab/ links are rewritten elsewhere, - # capstone has none, so copy through with a banner. - body = rewrite_repo_file_links(body, args.web_base, args.branch, args.host) - page = ( - banner(source_path, src_url(args.web_base, args.branch, args.host, source_path)) - + "\n" - + body - + "\n" - ) - (out / "capstone.md").write_text(page, encoding="utf-8") + seq.append({"page": "capstone", "label": cap_title, "body": body, "slug": None}) + + # --- pass 2: write each page with banner + prev/next nav -------------- + for i, entry in enumerate(seq): + prev = (seq[i - 1]["page"], seq[i - 1]["label"]) if i > 0 else None + nxt = (seq[i + 1]["page"], seq[i + 1]["label"]) if i < len(seq) - 1 else None + + if entry["slug"] is not None: + source_path = f"modules/{entry['slug']}/README.md" + body = rewrite_lab_links(entry["body"], args.web_base, args.branch, args.host, entry["slug"]) + body = rewrite_repo_file_links(body, args.web_base, args.branch, args.host) + else: + source_path = "capstone/README.md" + body = rewrite_repo_file_links(entry["body"], args.web_base, args.branch, args.host) + + parts = [banner(source_path, src_url(args.web_base, args.branch, args.host, source_path))] + tn = top_nav(prev) + if tn: + parts.append("\n" + tn) + parts.append("\n" + body) + bn = bottom_nav(nxt) + if bn: + parts.append("\n---\n\n" + bn) + (out / f"{entry['page']}.md").write_text("\n".join(parts) + "\n", encoding="utf-8") # --- _Sidebar.md (textbook nav) -------------------------------------- sb = ["### [📖 Home](Home)\n"] @@ -205,9 +223,10 @@ def main() -> int: encoding="utf-8", ) - pages = len(mods) + (1 if cap_title else 0) + 3 + pages = len(seq) + 3 print(f"wrote {pages} wiki files to {out} ({len(mods)} modules" - f"{' + capstone' if cap_title else ''} + Home/_Sidebar/_Footer)", flush=True) + f"{' + capstone' if cap_title else ''} + Home/_Sidebar/_Footer, with prev/next nav)", + flush=True) return 0