intentumdiff-perceptual-asset-diff
The IntentumDiff perceptual image/asset diff — how binary assets are compared and visualised. Use this whenever you work on image diffing, the asset viewer, or the interactive comparison UX (side-by-s
SKILL.mdskills/intentumdiff-perceptual-asset-diff/SKILL.md
GitHubIntentumDiff — Perceptual asset diff
For images (and, on the roadmap, other binary media), IntentumDiff shows a perceptual comparison instead of a text diff. The hard architectural rule: all decode/compute happens in the Rust engine; the webview does no image processing — it renders engine-produced PNG artifacts and draws lightweight SVG/CSS overlays on top.
Engine side (crates/rust-core-host/src/asset_diff.rs)
diff_asset_image_value (and the git-driven diff_git_assets) produce, for a before/after
pair, all of:
- Metrics: MAE, RMSE, pixels-changed.
- Layers/artifacts (PNG):
before,after,diff,mask,overlay,heatmap. hotspots: changed regions with pixelbbox, normalizedcentroid, andseverity; optionalhotspot_navigation.order.histograms: 16-bin per-channel (R/G/B/brightness, +alpha) delta arrays.comparison_dimensions:{ width, height }— the reference frame for normalizing hotspot bboxes into the viewer.
Decode cost is bounded (max_decoded_pixels) so large/adversarial media fails fast. Never send
media to a cloud LLM — asset diff is fully on-device.
Webview side (plugins/vscode/src/reviewWebviewModel.ts)
assetModeViewer(...) renders four modes and the overlays. All are pure DOM/CSS/SVG:
- Side-by-side / Onion (opacity + blink) / Swipe (curtain) / Difference (heatmap).
- Lasso (marching ants): an SVG
rectper hotspot with animatedstroke-dashoffset, drawn over both panes so the same change is locatable on LHS and RHS. Rectangles come fromhotspot.bboxnormalized bycomparison_dimensions(left=x/W, top=y/H, …). - Blink comparator:
setIntervaltoggling the onion top layer opacity 0↔1. - Interactive hotspots: list items + on-image markers are clickable; selection pulses the
lasso, dims others; prev/next + arrow keys cycle; uses
hotspot_navigation.orderwhen present. - Histograms: per-channel SVG bar charts from the delta arrays (theme-tinted).
Pure helpers to reuse: assetLasso(hotspots, compDims, options), assetHistogramBars(histograms),
assetHotspot, assetMarker. Client interactions follow the existing setAssetMode /
setAssetOpacity delegation pattern (no round-trip to the engine).
Threading gotcha: comparison_dimensions must be parsed in assetDiffFromMetadata and
threaded from the call site into assetModeViewer — the model historically dropped it, which
breaks overlay alignment. Wrap each <img> in an inline-block box sized to the image so
inset:0 overlays align over object-fit:contain.
Routing & lifecycle
Image-extension files route to asset review instead of the text semantic diff
(isImageLikePath, and the engine's content_type category). Clicking an image's Evidence
must open the asset viewer, not an intentumdiff-base: text diff.
Performance
The perceptual pipeline is compute-heavy (PNG deflate encoding + per-pixel passes). It is
~20–50× slower on a debug core — always build with maturin develop --release (a 1050×700
image drops from ~6 s to ~0.3 s). The VS Code compare is also non-blocking: the preview shows
instantly and upgrades when the engine result is ready. See intentumdiff-architecture (build)
and docs/PERCEPTUAL_ASSET_DIFF.md.
Roadmap: video / audio / other binary (docs/BACKLOG.md)
Extend the pattern by content-type routing, keeping all decode/compute in Rust and on-device:
- Video: sample keyframes, run the per-frame image pipeline, add a timeline scrubber with changed-frame markers (needs a vetted ffmpeg-backed decode dependency).
- Audio: waveform + spectrogram comparison (prefer pure-Rust
symphonia+rustfft). - Other binary: a routed fallback (size/format delta) for formats with no perceptual
comparator. Bound decode cost like the image
max_decoded_pixelscap.
Tests / verify
test/reviewWebviewModel.test.ts asserts the Swipe tab, blink controls, lasso <svg> with
per-hotspot data-asset-hotspot, histogram <svg> bars, and that the panel script executes.
For interaction, use the panel-render harness (see intentumdiff-vscode) with a real image
injected as every artifact, served for the Claude Preview MCP; resize narrow (~760px) to
confirm no overflow regression. Rust side: cargo test -p rust-core-host.