Menu ▾ ▴

#7 guide: HTML/PDF heading shows last step's title (loop variable shadows `title` in export_html._html)

open
nobody
None
6 days ago
6 days ago
Anonymous
No

Originally created by: Pranav-Bobde

Reproduced with ainotate 0.1.8, installed via uv tool install "ainotate[all]", on Python 3.12.

The HTML guide heading and document title use the last step's title instead of the guide title. Markdown keeps the correct guide title.

Minimal repro

Create two tiny images using a Python interpreter with Pillow installed:

mkdir -p /tmp/ainotate-repro
cd /tmp/ainotate-repro
python - <<'PY'
from PIL import Image
for name in ("first.png", "last.png"):
    Image.new("RGB", (16, 16), "white").save(name)
PY

Save this as steps.json in that directory:

{
  "title": "My Guide",
  "intro": "Intro with **bold**.\n- Intro item",
  "steps": [
    {"image": "first.png", "title": "First", "text": "Step with **bold**.\n- Step item"},
    {"image": "last.png", "title": "Last", "text": "Done."}
  ]
}

Run:

ainotate guide steps.json --formats md,html --out-dir /tmp/ainotate-repro

Expected: My Guide.html contains <h1>My Guide</h1> and <title>My Guide</title>.

Actual: it contains <h1>Last</h1> and <title>Last</title>. My Guide.md correctly starts with # My Guide.

Root cause

In src/ainotate/export_html.py, _html reassigns its title parameter inside the step loop. Exact lines in 0.1.8, also present on current main:

100: def _html(title, intro, steps) -> str:
...
102:     for i, s in enumerate(steps, 1):
...
105:         title = _inline(s.get("title", ""))
106:         if title:
107:             head_html = f'<span class="t">{title}</span>'
...
115:     head = f"<h1>{html.escape(title)}</h1>" if title else ""
...
119:             f'<title>{html.escape(title or "Guide")}</title><style>{_CSS}</style></head>'

Suggested fix: rename the loop-local title to step_title, including its uses on lines 106–107, preserving the guide's title parameter.

Source inspection also shows the Playwright PDF path prints this same HTML (export.py:225–237), so it inherits the heading bug. This repro checks Markdown and HTML; the Pillow PDF fallback uses a separate renderer.

The same repro leaves the intro as literal **bold** and - Intro item, while the step text produces <strong> and <ul> markup. The Markdown output preserves the intro's Markdown syntax for Markdown renderers.

Line 116 only escapes the intro and replaces newlines:

116:     lead = f'<p class="intro">{html.escape(intro).replace(chr(10), "<br>")}</p>' if intro else ""

Step text uses _rich() on line 108. Applying equivalent formatting to the intro, with a container suitable for paragraphs/lists, would make the outputs consistent.

Discussion


Log in to post a comment.