From 93be2b9b6af6d6eb6167e205dd8da8dcf141ef83 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 26 Aug 2026 22:42:09 -0400 Subject: [PATCH] scripts --- archive/threads/index.md | 28 ++++++++++++++-------------- scripts/twitter/emit_archive.py | 24 +++++++++++++++++++++++- scripts/twitter/tw.py | 8 ++++++++ 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/archive/threads/index.md b/archive/threads/index.md index 0b0337f..c057981 100644 --- a/archive/threads/index.md +++ b/archive/threads/index.md @@ -14,38 +14,38 @@ How to read a folder: [[notes/How threads are organized]]. Example of the note shape (single spine, no branches): [[archive/threads/How to archive a thread]] - [[archive/threads/AgileJebrim]] +- [[archive/threads/bmcnett]] - [[archive/threads/BOENSAW]] - [[archive/threads/CharlieMQV]] - [[archive/threads/ChipsandCheese9]] +- [[archive/threads/cmuratori]] - [[archive/threads/Dispatch_Graph]] - [[archive/threads/FFmpeg]] -- [[archive/threads/JohannesVuorine]] -- [[archive/threads/Jonathan_Blow]] -- [[archive/threads/KostasAAA]] -- [[archive/threads/MissQuickstep]] -- [[archive/threads/MyNameIsMJP]] -- [[archive/threads/NOTimothyLottes]] -- [[archive/threads/SebAaltonen]] -- [[archive/threads/SheriefFYI]] -- [[archive/threads/TheGingerBill]] -- [[archive/threads/Triang3l]] -- [[archive/threads/VPCOMPRESSB]] -- [[archive/threads/VictorTaelin]] -- [[archive/threads/bmcnett]] -- [[archive/threads/cmuratori]] - [[archive/threads/hsn8086]] - [[archive/threads/iquilezles]] +- [[archive/threads/JohannesVuorine]] +- [[archive/threads/Jonathan_Blow]] - [[archive/threads/kechogarcia]] - [[archive/threads/kenpex]] +- [[archive/threads/KostasAAA]] - [[archive/threads/levelsio]] - [[archive/threads/m_schuetz]] - [[archive/threads/mike_acton]] +- [[archive/threads/MissQuickstep]] - [[archive/threads/mmalex]] +- [[archive/threads/MyNameIsMJP]] - [[archive/threads/nicbarkeragain]] +- [[archive/threads/NOTimothyLottes]] - [[archive/threads/rfleury]] - [[archive/threads/rianflo]] +- [[archive/threads/SebAaltonen]] +- [[archive/threads/SheriefFYI]] - [[archive/threads/simplex_fx]] +- [[archive/threads/TheGingerBill]] - [[archive/threads/tom_forsyth]] +- [[archive/threads/Triang3l]] - [[archive/threads/valigo]] +- [[archive/threads/VictorTaelin]] - [[archive/threads/vkrajacic]] +- [[archive/threads/VPCOMPRESSB]] - [[archive/threads/winning_tactic]] diff --git a/scripts/twitter/emit_archive.py b/scripts/twitter/emit_archive.py index ad03f84..7495d3c 100644 --- a/scripts/twitter/emit_archive.py +++ b/scripts/twitter/emit_archive.py @@ -488,7 +488,7 @@ def _upsert_wikilink(path: Path, target: str) -> None: kept.append(line) if target not in names: names.append(target) - names.sort() + names.sort(key=str.lower) while kept and kept[0] == "": kept.pop(0) while kept and kept[-1] == "": @@ -657,6 +657,28 @@ def ensure_threads_index(vault: Path, handle: str) -> None: _upsert_wikilink(path, f"archive/threads/{handle}") +def rewrite_threads_index(vault: Path) -> bool: + """Re-sort the top-level archive/threads/index.md wikilinks alphabetically (case-insensitive). + + Preserves frontmatter and prose. Idempotent. No-op if the file is missing + or if no real handle dirs exist. + """ + path = vault / "archive" / "threads" / "index.md" + if not path.is_file(): + return False + threads_root = path.parent + real_handles = sorted( + (d.name for d in threads_root.iterdir() if d.is_dir() and not d.name.startswith(".")), + key=str.lower, + ) + if not real_handles: + return False + # Pick the first real handle as a no-op upsert target so the existing + # _upsert_wikilink re-sort path runs. + _upsert_wikilink(path, f"archive/threads/{real_handles[0]}") + return True + + def discover_dumps(root: Path) -> list[Path]: """Return the sorted list of dump subdirectories that contain ``thread_data.json``.""" dumps = [ diff --git a/scripts/twitter/tw.py b/scripts/twitter/tw.py index 4fa5e5d..9e49f34 100644 --- a/scripts/twitter/tw.py +++ b/scripts/twitter/tw.py @@ -48,6 +48,7 @@ try: format_reslug_plan, plan_relabel, plan_reslug, + rewrite_threads_index, ) from fallback_media import activate_fallback, find_existing_fallback, restore_origin from media_audit import audit_thread @@ -63,6 +64,7 @@ except ImportError: # pragma: no cover - script-mode import format_reslug_plan, plan_relabel, plan_reslug, + rewrite_threads_index, ) from fallback_media import activate_fallback, find_existing_fallback, restore_origin from media_audit import audit_thread @@ -755,6 +757,12 @@ def cmd_sync(args: argparse.Namespace) -> int: print(f"rewrote {handle_dir.name}: {len(dirs)} dirs") else: print(f"ok {handle_dir.name}: {len(dirs)} dirs") + # Re-sort the top-level handle list whenever sync runs without a target + # handle. Per-handle indexes (above) are sorted in-place; the top-level + # needs a separate call because its bullet list is not a per-handle file. + if not handle: + if rewrite_threads_index(VAULT): + print("rewrote top-level threads index") print(f"changed {changed}, skipped {skipped}") return 0