@saeroon/cli 0.2.9 → 0.2.11
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/index.js +226 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2547,9 +2547,8 @@ var VALID_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
|
2547
2547
|
"main-block",
|
|
2548
2548
|
"aside-block",
|
|
2549
2549
|
"article-block",
|
|
2550
|
-
// Specialized (
|
|
2550
|
+
// Specialized (4)
|
|
2551
2551
|
"site-menu",
|
|
2552
|
-
"floating-action-widget",
|
|
2553
2552
|
"sticky-cta-bar",
|
|
2554
2553
|
"cookie-consent-bar",
|
|
2555
2554
|
"announcement-bar"
|
|
@@ -4035,6 +4034,25 @@ var EXTRACTION_SCRIPT = `
|
|
|
4035
4034
|
return 'card-thumbnail';
|
|
4036
4035
|
}
|
|
4037
4036
|
|
|
4037
|
+
// \u2500\u2500 0. \uD31D\uC5C5/\uC624\uBC84\uB808\uC774 DOM \uC81C\uAC70 (\uC139\uC158 \uAC10\uC9C0 \uC804) \u2500\u2500
|
|
4038
|
+
document.querySelectorAll(
|
|
4039
|
+
'dialog[open], [class*=popup], [class*=layerpopup], [class*=modal]:not(body), [class*=cookie], [class*=consent], [class*=overlay]:not(body), [class*=dimmed]'
|
|
4040
|
+
).forEach(el => {
|
|
4041
|
+
const rect = el.getBoundingClientRect();
|
|
4042
|
+
const style = window.getComputedStyle(el);
|
|
4043
|
+
const pos = style.position;
|
|
4044
|
+
// fixed/absolute \uC704\uCE58 + \uBDF0\uD3EC\uD2B8 \uB300\uBD80\uBD84 \uCC28\uC9C0 \u2192 \uD31D\uC5C5/\uC624\uBC84\uB808\uC774
|
|
4045
|
+
if ((pos === 'fixed' || pos === 'absolute') && rect.width >= window.innerWidth * 0.5) {
|
|
4046
|
+
el.remove();
|
|
4047
|
+
return;
|
|
4048
|
+
}
|
|
4049
|
+
// leeko-layerpopup \uAC19\uC740 \uD31D\uC5C5: class\uC5D0 popup/modal/overlay \uD3EC\uD568 + \uB2EB\uAE30 \uBC84\uD2BC \uC874\uC7AC
|
|
4050
|
+
if (el.querySelector('[class*=close], [aria-label*=close], button')) {
|
|
4051
|
+
const isSmall = rect.width < 100 && rect.height < 100;
|
|
4052
|
+
if (!isSmall) el.remove();
|
|
4053
|
+
}
|
|
4054
|
+
});
|
|
4055
|
+
|
|
4038
4056
|
// \u2500\u2500 1. Structure \u2500\u2500
|
|
4039
4057
|
const sectionSelectors = 'body > header, body > footer, body > nav, body > main, body > section, body > div, body > article, body > aside';
|
|
4040
4058
|
const topLevelEls = document.querySelectorAll(sectionSelectors);
|
|
@@ -4107,6 +4125,39 @@ var EXTRACTION_SCRIPT = `
|
|
|
4107
4125
|
return !leafTags.has(tag);
|
|
4108
4126
|
});
|
|
4109
4127
|
|
|
4128
|
+
// \u2500\u2500 Fallback: \uC139\uC158 3\uAC1C \uBBF8\uB9CC\uC774\uBA74 content-based \uD0D0\uC0C9 \u2500\u2500
|
|
4129
|
+
if (sectionEls.length < 3) {
|
|
4130
|
+
const vw = window.innerWidth;
|
|
4131
|
+
const candidates = [];
|
|
4132
|
+
// \uBAA8\uB4E0 \uC694\uC18C\uB97C \uC21C\uD68C\uD558\uBA70 \uC139\uC158 \uD6C4\uBCF4 \uD0D0\uC0C9
|
|
4133
|
+
document.querySelectorAll('header, footer, nav, section, article, aside, div, main').forEach(el => {
|
|
4134
|
+
if (leafTags.has(el.tagName.toLowerCase())) return;
|
|
4135
|
+
const rect = el.getBoundingClientRect();
|
|
4136
|
+
// \uC804\uCCB4 \uD3ED\uC758 80% \uC774\uC0C1 + \uB192\uC774 100px \uC774\uC0C1
|
|
4137
|
+
if (rect.width < vw * 0.8 || rect.height < 100) return;
|
|
4138
|
+
// \uD615\uC81C\uAC00 \uC788\uB294 \uC694\uC18C\uB9CC (\uC139\uC158 = \uAC19\uC740 \uB808\uBCA8 \uD615\uC81C\uB4E4)
|
|
4139
|
+
const parent = el.parentElement;
|
|
4140
|
+
if (!parent) return;
|
|
4141
|
+
const siblings = Array.from(parent.children).filter(s => {
|
|
4142
|
+
const sr = s.getBoundingClientRect();
|
|
4143
|
+
return sr.width >= vw * 0.8 && sr.height >= 100 && !leafTags.has(s.tagName.toLowerCase());
|
|
4144
|
+
});
|
|
4145
|
+
if (siblings.length >= 2) {
|
|
4146
|
+
candidates.push({ el, siblings, parentEl: parent, sibCount: siblings.length });
|
|
4147
|
+
}
|
|
4148
|
+
});
|
|
4149
|
+
|
|
4150
|
+
// \uAC00\uC7A5 \uB9CE\uC740 \uD615\uC81C\uB97C \uAC00\uC9C4 \uADF8\uB8F9 \uC120\uD0DD
|
|
4151
|
+
if (candidates.length > 0) {
|
|
4152
|
+
candidates.sort((a, b) => b.sibCount - a.sibCount);
|
|
4153
|
+
const best = candidates[0];
|
|
4154
|
+
// \uAE30\uC874 \uACB0\uACFC\uBCF4\uB2E4 \uB098\uC740 \uACBD\uC6B0\uB9CC \uAD50\uCCB4
|
|
4155
|
+
if (best.sibCount > sectionEls.length) {
|
|
4156
|
+
sectionEls = best.siblings;
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
|
|
4110
4161
|
const sections = [];
|
|
4111
4162
|
let maxDepth = 0;
|
|
4112
4163
|
|
|
@@ -4265,7 +4316,22 @@ var EXTRACTION_SCRIPT = `
|
|
|
4265
4316
|
if (ff) fontFamilySet.add(ff.split(',')[0].trim().replace(/['"]/g, ''));
|
|
4266
4317
|
});
|
|
4267
4318
|
|
|
4268
|
-
// 2c. Spacing
|
|
4319
|
+
// 2c. Spacing (B-2: \uC815\uBC00 \uCD94\uCD9C)
|
|
4320
|
+
function median(arr) {
|
|
4321
|
+
if (arr.length === 0) return 0;
|
|
4322
|
+
const s = [...arr].sort((a, b) => a - b);
|
|
4323
|
+
const mid = Math.floor(s.length / 2);
|
|
4324
|
+
return s.length % 2 ? s[mid] : Math.round((s[mid - 1] + s[mid]) / 2);
|
|
4325
|
+
}
|
|
4326
|
+
function mode(arr) {
|
|
4327
|
+
if (arr.length === 0) return 0;
|
|
4328
|
+
const freq = {};
|
|
4329
|
+
arr.forEach(v => { const k = Math.round(v); freq[k] = (freq[k] || 0) + 1; });
|
|
4330
|
+
return Number(Object.entries(freq).sort((a, b) => b[1] - a[1])[0][0]);
|
|
4331
|
+
}
|
|
4332
|
+
function roundTo4(v) { return Math.round(v / 4) * 4; } // 4px \uB2E8\uC704 \uC815\uADDC\uD654
|
|
4333
|
+
|
|
4334
|
+
// \uC139\uC158 \uAC04 \uAC04\uACA9
|
|
4269
4335
|
const sectionGaps = [];
|
|
4270
4336
|
for (let i = 1; i < sections.length; i++) {
|
|
4271
4337
|
const prev = topLevelEls[i - 1];
|
|
@@ -4274,25 +4340,107 @@ var EXTRACTION_SCRIPT = `
|
|
|
4274
4340
|
const prevRect = prev.getBoundingClientRect();
|
|
4275
4341
|
const currRect = curr.getBoundingClientRect();
|
|
4276
4342
|
const gap = currRect.top - prevRect.bottom;
|
|
4277
|
-
if (gap > 0 && gap < 500) sectionGaps.push(gap);
|
|
4343
|
+
if (gap > 0 && gap < 500) sectionGaps.push(Math.round(gap));
|
|
4278
4344
|
}
|
|
4279
4345
|
}
|
|
4280
4346
|
|
|
4281
|
-
|
|
4282
|
-
const
|
|
4347
|
+
// \uC139\uC158 padding-top / padding-bottom \uC218\uC9D1
|
|
4348
|
+
const sectionPaddingTops = [];
|
|
4349
|
+
const sectionPaddingBottoms = [];
|
|
4350
|
+
topLevelEls.forEach(el => {
|
|
4351
|
+
if (!el) return;
|
|
4352
|
+
const pt = parsePixel(getComputedProp(el, 'padding-top'));
|
|
4353
|
+
const pb = parsePixel(getComputedProp(el, 'padding-bottom'));
|
|
4354
|
+
if (pt > 0) sectionPaddingTops.push(Math.round(pt));
|
|
4355
|
+
if (pb > 0) sectionPaddingBottoms.push(Math.round(pb));
|
|
4356
|
+
});
|
|
4283
4357
|
|
|
4284
|
-
//
|
|
4285
|
-
|
|
4286
|
-
const
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4358
|
+
// content padding: \uB2E4\uC218 \uCEE8\uD14C\uC774\uB108\uC5D0\uC11C \uC218\uC9D1
|
|
4359
|
+
const contentPaddingValues = [];
|
|
4360
|
+
const contentPaddingDetails = [];
|
|
4361
|
+
const containerSelectors = 'main, [class*=container], [class*=wrapper], [class*=content], [class*=inner]';
|
|
4362
|
+
document.querySelectorAll(containerSelectors).forEach(el => {
|
|
4363
|
+
const pl = parsePixel(getComputedProp(el, 'padding-left'));
|
|
4364
|
+
const pr = parsePixel(getComputedProp(el, 'padding-right'));
|
|
4365
|
+
if (pl > 0 || pr > 0) {
|
|
4366
|
+
contentPaddingValues.push(pl, pr);
|
|
4367
|
+
contentPaddingDetails.push({ left: Math.round(pl), right: Math.round(pr), count: 1 });
|
|
4368
|
+
}
|
|
4369
|
+
});
|
|
4370
|
+
// \uC911\uBCF5 \uD569\uC0B0
|
|
4371
|
+
const paddingMap = {};
|
|
4372
|
+
contentPaddingDetails.forEach(d => {
|
|
4373
|
+
const k = d.left + ',' + d.right;
|
|
4374
|
+
if (paddingMap[k]) paddingMap[k].count++;
|
|
4375
|
+
else paddingMap[k] = { ...d };
|
|
4376
|
+
});
|
|
4377
|
+
const uniquePaddings = Object.values(paddingMap).sort((a, b) => b.count - a.count);
|
|
4378
|
+
const contentPadding = contentPaddingValues.length > 0 ? mode(contentPaddingValues.filter(v => v > 0)) : 16;
|
|
4379
|
+
|
|
4380
|
+
// card gap: \uBAA8\uB4E0 grid/flex \uCEE8\uD14C\uC774\uB108\uC5D0\uC11C \uC218\uC9D1
|
|
4381
|
+
const cardGapValues = [];
|
|
4382
|
+
const allLayoutContainers = document.querySelectorAll('*');
|
|
4383
|
+
allLayoutContainers.forEach(el => {
|
|
4384
|
+
const display = getComputedProp(el, 'display');
|
|
4385
|
+
if (!display || (!display.includes('grid') && !display.includes('flex'))) return;
|
|
4386
|
+
const gap = parsePixel(getComputedProp(el, 'gap'));
|
|
4387
|
+
const colGap = parsePixel(getComputedProp(el, 'column-gap'));
|
|
4388
|
+
const rowGap = parsePixel(getComputedProp(el, 'row-gap'));
|
|
4389
|
+
const v = gap || colGap || rowGap;
|
|
4390
|
+
if (v > 0 && v < 200) cardGapValues.push(Math.round(v));
|
|
4391
|
+
});
|
|
4392
|
+
// margin \uAE30\uBC18 \uAC04\uACA9\uB3C4 \uAC10\uC9C0 (flex/grid \uC544\uB2CC \uB9AC\uC2A4\uD2B8)
|
|
4393
|
+
document.querySelectorAll('ul, ol, [class*=list], [class*=cards]').forEach(parent => {
|
|
4394
|
+
const children = Array.from(parent.children);
|
|
4395
|
+
for (let i = 1; i < Math.min(children.length, 5); i++) {
|
|
4396
|
+
const prevRect = children[i - 1].getBoundingClientRect();
|
|
4397
|
+
const currRect = children[i].getBoundingClientRect();
|
|
4398
|
+
const vGap = currRect.top - prevRect.bottom;
|
|
4399
|
+
const hGap = currRect.left - prevRect.right;
|
|
4400
|
+
const g = Math.abs(vGap) < 2 ? hGap : vGap; // \uC218\uD3C9 \uBC30\uCE58\uBA74 hGap
|
|
4401
|
+
if (g > 0 && g < 200) cardGapValues.push(Math.round(g));
|
|
4402
|
+
}
|
|
4403
|
+
});
|
|
4404
|
+
const cardGap = cardGapValues.length > 0 ? mode(cardGapValues) : 16;
|
|
4405
|
+
|
|
4406
|
+
// \uC139\uC158 \uB0B4 \uD615\uC81C \uC694\uC18C \uAC04 \uC218\uC9C1 \uAC04\uACA9 (elementGap)
|
|
4407
|
+
const elementGapValues = [];
|
|
4408
|
+
topLevelEls.forEach(sectionEl => {
|
|
4409
|
+
if (!sectionEl) return;
|
|
4410
|
+
const children = Array.from(sectionEl.children);
|
|
4411
|
+
for (let i = 1; i < Math.min(children.length, 8); i++) {
|
|
4412
|
+
const prevRect = children[i - 1].getBoundingClientRect();
|
|
4413
|
+
const currRect = children[i].getBoundingClientRect();
|
|
4414
|
+
const gap = currRect.top - prevRect.bottom;
|
|
4415
|
+
if (gap > 0 && gap < 200) elementGapValues.push(Math.round(gap));
|
|
4416
|
+
}
|
|
4417
|
+
});
|
|
4418
|
+
const elementGap = elementGapValues.length > 0 ? median(elementGapValues) : 16;
|
|
4419
|
+
|
|
4420
|
+
// \uBE48\uB3C4 \uBD84\uD3EC (4px \uB2E8\uC704\uB85C \uC815\uADDC\uD654)
|
|
4421
|
+
const spacingFrequency = {};
|
|
4422
|
+
[...sectionGaps, ...sectionPaddingTops, ...sectionPaddingBottoms, ...contentPaddingValues, ...cardGapValues, ...elementGapValues]
|
|
4423
|
+
.filter(v => v > 0)
|
|
4424
|
+
.forEach(v => { const k = roundTo4(v); spacingFrequency[k] = (spacingFrequency[k] || 0) + 1; });
|
|
4291
4425
|
|
|
4292
|
-
// base unit \
|
|
4293
|
-
const
|
|
4426
|
+
// base unit: \uBE48\uB3C4 \uCD5C\uB2E4 \uAC12 \uAE30\uBC18, 4/8 \uCCB4\uACC4 \uC790\uB3D9 \uAC10\uC9C0
|
|
4427
|
+
const freqEntries = Object.entries(spacingFrequency).sort((a, b) => b[1] - a[1]);
|
|
4428
|
+
const topValues = freqEntries.slice(0, 6).map(e => Number(e[0]));
|
|
4294
4429
|
function gcd(a, b) { return b === 0 ? a : gcd(b, a % b); }
|
|
4295
|
-
const baseUnit =
|
|
4430
|
+
const baseUnit = topValues.length > 1 ? topValues.reduce((a, b) => gcd(a, b)) : (topValues[0] || 8);
|
|
4431
|
+
|
|
4432
|
+
// spacing details \uAC1D\uCCB4
|
|
4433
|
+
const spacingDetails = {
|
|
4434
|
+
sectionGaps,
|
|
4435
|
+
sectionPadding: {
|
|
4436
|
+
top: sectionPaddingTops.length > 0 ? median(sectionPaddingTops) : 0,
|
|
4437
|
+
bottom: sectionPaddingBottoms.length > 0 ? median(sectionPaddingBottoms) : 0,
|
|
4438
|
+
},
|
|
4439
|
+
contentPaddings: uniquePaddings.slice(0, 5),
|
|
4440
|
+
cardGaps: [...new Set(cardGapValues)].sort((a, b) => a - b).slice(0, 10),
|
|
4441
|
+
elementGap,
|
|
4442
|
+
spacingFrequency,
|
|
4443
|
+
};
|
|
4296
4444
|
|
|
4297
4445
|
// 2d. BorderRadius
|
|
4298
4446
|
const radiusValues = [];
|
|
@@ -4605,10 +4753,11 @@ var EXTRACTION_SCRIPT = `
|
|
|
4605
4753
|
scale: typographyScale,
|
|
4606
4754
|
},
|
|
4607
4755
|
spacing: {
|
|
4608
|
-
sectionGap: sectionGaps.length > 0 ?
|
|
4756
|
+
sectionGap: sectionGaps.length > 0 ? median(sectionGaps) : 80,
|
|
4609
4757
|
contentPadding,
|
|
4610
4758
|
cardGap: cardGap || 16,
|
|
4611
4759
|
baseUnit: Math.max(baseUnit, 4),
|
|
4760
|
+
details: spacingDetails,
|
|
4612
4761
|
},
|
|
4613
4762
|
borderRadius: {
|
|
4614
4763
|
small: uniqueRadii[0] || 0,
|
|
@@ -5042,7 +5191,17 @@ async function commandAnalyze(url, options) {
|
|
|
5042
5191
|
console.log(chalk15.dim(` Accent: ${colors.accent}`));
|
|
5043
5192
|
console.log(chalk15.dim(` \uD3F0\uD2B8: ${typography.fontFamilies.join(", ") || "(\uCD94\uCD9C \uC2E4\uD328)"}`));
|
|
5044
5193
|
console.log(chalk15.dim(` \uC139\uC158 \uAC04\uACA9: ${spacing.sectionGap}px`));
|
|
5194
|
+
console.log(chalk15.dim(` \uCF58\uD150\uCE20 \uD328\uB529: ${spacing.contentPadding}px`));
|
|
5195
|
+
console.log(chalk15.dim(` \uCE74\uB4DC \uAC04\uACA9: ${spacing.cardGap}px`));
|
|
5045
5196
|
console.log(chalk15.dim(` \uAE30\uBCF8 \uB2E8\uC704: ${spacing.baseUnit}px`));
|
|
5197
|
+
if (spacing.details) {
|
|
5198
|
+
const d = spacing.details;
|
|
5199
|
+
console.log(chalk15.dim(` \uC139\uC158 \uD328\uB529: top ${d.sectionPadding.top}px / bottom ${d.sectionPadding.bottom}px`));
|
|
5200
|
+
console.log(chalk15.dim(` \uC694\uC18C \uAC04\uACA9: ${d.elementGap}px`));
|
|
5201
|
+
if (d.cardGaps.length > 1) {
|
|
5202
|
+
console.log(chalk15.dim(` \uCE74\uB4DC \uAC04\uACA9 \uBD84\uD3EC: [${d.cardGaps.join(", ")}]px`));
|
|
5203
|
+
}
|
|
5204
|
+
}
|
|
5046
5205
|
console.log("");
|
|
5047
5206
|
console.log(chalk15.bold("\uC778\uD130\uB799\uC158"));
|
|
5048
5207
|
const { interactions } = analysis;
|
|
@@ -5232,6 +5391,27 @@ async function runMultiViewportCompare(options) {
|
|
|
5232
5391
|
console.log("");
|
|
5233
5392
|
const hasMagick = checkCommand("magick");
|
|
5234
5393
|
const results = [];
|
|
5394
|
+
const baselineMap = /* @__PURE__ */ new Map();
|
|
5395
|
+
if (hasMagick) {
|
|
5396
|
+
const blSpinner = spinner("\uAE30\uC900\uC120 \uCE21\uC815 \uC911 (\uB808\uD37C\uB7F0\uC2A4 2\uD68C \uCEA1\uCC98)...");
|
|
5397
|
+
for (const vp of viewports) {
|
|
5398
|
+
const blRef1 = join3(outputDir, `baseline-1-${vp.name}.png`);
|
|
5399
|
+
const blRef2 = join3(outputDir, `baseline-2-${vp.name}.png`);
|
|
5400
|
+
const blDiff = join3(outputDir, `baseline-diff-${vp.name}.png`);
|
|
5401
|
+
try {
|
|
5402
|
+
captureScreenshot2(options.ref, blRef1, vp.width, vp.height);
|
|
5403
|
+
captureScreenshot2(options.ref, blRef2, vp.width, vp.height);
|
|
5404
|
+
const blPct = generateDiffWithMagick(blRef1, blRef2, blDiff);
|
|
5405
|
+
baselineMap.set(vp.name, blPct >= 0 ? blPct : 0);
|
|
5406
|
+
} catch {
|
|
5407
|
+
baselineMap.set(vp.name, 0);
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
const blValues = [...baselineMap.values()];
|
|
5411
|
+
const blAvg = blValues.reduce((a, b) => a + b, 0) / blValues.length;
|
|
5412
|
+
blSpinner.stop(chalk16.dim(` \uAE30\uC900\uC120 \uB178\uC774\uC988: \uD3C9\uADE0 ${blAvg.toFixed(1)}% (${blValues.map((v, i) => `${viewports[i].name} ${v.toFixed(1)}%`).join(", ")})`));
|
|
5413
|
+
console.log("");
|
|
5414
|
+
}
|
|
5235
5415
|
for (const vp of viewports) {
|
|
5236
5416
|
const vpSpinner = spinner(`${vp.name} (${vp.width}px) \uBE44\uAD50 \uC911...`);
|
|
5237
5417
|
const refPath = join3(outputDir, `ref-${vp.name}.png`);
|
|
@@ -5247,17 +5427,23 @@ async function runMultiViewportCompare(options) {
|
|
|
5247
5427
|
generateSideBySide(refPath, previewPath, diffPath);
|
|
5248
5428
|
diffPercentage = -1;
|
|
5249
5429
|
}
|
|
5430
|
+
const baselinePct = baselineMap.get(vp.name) ?? 0;
|
|
5431
|
+
const adjustedPct = diffPercentage >= 0 ? Math.round(Math.max(0, diffPercentage - baselinePct) * 10) / 10 : -1;
|
|
5250
5432
|
results.push({
|
|
5251
5433
|
name: vp.name,
|
|
5252
5434
|
width: vp.width,
|
|
5253
5435
|
referenceScreenshot: refPath,
|
|
5254
5436
|
previewScreenshot: previewPath,
|
|
5255
5437
|
diffScreenshot: diffPath,
|
|
5256
|
-
diffPercentage
|
|
5438
|
+
diffPercentage,
|
|
5439
|
+
baselineDiffPercentage: baselinePct,
|
|
5440
|
+
adjustedDiffPercentage: adjustedPct
|
|
5257
5441
|
});
|
|
5258
|
-
const
|
|
5259
|
-
const
|
|
5260
|
-
|
|
5442
|
+
const rawText = diffPercentage >= 0 ? `${diffPercentage.toFixed(1)}%` : "(\uACC4\uC0B0 \uBD88\uAC00)";
|
|
5443
|
+
const adjText = adjustedPct >= 0 ? `${adjustedPct.toFixed(1)}%` : "";
|
|
5444
|
+
const adjColor = adjustedPct <= 10 ? chalk16.green : adjustedPct <= 25 ? chalk16.yellow : chalk16.red;
|
|
5445
|
+
const label = baselinePct > 0 ? ` ${vp.name}: raw ${rawText} \u2192 ${chalk16.bold("adjusted")} ${adjColor(adjText)} ${chalk16.dim(`(baseline ${baselinePct.toFixed(1)}%)`)}` : ` ${vp.name}: diff ${adjColor(rawText)}`;
|
|
5446
|
+
vpSpinner.stop(label);
|
|
5261
5447
|
} catch (error) {
|
|
5262
5448
|
vpSpinner.stop(chalk16.red(` ${vp.name}: \uC2E4\uD328 \u2014 ${error instanceof Error ? error.message : String(error)}`));
|
|
5263
5449
|
results.push({
|
|
@@ -5272,27 +5458,38 @@ async function runMultiViewportCompare(options) {
|
|
|
5272
5458
|
}
|
|
5273
5459
|
const validResults = results.filter((r) => r.diffPercentage >= 0);
|
|
5274
5460
|
const overallDiff = validResults.length > 0 ? validResults.reduce((sum, r) => sum + r.diffPercentage, 0) / validResults.length : -1;
|
|
5461
|
+
const adjustedResults = results.filter((r) => (r.adjustedDiffPercentage ?? -1) >= 0);
|
|
5462
|
+
const overallAdjusted = adjustedResults.length > 0 ? adjustedResults.reduce((sum, r) => sum + r.adjustedDiffPercentage, 0) / adjustedResults.length : void 0;
|
|
5275
5463
|
const report = {
|
|
5276
5464
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5277
5465
|
referenceUrl: options.ref,
|
|
5278
5466
|
previewUrl: options.preview,
|
|
5279
5467
|
viewports: results,
|
|
5280
|
-
overallDiffPercentage: Math.round(overallDiff * 10) / 10
|
|
5468
|
+
overallDiffPercentage: Math.round(overallDiff * 10) / 10,
|
|
5469
|
+
overallAdjustedDiffPercentage: overallAdjusted != null ? Math.round(overallAdjusted * 10) / 10 : void 0
|
|
5281
5470
|
};
|
|
5282
5471
|
const reportPath = join3(outputDir, "comparison-report.json");
|
|
5283
5472
|
await writeFile9(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
5284
5473
|
console.log("");
|
|
5285
5474
|
console.log(chalk16.bold("\uBE44\uAD50 \uC694\uC57D"));
|
|
5475
|
+
const hasBaseline = results.some((r) => (r.baselineDiffPercentage ?? 0) > 0);
|
|
5286
5476
|
for (const r of results) {
|
|
5287
|
-
const
|
|
5288
|
-
const
|
|
5289
|
-
|
|
5477
|
+
const adjPct = r.adjustedDiffPercentage ?? r.diffPercentage;
|
|
5478
|
+
const displayPct = adjPct >= 0 ? `${adjPct.toFixed(1)}%` : "N/A";
|
|
5479
|
+
const icon = adjPct <= 10 ? chalk16.green("\u25CF") : adjPct <= 25 ? chalk16.yellow("\u25CF") : chalk16.red("\u25CF");
|
|
5480
|
+
const blNote = hasBaseline && (r.baselineDiffPercentage ?? 0) > 0 ? chalk16.dim(` (raw ${r.diffPercentage.toFixed(1)}%, baseline \u2212${r.baselineDiffPercentage.toFixed(1)}%)`) : "";
|
|
5481
|
+
console.log(` ${icon} ${r.name.padEnd(8)} ${displayPct.padStart(6)}${blNote}`);
|
|
5290
5482
|
}
|
|
5483
|
+
const judgeDiff = overallAdjusted ?? overallDiff;
|
|
5291
5484
|
console.log("");
|
|
5292
|
-
if (
|
|
5293
|
-
const overallColor =
|
|
5294
|
-
|
|
5295
|
-
|
|
5485
|
+
if (judgeDiff >= 0) {
|
|
5486
|
+
const overallColor = judgeDiff <= 10 ? chalk16.green : judgeDiff <= 25 ? chalk16.yellow : chalk16.red;
|
|
5487
|
+
const label = overallAdjusted != null ? "\uC804\uCCB4 \uD3C9\uADE0 diff (adjusted)" : "\uC804\uCCB4 \uD3C9\uADE0 diff";
|
|
5488
|
+
console.log(chalk16.bold(`${label}: ${overallColor(`${judgeDiff.toFixed(1)}%`)}`));
|
|
5489
|
+
if (overallAdjusted != null && overallAdjusted !== overallDiff) {
|
|
5490
|
+
console.log(chalk16.dim(` (raw \uD3C9\uADE0: ${overallDiff.toFixed(1)}%, \uAE30\uC900\uC120 \uCC28\uAC10: \u2212${(overallDiff - overallAdjusted).toFixed(1)}%)`));
|
|
5491
|
+
}
|
|
5492
|
+
if (judgeDiff <= 10) {
|
|
5296
5493
|
console.log(chalk16.green(" \u2192 \uBE44\uAD50 \uB8E8\uD504 \uC644\uB8CC! CEO \uCD5C\uC885 \uD655\uC778 \uC694\uCCAD \uAC00\uB2A5"));
|
|
5297
5494
|
} else {
|
|
5298
5495
|
console.log(chalk16.yellow(" \u2192 diff > 10%: Vision \uBE44\uAD50 \u2192 \uC2A4\uD0A4\uB9C8 \uC218\uC815 \uD544\uC694"));
|