| 587 | |
| 588 | |
| 589 | def _write_diff_index( |
| 590 | report_dir: Path, |
| 591 | *, |
| 592 | baseline: dict[str, Any], |
| 593 | current: dict[str, Any], |
| 594 | cards: list[dict[str, Any]], |
| 595 | ) -> None: |
| 596 | cards_html: list[str] = [] |
| 597 | options_html: list[str] = [] |
| 598 | for index, card in enumerate(cards): |
| 599 | reasons = ( |
| 600 | ", ".join(html_escape(r) for r in card["reasons"]) or "Included by --all" |
| 601 | ) |
| 602 | metric_items = [ |
| 603 | html_escape(line.removeprefix("- `").replace("`: ", ": ")) |
| 604 | for line in card["metric_lines"] |
| 605 | ] |
| 606 | metrics = ( |
| 607 | "".join(f"<li>{m}</li>" for m in metric_items) |
| 608 | or "<li>No tracked metric deltas</li>" |
| 609 | ) |
| 610 | card_id = f"card-{card['stem']}" |
| 611 | active_attr = " active" if index == 0 else "" |
| 612 | options_html.append( |
| 613 | f'<option value="{html_escape(card_id)}">' |
| 614 | f"{html_escape(card['path'])}</option>" |
| 615 | ) |
| 616 | cards_html.append( |
| 617 | f""" |
| 618 | <section id=\"{html_escape(card_id)}\" class=\"card{active_attr}\"> |
| 619 | <h2>{html_escape(card["path"])}</h2> |
| 620 | <div class=\"meta\"> |
| 621 | <p><b>Why flagged:</b> {reasons}</p> |
| 622 | <details><summary>Metric deltas</summary><ul>{metrics}</ul></details> |
| 623 | </div> |
| 624 | <nav class=\"tabs\" role=\"tablist\"> |
| 625 | <button data-mode=\"slider\" aria-selected=\"true\">Slider</button> |
| 626 | <button data-mode=\"side-by-side\">Side by side</button> |
| 627 | <button data-mode=\"actual\">Actual</button> |
| 628 | <button data-mode=\"expected\">Expected</button> |
| 629 | </nav> |
| 630 | <div class=\"view view-slider active\"> |
| 631 | <img-comparison-slider hover=\"false\" tabindex=\"0\"> |
| 632 | <img slot=\"first\" src=\"{html_escape(card["expected_png"])}\" alt=\"Expected screenshot\"> |
| 633 | <img slot=\"second\" src=\"{html_escape(card["actual_png"])}\" alt=\"Actual screenshot\"> |
| 634 | </img-comparison-slider> |
| 635 | </div> |
| 636 | <div class=\"view view-side-by-side\"> |
| 637 | <div><div class=\"side-label\">expected</div><img src=\"{html_escape(card["expected_png"])}\" alt=\"Expected screenshot\"></div> |
| 638 | <div><div class=\"side-label\">actual</div><img src=\"{html_escape(card["actual_png"])}\" alt=\"Actual screenshot\"></div> |
| 639 | </div> |
| 640 | <div class=\"view view-actual\"><img src=\"{html_escape(card["actual_png"])}\" alt=\"Actual screenshot\"></div> |
| 641 | <div class=\"view view-expected\"><img src=\"{html_escape(card["expected_png"])}\" alt=\"Expected screenshot\"></div> |
| 642 | <p class=\"links\"> |
| 643 | <a href=\"{html_escape(card["expected_html"])}\">expected HTML</a> |
| 644 | <a href=\"{html_escape(card["actual_html"])}\">actual HTML</a> |
| 645 | <a href=\"{html_escape(card["expected_md"])}\">expected markdown</a> |
| 646 | <a href=\"{html_escape(card["actual_md"])}\">actual markdown</a> |