snapdrift 0.16.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +2 -2
- package/action.yml +2 -2
- package/docs/contracts.md +31 -12
- package/docs/integration-guide.md +12 -11
- package/docs/workflow-templates/refresh-baseline-on-merge.yml +1 -1
- package/package.json +3 -3
- package/types/visual-diff-types.d.ts +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.17.0 - 2026-10-28
|
|
6
|
+
|
|
7
|
+
Workspace releases this version:
|
|
8
|
+
|
|
9
|
+
- `@snapdrift/compare-core` 1.8.0 — policy v2 offset-run alignment suppresses mobile rendering noise: structural padding is skipped, compensating duplicate bands are recognized, unresolved shift-boundary bands split into ink plus padding, localized glyph/badge displacement requires background and foreground evidence, a stable row keeps its own offset unless its own cost exceeds `LOCAL_MISMATCH_COST`, and the offset scoring budget admits a 1170×17187 pair. Ambiguous exact alignment tries the offset mapping before coordinate fallback.
|
|
10
|
+
- `@snapdrift/adapter-fs` 1.7.2 — now depends on `@snapdrift/compare-core ^1.8.0` for the released comparison engine.
|
|
11
|
+
|
|
12
|
+
`@snapdrift/manifest` stays 1.8.0 and `@snapdrift/adapter-report-md` stays 1.4.0. Root floors move to `@snapdrift/compare-core ^1.8.0` and `@snapdrift/adapter-fs ^1.7.2`; the previous `adapter-fs` 1.7.1 and `compare-core` 1.7.0 do not satisfy them, so installing `snapdrift` 0.17.0 always gets the released engine. The dispatcher pins `2d93a54`, the #212 merge commit on `main`, which includes the newest commit touching `actions/` (`1cbf629`, the shell-only Playwright install).
|
|
13
|
+
|
|
14
|
+
### Fixes
|
|
15
|
+
|
|
16
|
+
- `@snapdrift/compare-core`: policy v2 (`alignment: 'vertical'`) no longer falls back to coordinate comparison with `fallbackReason: 'ambiguous'` just because a page grew by rows that match repeated card padding or background rows elsewhere in the image. An unmatched row now counts as ambiguous only when it occurs fewer than `MIN_STRUCTURAL_ROW_REPEATS` (16, newly exported) times on either side. On a real 1440x3386 page screenshot pair where one card grew by 22 rows, this took the reported drift from 10.83% in coordinate fallback to 1.43% aligned.
|
|
17
|
+
- `@snapdrift/compare-core`: offset-run content intervals now report a row-weighted `meanCost` when adjacent ±1 runs merge, instead of averaging the two run means. This is diagnostic output only; row plans and metrics are unchanged.
|
|
18
|
+
- `@snapdrift/compare-core`: the exact row alignment no longer wraps the Myers search in a bare `catch`. An exhausted edit budget still falls back as `alignment-limit`; an unexpected error now surfaces instead of being reported as that fallback.
|
|
19
|
+
- `@snapdrift/compare-core`: policy v2 no longer throws `comparison_too_large` when the exact Myers row alignment produces an output canvas above `maxPixels`. The exact and offset-run alignment paths now agree: an over-budget aligned canvas falls back to coordinate comparison with `fallbackReason: 'alignment-limit'`, so a pair v1 compares on a fitting union canvas completes under v2 (repro: baseline `[A,X,B,C]` vs current `[A,B,C,Y]` at width 1 and `maxPixels` 4). The coordinate union guard still throws `comparison_too_large`. Closes #199.
|
|
20
|
+
- `@snapdrift/compare-core`: policy v2 vertical alignment no longer paints mobile rendering noise as content. Offset-run plans skip structural padding (a one-sided row needs `CONTENT_INK_MIN`, 16, full-width ink pixels), treat a one-sided content row that duplicates a claimed row within `STRUCTURAL_DUPLICATE_COST` 0.05 at an offset within `STRUCTURAL_DUPLICATE_WINDOW` (3) of the neighboring stable runs as a compensating band, and split an unresolved shift-boundary band into changed-ink rows plus padding instead of deleting or painting the whole band. Inside a paired row, fewer than 16 mismatched pixels are a localized glyph or badge displacement only when every mismatch carries both background evidence (the old baseline pixel exists in a claimed current row) and foreground evidence (the new current pixel exists in a claimed baseline row) at one common non-zero offset within ±3; inserted current rows and deleted baseline rows count as claimed rows on their respective sides. A mismatch with only one side's duplicate, such as a new small mark on uniform background, stays orange as content. A displaced run bordering unexplained mismatched content or content-carrying one-sided rows stays painted as part of that edit. A stable row refines to a ±1 neighbor only when its own recorded-offset cost exceeds `LOCAL_MISMATCH_COST` 0.5; a smaller-residual row keeps its own offset so its nominal current row is compared instead of being consumed without comparison. On the mobile capture pair in Snap visual run `run_6c287da7aef84a94bd663c01` (1170×17187 baseline vs 1170×17268 current), this took the reported mobile diff from `0.18773458736975068` (3,792,907 / 20,203,560 px) in coordinate fallback to `0.0036852305076227753` (74,942 / 20,335,770 px) with `alignmentStrategy: "offset"`. The 74,942 px are the real paragraph reflow (58,557 orange changed + 16,380 green inserted); five residual rendering-noise pixels at output rows 8720..8734 remain, whose mismatches find no claimed duplicate. The desktop diff stayed `0.0019953484937979917` (9,729 / 4,875,840 px), so both routes now sit below the configured 1% threshold.
|
|
21
|
+
- `@snapdrift/compare-core`: an exact-alignment `ambiguous` result now tries the offset-run mapping before falling back to v1 coordinate comparison, so a page that grew by rows matching repeated card padding can align instead of taking a whole-page coordinate diff. Three offset-run boundary cases are fixed: the Viterbi incumbent is free only when its own row cost is zero (a row matching several offsets with ink residue still votes to leave), an unresolved row keeps its best in-window offset as evidence and only a ±1 neighbor refines to the full window when the row's own cost exceeds `LOCAL_MISMATCH_COST` 0.5, and a pair or evidence offset is accepted only within `STRUCTURAL_DUPLICATE_WINDOW` (3) of the neighboring stable runs; a final run overflowing the current height trims to a deletion instead of abandoning the mapping. The offset scoring budget rises to `MAX_OFFSET_SCORE_VISITS` 2.5e9 so a 1170×17187 pair (~2.02e9 visits) scores instead of falling back; the 1440×3386 desktop pair stays ~0.49e9.
|
|
22
|
+
|
|
23
|
+
### Performance
|
|
24
|
+
|
|
25
|
+
- `@snapdrift/compare-core`: policy v2 metrics-only comparisons (`renderDiffImage: false`) skip the per-pixel loop for byte-identical matched row runs and count them straight into the denominator.
|
|
26
|
+
- `@snapdrift/compare-core`: offset-run analysis caches the pass-1 full-width `hotRuns` scan for each stable row and reuses it when summarizing content intervals, instead of scanning every stable row a second time. A row whose offset changes after gap bridging or sliver absorption is scanned once at its final offset. Interval `highlightRows`/`structuredRows`/`meanCost`, row plans, and metrics are unchanged. Measured on the insertion-shift fixture (1440×5622 baseline, 1440×5644 current) in plain Node, best of 3 alternating runs: `analyzeOffsetRuns` 2518.1 ms -> 2500.3 ms (-0.7%); `compareImages(renderDiffImage: false)` 2924.3 ms -> 2862.7 ms (-2.1%).
|
|
27
|
+
- `@snapdrift/compare-core`: exact row alignment now fingerprints rows with two independent 32-bit FNV-style accumulators over `Uint32Array` word views (byte hashing when the view cannot be built) packed into one combined 53-bit JS safe integer (`high * 2^32 + low`) instead of Node SHA-1 hex digests. A plain 32-bit value over ~16k rows has a few-percent birthday collision chance (~1.6% at 16,384 rows over 2^32); the packed 53-bit value shrinks that to roughly 7.5e-9, and `rowsEqual` still byte-verifies every Myers match, so a collision still falls back as `verification-failed`. No BigInt is used. Best of 3 in plain Node outside Jest: the insertion-shift fixture (1440x5622) fingerprint-only 58.4ms -> 53.2ms (-8.9%) and alignRows 168.2ms -> 150.9ms (-10.3%); a tall synthetic 1440x8000 pair with one inserted row fingerprint-only 64.6ms -> 51.8ms (-19.8%), alignRows 64.7ms -> 51.9ms (-19.7%), and full metrics-only `compareImages` 402.9ms -> 386.3ms (-4.1%). Row mappings and metrics are identical before and after; the fixture's full comparison is dominated by the unchanged offset-run path, so its metrics-only time stayed ~2890ms.
|
|
28
|
+
|
|
29
|
+
### Features
|
|
30
|
+
|
|
31
|
+
- `@snapdrift/compare-core`: policy v2 aligned comparison metadata now identifies the row-alignment strategy (`alignmentStrategy: "exact" | "offset"`) and records the stable offset-run per-channel tolerance (`tolerance: 32`). Local-mismatch residual bands use a stricter `NOISE_DELTA` floor (2) and are not covered by `tolerance`. `mode` remains `"vertical-aligned"`; a coordinate fallback carries neither field. Additive metadata so consumers can tell byte-identical exact matches from tolerant stable-run matches and know residual bands are stricter. Closes #200.
|
|
32
|
+
|
|
33
|
+
### Refactors
|
|
34
|
+
|
|
35
|
+
- `@snapdrift/compare-core`: `compareBuffers`, `compareWithIgnoreRegions`, and `generateDiffImage` now delegate to one internal strict decoded-PNG pixel loop instead of keeping three copies of RGBA equality, ignore-mask handling, and diff painting. Public signatures, dimension-mismatch errors, result shapes, byte-equality and re-encode behavior, ignore gray, highlight color, and the hidden-RGB semantics are unchanged. Closes #203.
|
|
36
|
+
- `@snapdrift/compare-core`: `offset-align` exports `rowInkCost`, `rowHasAnyInk`, and `rowHasContentInk` plus the new `CONTENT_INK_MIN`, `STRUCTURAL_DUPLICATE_COST`, and `STRUCTURAL_DUPLICATE_WINDOW` constants so the render plan and painter share one ink and duplicate definition. These are module-level exports; the package index is unchanged.
|
|
37
|
+
|
|
5
38
|
## 0.16.1 - 2026-10-27
|
|
6
39
|
|
|
7
40
|
Workspace releases this version:
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ You keep ownership of checkout, build, startup, readiness, and teardown. SnapDri
|
|
|
48
48
|
|
|
49
49
|
```yaml
|
|
50
50
|
- name: SnapDrift Baseline
|
|
51
|
-
uses: ranacseruet/snapdrift@v0.
|
|
51
|
+
uses: ranacseruet/snapdrift@v0.17.0
|
|
52
52
|
with:
|
|
53
53
|
mode: baseline
|
|
54
54
|
repo-config-path: .github/snapdrift.json
|
|
@@ -60,7 +60,7 @@ With `provider: "snap"`, baseline publication is a complete, fail-closed snapsho
|
|
|
60
60
|
|
|
61
61
|
```yaml
|
|
62
62
|
- name: SnapDrift Report
|
|
63
|
-
uses: ranacseruet/snapdrift@v0.
|
|
63
|
+
uses: ranacseruet/snapdrift@v0.17.0
|
|
64
64
|
with:
|
|
65
65
|
mode: pr-diff
|
|
66
66
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
package/action.yml
CHANGED
|
@@ -155,7 +155,7 @@ runs:
|
|
|
155
155
|
- name: SnapDrift baseline
|
|
156
156
|
id: baseline
|
|
157
157
|
if: inputs.mode == 'baseline'
|
|
158
|
-
uses: ranacseruet/snapdrift/actions/baseline@
|
|
158
|
+
uses: ranacseruet/snapdrift/actions/baseline@2d93a5499daf6e0273ed11da69b364d33dc8d4d2 # v0.17.0
|
|
159
159
|
with:
|
|
160
160
|
repo-config-path: ${{ inputs.repo-config-path }}
|
|
161
161
|
route-ids: ${{ inputs.route-ids }}
|
|
@@ -165,7 +165,7 @@ runs:
|
|
|
165
165
|
- name: SnapDrift pull request diff
|
|
166
166
|
id: pr-diff
|
|
167
167
|
if: inputs.mode == 'pr-diff'
|
|
168
|
-
uses: ranacseruet/snapdrift/actions/pr-diff@
|
|
168
|
+
uses: ranacseruet/snapdrift/actions/pr-diff@2d93a5499daf6e0273ed11da69b364d33dc8d4d2 # v0.17.0
|
|
169
169
|
with:
|
|
170
170
|
github-token: ${{ inputs.github-token }}
|
|
171
171
|
repo-config-path: ${{ inputs.repo-config-path }}
|
package/docs/contracts.md
CHANGED
|
@@ -293,17 +293,24 @@ objects, `dimensionsChanged`, and the effective `totalPixels` denominator. A
|
|
|
293
293
|
local changed item with a generated image contains `diffImagePath`, relative to
|
|
294
294
|
the diff output bundle (for example `diffs/home.png`).
|
|
295
295
|
|
|
296
|
-
Policy v2 comparison metadata additionally includes `policyVersion: 2
|
|
297
|
-
`mode
|
|
298
|
-
`rowMapping` describing matched,
|
|
299
|
-
|
|
300
|
-
|
|
296
|
+
Policy v2 comparison metadata additionally includes `policyVersion: 2`,
|
|
297
|
+
`mode`, and, for aligned results, `alignmentStrategy`. An aligned result has
|
|
298
|
+
`mode: "vertical-aligned"` and a compact `rowMapping` describing matched,
|
|
299
|
+
changed, inserted, and deleted row runs. `alignmentStrategy: "exact"` records
|
|
300
|
+
the Myers fingerprint path: matched rows are byte-identical and `tolerance` is
|
|
301
|
+
absent. `alignmentStrategy: "offset"` records the piecewise vertical-offset
|
|
302
|
+
fallback: `tolerance` is the maximum per-channel delta that stays unhighlighted
|
|
303
|
+
on stable offset-run rows (`STRUCTURED_DELTA`, 32). Local-mismatch residual
|
|
304
|
+
bands between stable runs instead highlight deltas above `NOISE_DELTA` (2), so
|
|
305
|
+
`tolerance` does not cover those rows. A matched stable offset-run row was
|
|
306
|
+
mapped and fell below the 32 rule, so its rows need not be byte-identical. A segment may include `comparedOffset` of `-1` or
|
|
301
307
|
`1` when the compared current row is one pixel away from `currentStart`; output
|
|
302
|
-
row `i` was compared to current row `currentStart + i + comparedOffset`. A
|
|
303
|
-
includes `fallbackReason` when widths,
|
|
304
|
-
alignment resource limit prevent safe row
|
|
305
|
-
|
|
306
|
-
|
|
308
|
+
row `i` was compared to current row `currentStart + i + comparedOffset`. A
|
|
309
|
+
`mode: "coordinate-fallback"` result includes `fallbackReason` when widths,
|
|
310
|
+
ignore regions, ambiguity, or the alignment resource limit prevent safe row
|
|
311
|
+
alignment, and carries neither `alignmentStrategy` nor `tolerance`. The aligned
|
|
312
|
+
canvas may be taller than either source image because inserted and deleted rows
|
|
313
|
+
each occupy an output row.
|
|
307
314
|
|
|
308
315
|
When a changed item has a generated local `diffImagePath`, its PR comment cell
|
|
309
316
|
links to the uploaded report artifact (or the workflow run when no artifact URL
|
|
@@ -339,7 +346,7 @@ Additional missing-baseline fields: `baselineAvailable`, `currentResultsPath`.
|
|
|
339
346
|
- `diff.threshold` applies per screenshot
|
|
340
347
|
- Missing captures are counted separately from drift signals
|
|
341
348
|
- Comparison policy v1 `{ "version": 1, "threshold": number }` is deprecated but remains the compatibility default. Images are top-left aligned on a max-dimension union canvas with no scaling. Overlap pixels are compared, one-sided pixels count as changed (including transparent pixels), and empty union corners do not enter the denominator.
|
|
342
|
-
- Comparison policy v2 `{ "version": 2, "threshold": number }` is opt-in. When source widths match and no ignore regions are requested, rows are fingerprinted and aligned with a bounded Myers sequence diff. If that search hits its edit cap, long vertical-offset runs are mapped instead. Inside a run, a row
|
|
349
|
+
- Comparison policy v2 `{ "version": 2, "threshold": number }` is opt-in. When source widths match and no ignore regions are requested, rows are fingerprinted and aligned with a bounded Myers sequence diff. If that search hits its edit cap, long vertical-offset runs are mapped instead. Inside a run, a row is compared one pixel above or below the recorded current row only when the row's own recorded-offset ink cost exceeds `LOCAL_MISMATCH_COST` (0.5) and a ±1 neighbor matches; a stable row with a smaller residual keeps its own offset, so its nominal current row is still compared instead of being consumed without comparison. `currentStart` still consumes each current row once, so that neighbor is not a separate insertion or deletion. When the compared row differs, the segment includes `comparedOffset` of `-1` or `1`: output row `i` was compared to current row `currentStart + i + comparedOffset`. On stable offset-run rows, a pixel whose red, green, blue, or alpha channel differs by 32 or less stays unhighlighted, so rendering noise and soft edges remain matched. A local-mismatch residual band between stable runs is stricter: a pixel there is highlighted when any channel differs by more than `NOISE_DELTA` (2). A short cluster of such larger channel deltas is an orange edit. A wide band of those pixels is a local mismatch: it is highlighted in place and does not discard the rest of the page. Unmatched baseline rows are red deletions. Current rows with no baseline partner are green insertions. `matched` on this path means the row was mapped and fell below this highlight rule. Exact fingerprint matches remain byte-identical. Aligned metadata records which strategy applied: exact Myers results carry `alignmentStrategy: "exact"` and no `tolerance`; offset-run results carry `alignmentStrategy: "offset"` and the stable-run `tolerance: 32`, while residual local-mismatch bands are not covered by `tolerance`. An unmatched row that also occurs on the opposite side is ambiguous only when its repeat count on either side stays below `MIN_STRUCTURAL_ROW_REPEATS` (16): such a row could be moved content. Ambiguous exact alignment first tries the offset-run mapping; when that mapping succeeds, the result is `vertical-aligned` with `alignmentStrategy: "offset"`, and only an unsuccessful mapping falls back to v1 coordinate comparison as `ambiguous`. A row that repeats at least 16 times on either side (page background, card padding, borders) is structural and does not count: where it lands inside a run of identical rows makes no visible difference. Offset-run plans treat blank structural padding as matched rather than painting it green or red: a one-sided row carries content only when at least `CONTENT_INK_MIN` (16) full-width ink pixels are present. A one-sided content row that duplicates a claimed row in the other image within `STRUCTURAL_DUPLICATE_COST` (0.05 row ink cost) at an offset within `STRUCTURAL_DUPLICATE_WINDOW` (3) of the neighboring stable runs is a compensating rendering band, not a new or deleted line, and stays matched. An unresolved band at a shift boundary splits into changed-ink rows and padding: a baseline ink row pairs with the current row at the preceding stable offset when that row carries ink and is free, otherwise with its best evidence offset when that offset sits within the duplicate window of the neighboring runs, otherwise it is a red deletion. Leading blank rows stay with the preceding offset only when every candidate current row is free; trailing blank rows follow the last paired content offset when free, otherwise they are the deleted region's blank tail. A candidate current row already claimed by a stable run is never a free partner. Inside a paired row, a mismatch set with fewer than `CONTENT_INK_MIN` mismatched pixels is a localized rendering displacement (a glyph or badge edge drawn a few rows off its text) only when every mismatch has both background evidence — the baseline pixel exists in a claimed current row — and foreground evidence — the current pixel exists in a claimed baseline row — at one common non-zero relative offset within the duplicate window; inserted current rows and deleted baseline rows count as claimed rows on their respective sides. A mismatch with only one side's duplicate, such as a new small mark on a uniform background whose old background has a neighboring current copy but whose foreground exists nowhere in the baseline, is content and stays orange. A contiguous displaced run is suppressed only when it borders matched rows or blank structural one-sided rows; a run bordering unexplained mismatched content or content-carrying one-sided rows stays painted as part of that edit. A mismatch set reaching `CONTENT_INK_MIN` is a structured glyph or region recolor and stays orange. Width changes, ignore regions, resource limits, and ambiguous exact-row matches whose offset-run mapping also fails fall back to v1 coordinate comparison and record the fallback reason; coordinate-fallback metadata carries neither `alignmentStrategy` nor `tolerance`. This is a raster row alignment aid, not DOM or arbitrary component matching.
|
|
343
350
|
- Both policies preserve the explicit dimension-change signal: a source height/width change remains a changed route even when the aligned mismatch ratio is at or below the threshold. Empty output corners do not enter the denominator.
|
|
344
351
|
- Local diff images are rendered with a semantic palette: orange = changed pixels, green = added or inserted pixels, red = removed or deleted pixels. Equal-dimension strict diffs (`generateDiffImage`) use orange for changed pixels and cannot contain added/removed pixels. Reports render a legend whenever a local diff image is embedded; summaries that carry only comparison metadata (Snap provider runs, which render diffs hosted-side) render no legend.
|
|
345
352
|
- `dimensionChanges[]` remains part of the summary contract but is empty for local comparisons in v1. Strict same-dimension comparison (`compareBuffers`) remains available to direct `@snapdrift/compare-core`/`comparePngs` callers that pass no policy.
|
|
@@ -393,7 +400,19 @@ since a caller may raise it per call.
|
|
|
393
400
|
Policy v2 uses the aligned output height for this same budget. Because an
|
|
394
401
|
inserted row and a deleted row each occupy an output row, that height can be up
|
|
395
402
|
to the sum of the baseline and current heights. If the aligned canvas exceeds
|
|
396
|
-
the budget,
|
|
403
|
+
the budget, both the exact-row alignment path and the offset-run path fall back
|
|
404
|
+
to coordinate comparison with `fallbackReason: 'alignment-limit'`; the
|
|
405
|
+
coordinate union must still fit, and an oversized coordinate union still throws
|
|
406
|
+
`comparison_too_large` before allocating a diff image.
|
|
407
|
+
|
|
408
|
+
The offset-run scoring pass is separately bounded by `MAX_OFFSET_SCORE_VISITS`
|
|
409
|
+
(2.5e9). Its visit count is `baselineHeight × 201 offsets ×
|
|
410
|
+
ceil(baselineWidth / 2)` (sampling every second column, offsets `-40..160`);
|
|
411
|
+
above that budget, or when the per-row cost table and Viterbi backpointers exceed
|
|
412
|
+
their byte ceiling, the offset-run path falls back to coordinate comparison with
|
|
413
|
+
`fallbackReason: 'alignment-limit'`. The 2.5e9 budget admits a 1170×17187 mobile
|
|
414
|
+
pair (~2.02e9 visits) that the previous 1.5e9 budget rejected, while the
|
|
415
|
+
1440×3386 desktop pair stays at ~0.49e9.
|
|
397
416
|
|
|
398
417
|
`32 × 1024 × 1024` is the *default* ceiling, not an algorithm property. The cost
|
|
399
418
|
is dominated by the decoded images: both RGBA inputs (4 bytes per pixel each) are
|
|
@@ -46,7 +46,7 @@ SnapDrift owns route selection, capture, comparison, skipped-report generation,
|
|
|
46
46
|
|
|
47
47
|
```yaml
|
|
48
48
|
- name: SnapDrift Baseline
|
|
49
|
-
uses: ranacseruet/snapdrift@v0.
|
|
49
|
+
uses: ranacseruet/snapdrift@v0.17.0
|
|
50
50
|
with:
|
|
51
51
|
mode: baseline
|
|
52
52
|
repo-config-path: .github/snapdrift.json
|
|
@@ -71,7 +71,7 @@ Then add SnapDrift after the app is running:
|
|
|
71
71
|
|
|
72
72
|
```yaml
|
|
73
73
|
- name: SnapDrift Report
|
|
74
|
-
uses: ranacseruet/snapdrift@v0.
|
|
74
|
+
uses: ranacseruet/snapdrift@v0.17.0
|
|
75
75
|
with:
|
|
76
76
|
mode: pr-diff
|
|
77
77
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -86,8 +86,8 @@ The wrappers remain published in their own right, and are equivalent to the disp
|
|
|
86
86
|
|
|
87
87
|
| Entry point | Equivalent to |
|
|
88
88
|
|-------------|---------------|
|
|
89
|
-
| `ranacseruet/snapdrift@v0.
|
|
90
|
-
| `ranacseruet/snapdrift@v0.
|
|
89
|
+
| `ranacseruet/snapdrift@v0.17.0` with `mode: baseline` | `ranacseruet/snapdrift/actions/baseline@v0.17.0` |
|
|
90
|
+
| `ranacseruet/snapdrift@v0.17.0` with `mode: pr-diff` | `ranacseruet/snapdrift/actions/pr-diff@v0.17.0` |
|
|
91
91
|
|
|
92
92
|
Use the wrappers directly when you want to skip the dispatcher's input indirection, and the lower-level `capture`, `compare`, `scope`, `resolve-baseline`, `stage`, `comment`, and `enforce` actions when you need to orchestrate the stages yourself.
|
|
93
93
|
|
|
@@ -130,7 +130,7 @@ jobs:
|
|
|
130
130
|
done
|
|
131
131
|
|
|
132
132
|
- name: SnapDrift Report
|
|
133
|
-
uses: ranacseruet/snapdrift@v0.
|
|
133
|
+
uses: ranacseruet/snapdrift@v0.17.0
|
|
134
134
|
with:
|
|
135
135
|
mode: pr-diff
|
|
136
136
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -170,7 +170,7 @@ jobs:
|
|
|
170
170
|
done
|
|
171
171
|
|
|
172
172
|
- name: SnapDrift Report
|
|
173
|
-
uses: ranacseruet/snapdrift@v0.
|
|
173
|
+
uses: ranacseruet/snapdrift@v0.17.0
|
|
174
174
|
with:
|
|
175
175
|
mode: pr-diff
|
|
176
176
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -371,11 +371,12 @@ The Snap client never retries 4xx and never falls back, even when `onUnavailable
|
|
|
371
371
|
**Screenshots have different dimensions**
|
|
372
372
|
SnapDrift compares the top-left-aligned union canvas by default. Policy v2 can
|
|
373
373
|
align equal-width rows so unchanged content after an insertion is not painted
|
|
374
|
-
as drift; width changes, ignored regions, ambiguous rows
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
374
|
+
as drift; width changes, ignored regions, ambiguous rows (an inserted or
|
|
375
|
+
deleted row that also appears elsewhere but is not a repeated padding or
|
|
376
|
+
background row), and resource limits fall back to coordinate comparison.
|
|
377
|
+
Either policy records the route in `changed[]`, and local runs write a diff
|
|
378
|
+
PNG. Refresh the baseline after an intentional change when you want future
|
|
379
|
+
runs to return to equal-size comparisons.
|
|
379
380
|
|
|
380
381
|
**A route appears in `errors[]`**
|
|
381
382
|
Capture failed before comparison. Confirm the app is fully ready and reachable before SnapDrift runs.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snapdrift",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "SnapDrift captures, compares, and reports UI drift for Playwright-based app checks.",
|
|
@@ -103,9 +103,9 @@
|
|
|
103
103
|
"registry": "https://registry.npmjs.org"
|
|
104
104
|
},
|
|
105
105
|
"dependencies": {
|
|
106
|
-
"@snapdrift/adapter-fs": "^1.7.
|
|
106
|
+
"@snapdrift/adapter-fs": "^1.7.2",
|
|
107
107
|
"@snapdrift/adapter-report-md": "^1.4.0",
|
|
108
|
-
"@snapdrift/compare-core": "^1.
|
|
108
|
+
"@snapdrift/compare-core": "^1.8.0",
|
|
109
109
|
"@snapdrift/manifest": "^1.8.0",
|
|
110
110
|
"js-yaml": "^5.4.0",
|
|
111
111
|
"playwright": "^1.59.1",
|
|
@@ -41,6 +41,20 @@ export interface ComparisonMetadata {
|
|
|
41
41
|
totalPixels: number;
|
|
42
42
|
policyVersion?: 2;
|
|
43
43
|
mode?: 'vertical-aligned' | 'coordinate-fallback';
|
|
44
|
+
/**
|
|
45
|
+
* Strategy that produced an aligned result. `exact` matched byte-identical
|
|
46
|
+
* rows with no tolerance; `offset` mapped piecewise vertical-offset runs and
|
|
47
|
+
* used a per-channel highlight tolerance for stable runs. Absent for
|
|
48
|
+
* coordinate fallback.
|
|
49
|
+
*/
|
|
50
|
+
alignmentStrategy?: 'exact' | 'offset';
|
|
51
|
+
/**
|
|
52
|
+
* Maximum per-channel delta that stays matched on stable offset-run rows
|
|
53
|
+
* (`STRUCTURED_DELTA`, 32). Local-mismatch residual bands use `NOISE_DELTA`
|
|
54
|
+
* (2) instead, so this field does not describe those rows. Present only with
|
|
55
|
+
* `alignmentStrategy: 'offset'`.
|
|
56
|
+
*/
|
|
57
|
+
tolerance?: number;
|
|
44
58
|
rowMapping?: ComparisonRowMapping[];
|
|
45
59
|
fallbackReason?: ComparisonFallbackReason;
|
|
46
60
|
}
|