Repository navigation
tools: structural + visual diff for the SVG figure renders - #82
Merged
Merged
Conversation
The svg/ renders are auto-laid-out, so adding a single node reflows the whole page. A plain pixel diff of the render is therefore unreadable for review: on PR #81, which splices one decision diamond into two figures, a rendered image diff reports ~55 changed regions, the largest spanning half the diagram, because nearly every edge shifts by a few pixels. tools/svgdiff/svg_diff.py diffs the structure instead. The renderer emits each node as a <g transform="translate(..)"> wrapping one shape, so a node can be given a position-independent signature (shape + dimensions + label), an edge a signature of (source label -> target label), and a free label its text. Diffing those as multisets makes layout movement cancel out, leaving only genuinely added or removed content. The same PR reduces to seven findings that read as a sentence: + node V(r) < N(s) < V(r) + k? + edge N(s) == V(r)? -> V(r) < N(s) < V(r) + k? + edge V(r) < N(s) < V(r) + k? -> Discard Contents of I Frame - edge N(s) == V(r)? -> Reject Exception? Alongside the change list it writes a self-contained HTML viewer per figure (both renders embedded as data URIs; no network, no dependencies) with new/old/swipe/onion-skin/blink/difference views and a clickable change list that zooms to each change, flipping to the old render for removed content. The viewer also computes a raw pixel diff in-browser, kept behind a Structural/Pixel toggle for the cases where a purely cosmetic render change needs checking. --all walks every SVG differing between two revisions, --merge-base compares a branch against its fork point, and --format markdown emits the change list as a table suitable for posting as a PR comment. Viewers are written to the now-gitignored build/svgdiff/. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0188JcqNg5JDQqdsBt7LrbyE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reviewing a figure change by eye is currently impractical. The
svg/renders are auto-laid-out, so adding a single node reflows the whole page — on #81, which splices one decision diamond into two figures, a rendered image diff reports ~55 changed regions, the largest spanning half the diagram, because nearly every edge shifts by a few pixels. Off-the-shelf image-diff tools (ImageMagickcompare,pixelmatch, GitHub's own swipe/onion-skin) all hit the same wall.tools/svgdiff/svg_diff.pydiffs the structure instead. The renderer emits each node as a<g transform="translate(..)">wrapping one shape, so a node can be given a position-independent signature (shape + dimensions + label), an edge a signature of (source label → target label), and a free label its text. Diffing those as multisets makes layout movement cancel out, leaving only genuinely added or removed content.Run against #81, the same change reduces to seven findings that read as a sentence — the
N(s) == V(r)?No branch now goes through a window check, whose No side discards the frame:What's in it
svg_struct.py— parses a render into nodes / edges / free labels and diffs the signatures. Node counts come out as a sanity check (181→182, edges 209→211).svg_diff.py— the CLI.viewer_template.html— a self-contained HTML viewer, written per figure with both renders embedded as data URIs. No network, no dependencies, opens straight from disk. New / Old / Swipe / Onion-skin / Blink / Difference views, plus a clickable change list down the side that zooms to each change and flips to the old render for removed content. It also computes a raw pixel diff in-browser, kept behind a Structural ⇄ Pixel toggle for the cases where a purely cosmetic render change needs checking.Usage
Viewers land in the now-gitignored
build/svgdiff/. Stdlib Python only — no new CI dependency, and--format markdown --no-htmlis ready to drop into a workflow that posts the change list on any PR touchingspec-sdl/**/svg/. I have not added that workflow here; happy to follow up with it if you want it.Verification
Exercised against #81 (both figures), a pure-reflow case, an added-file case, and a no-change case; the generated viewers were rendered in headless Chrome to confirm the change list, the zoom-to-change navigation and the old/new flip all behave.
🤖 Generated with Claude Code
https://claude.ai/code/session_0188JcqNg5JDQqdsBt7LrbyE