import sys, pathlib, difflib, json, bs4
src_html = pathlib.Path(sys.argv[1]).read_text(encoding="utf-8")
out_dir = pathlib.Path(sys.argv[2])
out_dir.mkdir(parents=True, exist_ok=True)
soup = bs4.BeautifulSoup(src_html, "html.parser")
# 1) select elements to be revealed frame by frame
# here we use all tags inside .box as an example, you can filter by yourself:
targets = []
for box in soup.select("div.box"):
for node in box.descendants:
if isinstance(node, bs4.Tag):
# you can filter out wrapper / script / style etc. as needed
if node.name in {"script", "style"}:
continue
targets.append(node)
total = len(targets)
print(f"Total nodes to be revealed: {total}")
manifest, prev_lines = [], []
for idx, node in enumerate(targets, 1):
step_soup = bs4.BeautifulSoup(src_html, "html.parser")
# second pass: hide elements that are not yet shown
shown_ids = set() # use id() to identify same node
for j, n in enumerate(
step_soup.select("div.box *") # * = all descendants
):
if isinstance(n, bs4.Tag) and id(n) in shown_ids:
# already processed (prevent duplicate)
continue
if j + 1 > idx: # not yet shown elements
style = n.get("style", "")
# ensure no duplicate style
if "opacity" not in style:
style = (
"opacity:0;transition:opacity .35s ease-out;" + style
)
n["style"] = style
shown_ids.add(id(n))
# 3) write file
fname = f"{idx:04d}.html"
(out_dir / fname).write_text(str(step_soup), encoding="utf-8")
diff = "" if not prev_lines else "\n".join(
difflib.unified_diff(prev_lines, str(step_soup).splitlines(), lineterm="")
)
manifest.append(
{"file": fname, "caption": f"Elements: {idx}/{total}", "diff": diff}
)
prev_lines = str(step_soup).splitlines()
# 4) write manifest
(out_dir / "manifest.json").write_text(
json.dumps(manifest, ensure_ascii=False, indent=2)
)
print("Finished", out_dir.resolve())