Add prev/next textbook nav to wiki pages (#77)
Sync course wiki / sync-wiki (push) Successful in 4s
Sync course wiki / sync-wiki (push) Successful in 4s
Co-authored-by: claude <claude@jpaul.io> Co-committed-by: claude <claude@jpaul.io>
This commit was merged in pull request #77.
This commit is contained in:
+46
-27
@@ -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 <wiki-dir> \
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user