@logicsync/pixelprobe 0.1.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -0
  3. package/dist/bin/pixelprobe.d.ts +3 -0
  4. package/dist/bin/pixelprobe.d.ts.map +1 -0
  5. package/dist/bin/pixelprobe.js +218 -0
  6. package/dist/bin/pixelprobe.js.map +1 -0
  7. package/dist/browser/BrowserManager.d.ts +20 -0
  8. package/dist/browser/BrowserManager.d.ts.map +1 -0
  9. package/dist/browser/BrowserManager.js +64 -0
  10. package/dist/browser/BrowserManager.js.map +1 -0
  11. package/dist/browser/ViewportManager.d.ts +15 -0
  12. package/dist/browser/ViewportManager.d.ts.map +1 -0
  13. package/dist/browser/ViewportManager.js +65 -0
  14. package/dist/browser/ViewportManager.js.map +1 -0
  15. package/dist/cli/formatter.d.ts +4 -0
  16. package/dist/cli/formatter.d.ts.map +1 -0
  17. package/dist/cli/formatter.js +107 -0
  18. package/dist/cli/formatter.js.map +1 -0
  19. package/dist/comparator/DiffAggregator.d.ts +24 -0
  20. package/dist/comparator/DiffAggregator.d.ts.map +1 -0
  21. package/dist/comparator/DiffAggregator.js +94 -0
  22. package/dist/comparator/DiffAggregator.js.map +1 -0
  23. package/dist/comparator/LayoutComparator.d.ts +8 -0
  24. package/dist/comparator/LayoutComparator.d.ts.map +1 -0
  25. package/dist/comparator/LayoutComparator.js +68 -0
  26. package/dist/comparator/LayoutComparator.js.map +1 -0
  27. package/dist/comparator/PixelComparator.d.ts +9 -0
  28. package/dist/comparator/PixelComparator.d.ts.map +1 -0
  29. package/dist/comparator/PixelComparator.js +72 -0
  30. package/dist/comparator/PixelComparator.js.map +1 -0
  31. package/dist/comparator/StyleComparator.d.ts +8 -0
  32. package/dist/comparator/StyleComparator.d.ts.map +1 -0
  33. package/dist/comparator/StyleComparator.js +119 -0
  34. package/dist/comparator/StyleComparator.js.map +1 -0
  35. package/dist/core.d.ts +23 -0
  36. package/dist/core.d.ts.map +1 -0
  37. package/dist/core.js +211 -0
  38. package/dist/core.js.map +1 -0
  39. package/dist/extractor/ScreenshotCapturer.d.ts +20 -0
  40. package/dist/extractor/ScreenshotCapturer.d.ts.map +1 -0
  41. package/dist/extractor/ScreenshotCapturer.js +56 -0
  42. package/dist/extractor/ScreenshotCapturer.js.map +1 -0
  43. package/dist/extractor/SectionExtractor.d.ts +26 -0
  44. package/dist/extractor/SectionExtractor.d.ts.map +1 -0
  45. package/dist/extractor/SectionExtractor.js +92 -0
  46. package/dist/extractor/SectionExtractor.js.map +1 -0
  47. package/dist/extractor/StyleExtractor.d.ts +23 -0
  48. package/dist/extractor/StyleExtractor.d.ts.map +1 -0
  49. package/dist/extractor/StyleExtractor.js +88 -0
  50. package/dist/extractor/StyleExtractor.js.map +1 -0
  51. package/dist/index.d.ts +8 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +9 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/mcp/server.d.ts +3 -0
  56. package/dist/mcp/server.d.ts.map +1 -0
  57. package/dist/mcp/server.js +203 -0
  58. package/dist/mcp/server.js.map +1 -0
  59. package/dist/reporter/HTMLReporter.d.ts +8 -0
  60. package/dist/reporter/HTMLReporter.d.ts.map +1 -0
  61. package/dist/reporter/HTMLReporter.js +219 -0
  62. package/dist/reporter/HTMLReporter.js.map +1 -0
  63. package/dist/reporter/JSONReporter.d.ts +17 -0
  64. package/dist/reporter/JSONReporter.d.ts.map +1 -0
  65. package/dist/reporter/JSONReporter.js +77 -0
  66. package/dist/reporter/JSONReporter.js.map +1 -0
  67. package/dist/types/comparison.d.ts +133 -0
  68. package/dist/types/comparison.d.ts.map +1 -0
  69. package/dist/types/comparison.js +3 -0
  70. package/dist/types/comparison.js.map +1 -0
  71. package/dist/types/config.d.ts +138 -0
  72. package/dist/types/config.d.ts.map +1 -0
  73. package/dist/types/config.js +151 -0
  74. package/dist/types/config.js.map +1 -0
  75. package/package.json +72 -0
@@ -0,0 +1,64 @@
1
+ import { chromium } from "playwright";
2
+ export class BrowserManager {
3
+ browser = null;
4
+ options;
5
+ constructor(options = {}) {
6
+ this.options = {
7
+ headless: options.headless ?? true,
8
+ timeout: options.timeout ?? 30000,
9
+ };
10
+ }
11
+ async launch() {
12
+ if (this.browser)
13
+ return;
14
+ this.browser = await chromium.launch({
15
+ headless: this.options.headless,
16
+ });
17
+ }
18
+ async createPage(breakpoint) {
19
+ if (!this.browser) {
20
+ await this.launch();
21
+ }
22
+ const context = await this.browser.newContext({
23
+ viewport: {
24
+ width: breakpoint.width,
25
+ height: breakpoint.height,
26
+ },
27
+ deviceScaleFactor: 1,
28
+ });
29
+ context.setDefaultTimeout(this.options.timeout);
30
+ const page = await context.newPage();
31
+ return page;
32
+ }
33
+ async navigateAndWait(page, url, options = {}) {
34
+ // Use domcontentloaded first (fast), then wait for selector/timeout
35
+ // "networkidle" is unreliable on sites with analytics, chat widgets, etc.
36
+ await page.goto(url, {
37
+ waitUntil: "domcontentloaded",
38
+ timeout: this.options.timeout,
39
+ });
40
+ // Wait for the target selector to appear in the DOM
41
+ if (options.waitForSelector) {
42
+ await page.waitForSelector(options.waitForSelector, {
43
+ timeout: this.options.timeout,
44
+ });
45
+ }
46
+ // Let remaining assets (fonts, images, lazy-loaded CSS) settle.
47
+ // Default 1s even if no explicit wait was requested, to avoid
48
+ // capturing half-rendered pages.
49
+ const settleTime = options.waitForTimeout && options.waitForTimeout > 0
50
+ ? options.waitForTimeout
51
+ : 1000;
52
+ await page.waitForTimeout(settleTime);
53
+ }
54
+ async close() {
55
+ if (this.browser) {
56
+ await this.browser.close();
57
+ this.browser = null;
58
+ }
59
+ }
60
+ get isLaunched() {
61
+ return this.browser !== null;
62
+ }
63
+ }
64
+ //# sourceMappingURL=BrowserManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserManager.js","sourceRoot":"","sources":["../../src/browser/BrowserManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgD,MAAM,YAAY,CAAC;AAQpF,MAAM,OAAO,cAAc;IACjB,OAAO,GAAmB,IAAI,CAAC;IAC/B,OAAO,CAAkC;IAEjD,YAAY,UAAiC,EAAE;QAC7C,IAAI,CAAC,OAAO,GAAG;YACb,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI;YAClC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK;SAClC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;YACnC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;SAChC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAAsB;QACrC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC;QAED,MAAM,OAAO,GAAmB,MAAM,IAAI,CAAC,OAAQ,CAAC,UAAU,CAAC;YAC7D,QAAQ,EAAE;gBACR,KAAK,EAAE,UAAU,CAAC,KAAK;gBACvB,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B;YACD,iBAAiB,EAAE,CAAC;SACrB,CAAC,CAAC;QAEH,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEhD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,IAAU,EACV,GAAW,EACX,UAGI,EAAE;QAEN,oEAAoE;QACpE,0EAA0E;QAC1E,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;YACnB,SAAS,EAAE,kBAAkB;YAC7B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;SAC9B,CAAC,CAAC;QAEH,oDAAoD;QACpD,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,eAAe,EAAE;gBAClD,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;aAC9B,CAAC,CAAC;QACL,CAAC;QAED,gEAAgE;QAChE,8DAA8D;QAC9D,iCAAiC;QACjC,MAAM,UAAU,GAAG,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,cAAc,GAAG,CAAC;YACrE,CAAC,CAAC,OAAO,CAAC,cAAc;YACxB,CAAC,CAAC,IAAI,CAAC;QACT,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import { type Breakpoint } from "../types/config.js";
2
+ export declare class ViewportManager {
3
+ /**
4
+ * Resolve breakpoint names (preset or custom) to Breakpoint objects.
5
+ */
6
+ static resolve(breakpointNames?: string[], customBreakpoints?: Record<string, {
7
+ width: number;
8
+ height: number;
9
+ }>): Breakpoint[];
10
+ /**
11
+ * List all available preset breakpoints.
12
+ */
13
+ static listPresets(): Breakpoint[];
14
+ }
15
+ //# sourceMappingURL=ViewportManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewportManager.d.ts","sourceRoot":"","sources":["../../src/browser/ViewportManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EAEhB,MAAM,oBAAoB,CAAC;AAS5B,qBAAa,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,OAAO,CACZ,eAAe,CAAC,EAAE,MAAM,EAAE,EAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,GACpE,UAAU,EAAE;IAoDf;;OAEG;IACH,MAAM,CAAC,WAAW,IAAI,UAAU,EAAE;CAQnC"}
@@ -0,0 +1,65 @@
1
+ import { PRESET_BREAKPOINTS, } from "../types/config.js";
2
+ const DEFAULT_BREAKPOINT_NAMES = [
3
+ "mobile",
4
+ "tablet",
5
+ "desktop",
6
+ "desktop-lg",
7
+ ];
8
+ export class ViewportManager {
9
+ /**
10
+ * Resolve breakpoint names (preset or custom) to Breakpoint objects.
11
+ */
12
+ static resolve(breakpointNames, customBreakpoints) {
13
+ const names = breakpointNames ?? DEFAULT_BREAKPOINT_NAMES;
14
+ const resolved = [];
15
+ for (const name of names) {
16
+ // Check custom breakpoints first
17
+ if (customBreakpoints && name in customBreakpoints) {
18
+ const custom = customBreakpoints[name];
19
+ resolved.push({
20
+ name,
21
+ width: custom.width,
22
+ height: custom.height,
23
+ label: name,
24
+ });
25
+ continue;
26
+ }
27
+ // Check preset breakpoints
28
+ if (name in PRESET_BREAKPOINTS) {
29
+ const preset = PRESET_BREAKPOINTS[name];
30
+ resolved.push({
31
+ name,
32
+ width: preset.width,
33
+ height: preset.height,
34
+ label: preset.label,
35
+ });
36
+ continue;
37
+ }
38
+ // Try parsing "WIDTHxHEIGHT" format (e.g. "1440x900")
39
+ const match = name.match(/^(\d+)x(\d+)$/);
40
+ if (match) {
41
+ resolved.push({
42
+ name,
43
+ width: parseInt(match[1], 10),
44
+ height: parseInt(match[2], 10),
45
+ label: `${match[1]}×${match[2]}`,
46
+ });
47
+ continue;
48
+ }
49
+ throw new Error(`Unknown breakpoint "${name}". Available presets: ${Object.keys(PRESET_BREAKPOINTS).join(", ")}. Or use WIDTHxHEIGHT format (e.g. 1440x900).`);
50
+ }
51
+ return resolved;
52
+ }
53
+ /**
54
+ * List all available preset breakpoints.
55
+ */
56
+ static listPresets() {
57
+ return Object.entries(PRESET_BREAKPOINTS).map(([name, bp]) => ({
58
+ name,
59
+ width: bp.width,
60
+ height: bp.height,
61
+ label: bp.label,
62
+ }));
63
+ }
64
+ }
65
+ //# sourceMappingURL=ViewportManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewportManager.js","sourceRoot":"","sources":["../../src/browser/ViewportManager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAGnB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,wBAAwB,GAA2B;IACvD,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,YAAY;CACb,CAAC;AAEF,MAAM,OAAO,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,OAAO,CACZ,eAA0B,EAC1B,iBAAqE;QAErE,MAAM,KAAK,GAAG,eAAe,IAAI,wBAAwB,CAAC;QAC1D,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,iCAAiC;YACjC,IAAI,iBAAiB,IAAI,IAAI,IAAI,iBAAiB,EAAE,CAAC;gBACnD,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBACvC,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,KAAK,EAAE,IAAI;iBACZ,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,2BAA2B;YAC3B,IAAI,IAAI,IAAI,kBAAkB,EAAE,CAAC;gBAC/B,MAAM,MAAM,GACV,kBAAkB,CAAC,IAA4B,CAAC,CAAC;gBACnD,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,KAAK,EAAE,MAAM,CAAC,KAAK;iBACpB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,sDAAsD;YACtD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YAC1C,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;oBAC7B,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;oBAC9B,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE;iBACjC,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,IAAI,KAAK,CACb,uBAAuB,IAAI,yBAAyB,MAAM,CAAC,IAAI,CAC7D,kBAAkB,CACnB,CAAC,IAAI,CAAC,IAAI,CAAC,+CAA+C,CAC5D,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,WAAW;QAChB,OAAO,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7D,IAAI;YACJ,KAAK,EAAE,EAAE,CAAC,KAAK;YACf,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,KAAK,EAAE,EAAE,CAAC,KAAK;SAChB,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ import type { ComparisonResult } from "../types/comparison.js";
2
+ export declare function formatSeverity(severity: string): string;
3
+ export declare function formatResult(result: ComparisonResult): string;
4
+ //# sourceMappingURL=formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../src/cli/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAoB,MAAM,wBAAwB,CAAC;AAmBjF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAavD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAgH7D"}
@@ -0,0 +1,107 @@
1
+ // ANSI color codes (no dependency needed for basic colors)
2
+ const c = {
3
+ reset: "\x1b[0m",
4
+ bold: "\x1b[1m",
5
+ dim: "\x1b[2m",
6
+ red: "\x1b[31m",
7
+ green: "\x1b[32m",
8
+ yellow: "\x1b[33m",
9
+ blue: "\x1b[34m",
10
+ cyan: "\x1b[36m",
11
+ white: "\x1b[37m",
12
+ bgRed: "\x1b[41m",
13
+ bgGreen: "\x1b[42m",
14
+ bgYellow: "\x1b[43m",
15
+ bgBlue: "\x1b[44m",
16
+ };
17
+ export function formatSeverity(severity) {
18
+ switch (severity) {
19
+ case "critical":
20
+ return `${c.bgRed}${c.white} CRITICAL ${c.reset}`;
21
+ case "warning":
22
+ return `${c.bgYellow}${c.white} WARNING ${c.reset}`;
23
+ case "info":
24
+ return `${c.bgBlue}${c.white} INFO ${c.reset}`;
25
+ case "pass":
26
+ return `${c.bgGreen}${c.white} PASS ${c.reset}`;
27
+ default:
28
+ return severity;
29
+ }
30
+ }
31
+ export function formatResult(result) {
32
+ const lines = [];
33
+ lines.push("");
34
+ lines.push(`${c.bold}═══ Selection Comparison Report ═══${c.reset}`);
35
+ lines.push("");
36
+ const tgtSelector = result.config.targetSelector ?? result.config.selector;
37
+ const selectorsMatch = tgtSelector === result.config.selector;
38
+ lines.push(` ${c.dim}Source:${c.reset} ${result.config.sourceUrl} ${c.cyan}${result.config.selector}${c.reset}`);
39
+ lines.push(` ${c.dim}Target:${c.reset} ${result.config.targetUrl} ${c.cyan}${tgtSelector}${c.reset}${!selectorsMatch ? ` ${c.dim}(different selector)${c.reset}` : ""}`);
40
+ lines.push(` ${c.dim}Duration:${c.reset} ${(result.duration / 1000).toFixed(1)}s`);
41
+ lines.push("");
42
+ // Summary
43
+ lines.push(` ${c.bold}Overall:${c.reset} ${formatSeverity(result.summary.overallSeverity)} ` +
44
+ `${c.dim}Breakpoints with diffs:${c.reset} ${result.summary.breakpointsWithDifferences}/${result.summary.totalBreakpoints} ` +
45
+ `${c.dim}Avg pixel diff:${c.reset} ${result.summary.averagePixelDiff}%`);
46
+ if (result.summary.worstBreakpoint) {
47
+ lines.push(` ${c.dim}Worst breakpoint:${c.reset} ${c.red}${result.summary.worstBreakpoint}${c.reset}`);
48
+ }
49
+ lines.push("");
50
+ // Per-breakpoint
51
+ for (const [name, bp] of Object.entries(result.breakpoints)) {
52
+ lines.push(`${c.bold}── ${name} (${bp.breakpoint.width}×${bp.breakpoint.height}) ──${c.reset} ${c.dim}${bp.duration}ms${c.reset}`);
53
+ if (bp.error) {
54
+ lines.push(` ${c.red}Error: ${bp.error}${c.reset}`);
55
+ lines.push("");
56
+ continue;
57
+ }
58
+ // Visual
59
+ if (bp.visual) {
60
+ const pct = bp.visual.diffPercentage;
61
+ const color = pct > 5 ? c.red : pct > 1 ? c.yellow : pct > 0 ? c.blue : c.green;
62
+ lines.push(` ${c.dim}Visual:${c.reset} ${color}${pct}% different${c.reset} (${bp.visual.diffPixelCount.toLocaleString()} pixels)` +
63
+ (bp.visual.sizeMismatch ? ` ${c.yellow}⚠ size mismatch${c.reset}` : ""));
64
+ }
65
+ // Styles
66
+ if (bp.styles) {
67
+ const s = bp.styles.summary;
68
+ if (s.total === 0) {
69
+ lines.push(` ${c.dim}Styles:${c.reset} ${c.green}No differences${c.reset}`);
70
+ }
71
+ else {
72
+ lines.push(` ${c.dim}Styles:${c.reset} ${s.total} differences ` +
73
+ `(${c.red}${s.critical} critical${c.reset}, ${c.yellow}${s.warning} warning${c.reset}, ${c.blue}${s.info} info${c.reset})`);
74
+ // Show critical and warning diffs
75
+ for (const diff of bp.styles.differences.filter((d) => d.severity !== "info")) {
76
+ const sev = diff.severity === "critical"
77
+ ? `${c.red}●${c.reset}`
78
+ : `${c.yellow}●${c.reset}`;
79
+ lines.push(` ${sev} ${c.bold}${diff.property}${c.reset} ${c.dim}(${diff.category})${c.reset}`);
80
+ lines.push(` ${c.red}- ${diff.sourceValue}${c.reset}`);
81
+ lines.push(` ${c.green}+ ${diff.targetValue}${c.reset}`);
82
+ }
83
+ }
84
+ }
85
+ // Layout
86
+ if (bp.layout) {
87
+ const l = bp.layout.summary;
88
+ if (l.total === 0) {
89
+ lines.push(` ${c.dim}Layout:${c.reset} ${c.green}No differences${c.reset}`);
90
+ }
91
+ else {
92
+ lines.push(` ${c.dim}Layout:${c.reset} ${l.total} differences ` +
93
+ `(${c.red}${l.critical} critical${c.reset}, ${c.yellow}${l.warning} warning${c.reset}, ${c.blue}${l.info} info${c.reset})`);
94
+ for (const diff of bp.layout.differences.filter((d) => d.severity !== "info")) {
95
+ const sev = diff.severity === "critical"
96
+ ? `${c.red}●${c.reset}`
97
+ : `${c.yellow}●${c.reset}`;
98
+ const sign = diff.absoluteDiff > 0 ? "+" : "";
99
+ lines.push(` ${sev} ${c.bold}${diff.property}${c.reset}: ${diff.sourceValue}px → ${diff.targetValue}px (${sign}${diff.absoluteDiff}px / ${sign}${diff.percentageDiff}%)`);
100
+ }
101
+ }
102
+ }
103
+ lines.push("");
104
+ }
105
+ return lines.join("\n");
106
+ }
107
+ //# sourceMappingURL=formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../../src/cli/formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { Page } from "playwright";
2
+ import type { Breakpoint, StyleCategory } from "../types/config.js";
3
+ import type { BreakpointResult, ExtractedSection } from "../types/comparison.js";
4
+ export interface AggregatorOptions {
5
+ selector: string;
6
+ targetSelector?: string;
7
+ elementIndex: number;
8
+ styleCategories?: StyleCategory[];
9
+ pixelThreshold?: number;
10
+ includeVisual?: boolean;
11
+ includeStyles?: boolean;
12
+ includeLayout?: boolean;
13
+ }
14
+ export declare class DiffAggregator {
15
+ /**
16
+ * Extract section data from a page.
17
+ */
18
+ static extractSection(page: Page, selector: string, elementIndex: number, styleCategories?: StyleCategory[]): Promise<ExtractedSection>;
19
+ /**
20
+ * Compare extracted sections from source and target for a single breakpoint.
21
+ */
22
+ static compareBreakpoint(sourcePage: Page, targetPage: Page, breakpoint: Breakpoint, options: AggregatorOptions): Promise<BreakpointResult>;
23
+ }
24
+ //# sourceMappingURL=DiffAggregator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DiffAggregator.d.ts","sourceRoot":"","sources":["../../src/comparator/DiffAggregator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAEjB,MAAM,wBAAwB,CAAC;AAQhC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,aAAa,EAAE,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,qBAAa,cAAc;IACzB;;OAEG;WACU,cAAc,CACzB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,eAAe,CAAC,EAAE,aAAa,EAAE,GAChC,OAAO,CAAC,gBAAgB,CAAC;IA2B5B;;OAEG;WACU,iBAAiB,CAC5B,UAAU,EAAE,IAAI,EAChB,UAAU,EAAE,IAAI,EAChB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC;CAuF7B"}
@@ -0,0 +1,94 @@
1
+ import { SectionExtractor } from "../extractor/SectionExtractor.js";
2
+ import { StyleExtractor } from "../extractor/StyleExtractor.js";
3
+ import { ScreenshotCapturer } from "../extractor/ScreenshotCapturer.js";
4
+ import { StyleComparator } from "./StyleComparator.js";
5
+ import { PixelComparator } from "./PixelComparator.js";
6
+ import { LayoutComparator } from "./LayoutComparator.js";
7
+ export class DiffAggregator {
8
+ /**
9
+ * Extract section data from a page.
10
+ */
11
+ static async extractSection(page, selector, elementIndex, styleCategories) {
12
+ const { info, locator } = await SectionExtractor.getElement(page, selector, elementIndex);
13
+ if (!info.exists) {
14
+ throw new Error(`Element not found: "${selector}" at index ${elementIndex}`);
15
+ }
16
+ const [computedStyles, layoutMetrics, screenshot] = await Promise.all([
17
+ StyleExtractor.extractStyles(locator, styleCategories),
18
+ StyleExtractor.extractLayoutMetrics(locator),
19
+ ScreenshotCapturer.captureElement(locator),
20
+ ]);
21
+ return {
22
+ element: info,
23
+ computedStyles,
24
+ layoutMetrics,
25
+ screenshot,
26
+ };
27
+ }
28
+ /**
29
+ * Compare extracted sections from source and target for a single breakpoint.
30
+ */
31
+ static async compareBreakpoint(sourcePage, targetPage, breakpoint, options) {
32
+ const start = Date.now();
33
+ try {
34
+ // Extract both sections in parallel (may use different selectors)
35
+ const tgtSelector = options.targetSelector ?? options.selector;
36
+ const [source, target] = await Promise.all([
37
+ DiffAggregator.extractSection(sourcePage, options.selector, options.elementIndex, options.styleCategories),
38
+ DiffAggregator.extractSection(targetPage, tgtSelector, options.elementIndex, options.styleCategories),
39
+ ]);
40
+ const result = {
41
+ breakpoint: {
42
+ name: breakpoint.name,
43
+ width: breakpoint.width,
44
+ height: breakpoint.height,
45
+ },
46
+ sourceElement: source.element,
47
+ targetElement: target.element,
48
+ duration: 0,
49
+ };
50
+ // Style comparison
51
+ if (options.includeStyles !== false) {
52
+ result.styles = StyleComparator.compare(source.computedStyles, target.computedStyles);
53
+ }
54
+ // Pixel comparison
55
+ if (options.includeVisual !== false) {
56
+ result.visual = PixelComparator.compare(source.screenshot, target.screenshot, options.pixelThreshold ?? 0.1);
57
+ }
58
+ // Layout comparison
59
+ if (options.includeLayout !== false) {
60
+ result.layout = LayoutComparator.compare(source.layoutMetrics, target.layoutMetrics);
61
+ }
62
+ result.duration = Date.now() - start;
63
+ return result;
64
+ }
65
+ catch (err) {
66
+ return {
67
+ breakpoint: {
68
+ name: breakpoint.name,
69
+ width: breakpoint.width,
70
+ height: breakpoint.height,
71
+ },
72
+ sourceElement: {
73
+ selector: options.selector,
74
+ index: options.elementIndex,
75
+ tagName: "unknown",
76
+ textPreview: "",
77
+ boundingBox: { x: 0, y: 0, width: 0, height: 0 },
78
+ exists: false,
79
+ },
80
+ targetElement: {
81
+ selector: options.selector,
82
+ index: options.elementIndex,
83
+ tagName: "unknown",
84
+ textPreview: "",
85
+ boundingBox: { x: 0, y: 0, width: 0, height: 0 },
86
+ exists: false,
87
+ },
88
+ error: err.message,
89
+ duration: Date.now() - start,
90
+ };
91
+ }
92
+ }
93
+ }
94
+ //# sourceMappingURL=DiffAggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DiffAggregator.js","sourceRoot":"","sources":["../../src/comparator/DiffAggregator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { LayoutMetrics, LayoutComparisonResult } from "../types/comparison.js";
2
+ export declare class LayoutComparator {
3
+ /**
4
+ * Compare layout metrics between source and target elements.
5
+ */
6
+ static compare(source: LayoutMetrics, target: LayoutMetrics): LayoutComparisonResult;
7
+ }
8
+ //# sourceMappingURL=LayoutComparator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutComparator.d.ts","sourceRoot":"","sources":["../../src/comparator/LayoutComparator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,sBAAsB,EAEvB,MAAM,wBAAwB,CAAC;AAMhC,qBAAa,gBAAgB;IAC3B;;OAEG;IACH,MAAM,CAAC,OAAO,CACZ,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,aAAa,GACpB,sBAAsB;CAsC1B"}
@@ -0,0 +1,68 @@
1
+ // Threshold in pixels for severity classification
2
+ const CRITICAL_THRESHOLD_PX = 10;
3
+ const WARNING_THRESHOLD_PX = 3;
4
+ export class LayoutComparator {
5
+ /**
6
+ * Compare layout metrics between source and target elements.
7
+ */
8
+ static compare(source, target) {
9
+ const differences = [];
10
+ // Compare bounding box dimensions
11
+ comparePair(differences, "width", source.boundingBox.width, target.boundingBox.width);
12
+ comparePair(differences, "height", source.boundingBox.height, target.boundingBox.height);
13
+ // Compare margin
14
+ compareSpacing(differences, "margin", source.margin, target.margin);
15
+ // Compare padding
16
+ compareSpacing(differences, "padding", source.padding, target.padding);
17
+ // Compare border widths
18
+ compareSpacing(differences, "border", source.border, target.border);
19
+ // Compare scroll dimensions
20
+ comparePair(differences, "scrollWidth", source.scrollWidth, target.scrollWidth);
21
+ comparePair(differences, "scrollHeight", source.scrollHeight, target.scrollHeight);
22
+ // Sort by severity
23
+ const severityOrder = { critical: 0, warning: 1, info: 2 };
24
+ differences.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity] ||
25
+ Math.abs(b.absoluteDiff) - Math.abs(a.absoluteDiff));
26
+ return {
27
+ differences,
28
+ summary: {
29
+ total: differences.length,
30
+ critical: differences.filter((d) => d.severity === "critical").length,
31
+ warning: differences.filter((d) => d.severity === "warning").length,
32
+ info: differences.filter((d) => d.severity === "info").length,
33
+ },
34
+ };
35
+ }
36
+ }
37
+ function comparePair(diffs, property, sourceVal, targetVal) {
38
+ const absoluteDiff = Math.round((targetVal - sourceVal) * 100) / 100;
39
+ if (absoluteDiff === 0)
40
+ return;
41
+ const percentageDiff = sourceVal !== 0
42
+ ? Math.round((absoluteDiff / sourceVal) * 10000) / 100
43
+ : targetVal !== 0
44
+ ? 100
45
+ : 0;
46
+ diffs.push({
47
+ property,
48
+ sourceValue: sourceVal,
49
+ targetValue: targetVal,
50
+ absoluteDiff,
51
+ percentageDiff,
52
+ severity: classifyLayoutSeverity(Math.abs(absoluteDiff)),
53
+ });
54
+ }
55
+ function compareSpacing(diffs, prefix, source, target) {
56
+ comparePair(diffs, `${prefix}-top`, source.top, target.top);
57
+ comparePair(diffs, `${prefix}-right`, source.right, target.right);
58
+ comparePair(diffs, `${prefix}-bottom`, source.bottom, target.bottom);
59
+ comparePair(diffs, `${prefix}-left`, source.left, target.left);
60
+ }
61
+ function classifyLayoutSeverity(absDiff) {
62
+ if (absDiff >= CRITICAL_THRESHOLD_PX)
63
+ return "critical";
64
+ if (absDiff >= WARNING_THRESHOLD_PX)
65
+ return "warning";
66
+ return "info";
67
+ }
68
+ //# sourceMappingURL=LayoutComparator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutComparator.js","sourceRoot":"","sources":["../../src/comparator/LayoutComparator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { PixelComparisonResult } from "../types/comparison.js";
2
+ export declare class PixelComparator {
3
+ /**
4
+ * Compare two screenshots pixel-by-pixel.
5
+ * Handles size mismatches by padding the smaller image.
6
+ */
7
+ static compare(sourceBuffer: Buffer, targetBuffer: Buffer, threshold?: number): PixelComparisonResult;
8
+ }
9
+ //# sourceMappingURL=PixelComparator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PixelComparator.d.ts","sourceRoot":"","sources":["../../src/comparator/PixelComparator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,qBAAa,eAAe;IAC1B;;;OAGG;IACH,MAAM,CAAC,OAAO,CACZ,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,SAAS,GAAE,MAAY,GACtB,qBAAqB;CAoDzB"}
@@ -0,0 +1,72 @@
1
+ import pixelmatch from "pixelmatch";
2
+ import { PNG } from "pngjs";
3
+ export class PixelComparator {
4
+ /**
5
+ * Compare two screenshots pixel-by-pixel.
6
+ * Handles size mismatches by padding the smaller image.
7
+ */
8
+ static compare(sourceBuffer, targetBuffer, threshold = 0.1) {
9
+ const sourcePng = PNG.sync.read(sourceBuffer);
10
+ const targetPng = PNG.sync.read(targetBuffer);
11
+ const sizeMismatch = sourcePng.width !== targetPng.width ||
12
+ sourcePng.height !== targetPng.height;
13
+ // Use the larger dimensions for comparison canvas
14
+ const width = Math.max(sourcePng.width, targetPng.width);
15
+ const height = Math.max(sourcePng.height, targetPng.height);
16
+ // Pad images to same size if needed
17
+ const sourceData = sizeMismatch
18
+ ? padImage(sourcePng, width, height)
19
+ : sourcePng.data;
20
+ const targetData = sizeMismatch
21
+ ? padImage(targetPng, width, height)
22
+ : targetPng.data;
23
+ // Create diff image buffer
24
+ const diffPng = new PNG({ width, height });
25
+ const diffPixelCount = pixelmatch(sourceData, targetData, diffPng.data, width, height, {
26
+ threshold,
27
+ includeAA: false, // ignore anti-aliasing differences
28
+ alpha: 0.3,
29
+ diffColor: [255, 0, 0], // red for differences
30
+ diffColorAlt: [0, 255, 0], // green for anti-aliased diffs
31
+ });
32
+ const totalPixels = width * height;
33
+ return {
34
+ diffPixelCount,
35
+ totalPixels,
36
+ diffPercentage: Math.round((diffPixelCount / totalPixels) * 10000) / 100,
37
+ diffImage: PNG.sync.write(diffPng),
38
+ sourceWidth: sourcePng.width,
39
+ sourceHeight: sourcePng.height,
40
+ targetWidth: targetPng.width,
41
+ targetHeight: targetPng.height,
42
+ sizeMismatch,
43
+ };
44
+ }
45
+ }
46
+ /**
47
+ * Pad an image to a target size, filling extra space with transparent magenta
48
+ * so size differences are visually obvious in the diff.
49
+ */
50
+ function padImage(png, targetWidth, targetHeight) {
51
+ const data = new Uint8Array(targetWidth * targetHeight * 4);
52
+ // Fill with magenta (indicates missing area)
53
+ for (let i = 0; i < data.length; i += 4) {
54
+ data[i] = 255; // R
55
+ data[i + 1] = 0; // G
56
+ data[i + 2] = 255; // B
57
+ data[i + 3] = 255; // A
58
+ }
59
+ // Copy original image data
60
+ for (let y = 0; y < png.height; y++) {
61
+ for (let x = 0; x < png.width; x++) {
62
+ const srcIdx = (y * png.width + x) * 4;
63
+ const dstIdx = (y * targetWidth + x) * 4;
64
+ data[dstIdx] = png.data[srcIdx];
65
+ data[dstIdx + 1] = png.data[srcIdx + 1];
66
+ data[dstIdx + 2] = png.data[srcIdx + 2];
67
+ data[dstIdx + 3] = png.data[srcIdx + 3];
68
+ }
69
+ }
70
+ return data;
71
+ }
72
+ //# sourceMappingURL=PixelComparator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PixelComparator.js","sourceRoot":"","sources":["../../src/comparator/PixelComparator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { StyleComparisonResult } from "../types/comparison.js";
2
+ export declare class StyleComparator {
3
+ /**
4
+ * Compare computed styles from source and target elements.
5
+ */
6
+ static compare(sourceStyles: Record<string, string>, targetStyles: Record<string, string>): StyleComparisonResult;
7
+ }
8
+ //# sourceMappingURL=StyleComparator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StyleComparator.d.ts","sourceRoot":"","sources":["../../src/comparator/StyleComparator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,qBAAqB,EAEtB,MAAM,wBAAwB,CAAC;AA8ChC,qBAAa,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,OAAO,CACZ,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,qBAAqB;CAwDzB"}