afterbefore 0.1.8 → 0.1.10
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/dist/cli.js +48 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.js +47 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1009,6 +1009,7 @@ async function detectSections(page, maxSections) {
|
|
|
1009
1009
|
if (!container || tagged.has(container)) continue;
|
|
1010
1010
|
if (container.scrollHeight > document.documentElement.scrollHeight * 0.9) continue;
|
|
1011
1011
|
container.setAttribute("data-ab-section", String(idx));
|
|
1012
|
+
heading.setAttribute("data-ab-heading", String(idx));
|
|
1012
1013
|
tagged.add(container);
|
|
1013
1014
|
results.push({
|
|
1014
1015
|
label: (heading.textContent ?? "").trim(),
|
|
@@ -1067,6 +1068,7 @@ async function tagSectionOnPage(page, headingText, index) {
|
|
|
1067
1068
|
}
|
|
1068
1069
|
if (el) {
|
|
1069
1070
|
el.setAttribute("data-ab-section", String(idx));
|
|
1071
|
+
match.setAttribute("data-ab-heading", String(idx));
|
|
1070
1072
|
return true;
|
|
1071
1073
|
}
|
|
1072
1074
|
return false;
|
|
@@ -1074,12 +1076,33 @@ async function tagSectionOnPage(page, headingText, index) {
|
|
|
1074
1076
|
{ text: headingText, idx: index }
|
|
1075
1077
|
);
|
|
1076
1078
|
}
|
|
1079
|
+
async function hideSectionHeading(page, sectionIndex) {
|
|
1080
|
+
await page.evaluate((idx) => {
|
|
1081
|
+
const heading = document.querySelector(`[data-ab-heading="${idx}"]`);
|
|
1082
|
+
if (heading instanceof HTMLElement) {
|
|
1083
|
+
heading.style.display = "none";
|
|
1084
|
+
}
|
|
1085
|
+
}, sectionIndex);
|
|
1086
|
+
}
|
|
1087
|
+
async function showSectionHeading(page, sectionIndex) {
|
|
1088
|
+
await page.evaluate((idx) => {
|
|
1089
|
+
const heading = document.querySelector(`[data-ab-heading="${idx}"]`);
|
|
1090
|
+
if (heading instanceof HTMLElement) {
|
|
1091
|
+
heading.style.display = "";
|
|
1092
|
+
}
|
|
1093
|
+
}, sectionIndex);
|
|
1094
|
+
}
|
|
1077
1095
|
async function cleanupSectionTags(page) {
|
|
1078
1096
|
await page.evaluate(() => {
|
|
1079
|
-
const
|
|
1080
|
-
for (const el of tagged) {
|
|
1097
|
+
for (const el of document.querySelectorAll("[data-ab-section]")) {
|
|
1081
1098
|
el.removeAttribute("data-ab-section");
|
|
1082
1099
|
}
|
|
1100
|
+
for (const el of document.querySelectorAll("[data-ab-heading]")) {
|
|
1101
|
+
el.removeAttribute("data-ab-heading");
|
|
1102
|
+
if (el instanceof HTMLElement) {
|
|
1103
|
+
el.style.display = "";
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1083
1106
|
});
|
|
1084
1107
|
}
|
|
1085
1108
|
function sanitizeSectionLabel(label) {
|
|
@@ -1395,12 +1418,16 @@ async function captureAutoSections(afterPage, beforePage, parentPrefix, parentLa
|
|
|
1395
1418
|
const sectionAfterPath = join6(outputDir, `${sectionPrefix}-after.png`);
|
|
1396
1419
|
const sectionBeforePath = join6(outputDir, `${sectionPrefix}-before.png`);
|
|
1397
1420
|
try {
|
|
1421
|
+
await hideSectionHeading(afterPage, section.index);
|
|
1398
1422
|
const afterEl = afterPage.locator(`[data-ab-section="${section.index}"]`).first();
|
|
1399
1423
|
await afterEl.screenshot({ path: sectionAfterPath });
|
|
1424
|
+
await showSectionHeading(afterPage, section.index);
|
|
1400
1425
|
const found = await tagSectionOnPage(beforePage, section.label, section.index);
|
|
1401
1426
|
if (found) {
|
|
1427
|
+
await hideSectionHeading(beforePage, section.index);
|
|
1402
1428
|
const beforeEl = beforePage.locator(`[data-ab-section="${section.index}"]`).first();
|
|
1403
1429
|
await beforeEl.screenshot({ path: sectionBeforePath });
|
|
1430
|
+
await showSectionHeading(beforePage, section.index);
|
|
1404
1431
|
} else {
|
|
1405
1432
|
continue;
|
|
1406
1433
|
}
|
|
@@ -1625,18 +1652,30 @@ function normalizeDimensions(img1, img2) {
|
|
|
1625
1652
|
return [pad(img1), pad(img2)];
|
|
1626
1653
|
}
|
|
1627
1654
|
async function generateComposite(beforePath, afterPath, outputPath, browser, bgColor) {
|
|
1628
|
-
const
|
|
1629
|
-
const
|
|
1655
|
+
const beforeBuf = readFileSync5(beforePath);
|
|
1656
|
+
const afterBuf = readFileSync5(afterPath);
|
|
1657
|
+
const beforeUri = `data:image/png;base64,${beforeBuf.toString("base64")}`;
|
|
1658
|
+
const afterUri = `data:image/png;base64,${afterBuf.toString("base64")}`;
|
|
1659
|
+
const beforePng = PNG.sync.read(beforeBuf);
|
|
1660
|
+
const afterPng = PNG.sync.read(afterBuf);
|
|
1661
|
+
const imgW = Math.max(beforePng.width, afterPng.width);
|
|
1662
|
+
const imgH = Math.max(beforePng.height, afterPng.height);
|
|
1663
|
+
const PADDING = 120;
|
|
1664
|
+
const GAP = 80;
|
|
1665
|
+
const LABEL_H = 70;
|
|
1666
|
+
const canvasW = Math.max(600, Math.min(2400, imgW * 2 + GAP + PADDING * 2));
|
|
1667
|
+
const canvasH = Math.max(300, Math.min(2400, imgH + LABEL_H + PADDING * 2));
|
|
1668
|
+
const maxImgH = canvasH - PADDING * 2 - LABEL_H;
|
|
1630
1669
|
const page = await browser.newPage({
|
|
1631
|
-
viewport: { width:
|
|
1670
|
+
viewport: { width: canvasW, height: canvasH },
|
|
1632
1671
|
deviceScaleFactor: 1
|
|
1633
1672
|
});
|
|
1634
1673
|
const html = `<!DOCTYPE html>
|
|
1635
1674
|
<html><head><style>
|
|
1636
1675
|
* { margin: 0; box-sizing: border-box; }
|
|
1637
|
-
body { background: ${bgColor}; display: flex; justify-content: center; align-items: center; width:
|
|
1676
|
+
body { background: ${bgColor}; display: flex; justify-content: center; align-items: center; width: ${canvasW}px; height: ${canvasH}px; padding: ${PADDING}px; gap: ${GAP}px; overflow: hidden; }
|
|
1638
1677
|
.col { flex: 1; display: flex; flex-direction: column; align-items: center; min-width: 0; max-height: 100%; }
|
|
1639
|
-
img { width: 100%; max-height:
|
|
1678
|
+
img { width: 100%; max-height: ${maxImgH}px; object-fit: contain; }
|
|
1640
1679
|
.label { margin-top: 40px; font: 500 30px/1 system-ui, sans-serif; flex-shrink: 0; }
|
|
1641
1680
|
.before { color: #888; }
|
|
1642
1681
|
.after { color: #22c55e; }
|
|
@@ -1867,7 +1906,7 @@ async function runPipeline(options) {
|
|
|
1867
1906
|
const outputDir = resolve4(cwd, output, sessionName);
|
|
1868
1907
|
const startTime = Date.now();
|
|
1869
1908
|
try {
|
|
1870
|
-
const version = true ? "0.1.
|
|
1909
|
+
const version = true ? "0.1.10" : "dev";
|
|
1871
1910
|
console.log(`
|
|
1872
1911
|
afterbefore v${version} \xB7 Comparing against ${base}
|
|
1873
1912
|
`);
|
|
@@ -2012,7 +2051,7 @@ afterbefore v${version} \xB7 Comparing against ${base}
|
|
|
2012
2051
|
var program = new Command();
|
|
2013
2052
|
program.name("afterbefore").description(
|
|
2014
2053
|
"Automatic before/after screenshot capture for PRs. Git diff is the config."
|
|
2015
|
-
).version("0.1.
|
|
2054
|
+
).version("0.1.10").option("--base <ref>", "Base branch or ref to compare against", "main").option("--output <dir>", "Output directory for screenshots", ".afterbefore").option("--post", "Post results as a PR comment via gh CLI", false).option(
|
|
2016
2055
|
"--threshold <percent>",
|
|
2017
2056
|
"Diff threshold percentage (changes below this are ignored)",
|
|
2018
2057
|
"0.1"
|